diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..575f578 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,23 @@ +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the OS, Python version, and other tools you might need +build: + os: ubuntu-24.04 + tools: + python: "3.13" + +# Build documentation in the "docs/" directory with Sphinx +sphinx: + configuration: docs/conf.py + +# Optionally, but recommended, +# declare the Python requirements required to build your documentation +# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +# python: +# install: +# - requirements: docs/requirements.txt + diff --git a/concordantmodes/cma.py b/concordantmodes/cma.py index c12d39c..e8ea74c 100644 --- a/concordantmodes/cma.py +++ b/concordantmodes/cma.py @@ -34,7 +34,7 @@ class ConcordantModes: """ - Driver class for the Concordant Modes Algorithm (CMA). + Driver class for the Concordant Mode Approach (CMA). This class orchestrates the complete vibrational analysis workflow, including: @@ -107,12 +107,13 @@ def __init__(self, options, proj=np.array([]), extra_indices=[]): self.options = options # These constants are from: # https://physics.nist.gov/cgi-bin/cuu/Value?hr + # https://physics.nist.gov/cgi-bin/cuu/Value?bohrrada0|search_for=bohr # If this link dies, find the new link on NIST # for the Hartree to Joule conversion and pop it in there. - # There is a standard uncertainty of 0.0000000000085 to the MDYNE_HART constant. - # BOHR_ANG: Standard uncertainty of 0.00000000080 - self.MDYNE_HART = 4.3597447222071 - self.BOHR_ANG = 0.529177210903 + # There is a standard uncertainty of 0.0000000000048 to the MDYNE_HART constant. + # BOHR_ANG: Standard uncertainty of 0.00000000082 + self.MDYNE_HART = 4.3597447222060 + self.BOHR_ANG = 0.529177210544 self.proj = proj self.extra_indices = extra_indices @@ -170,16 +171,18 @@ def run(self, sym_sort=[]): g_mat = GMatrix(self.zmat_obj, self.s_vec, self.options, proj=self.proj) g_mat.run() G = g_mat.G.copy() + print(G.shape) + print(self.F_b.shape) self.options.init_bool = False if len(self.sym_sort) > 1: - F, g_mat.G = self.symm_obj.GF_sym_sort(self.F_b, g_mat, self.sym_sort) + self.F_b, G = self.symm_obj.GF_sym_sort(self.F_b, G, self.sym_sort) # Run the GF matrix method with the internal F-Matrix and computed G-Matrix! print("Level B Frequencies:") b_GF = GFMethod( - g_mat.G.copy(), + G.copy(), self.F_b.copy(), self.zmat_obj, self.TED_obj, @@ -205,10 +208,27 @@ def run(self, sym_sort=[]): ted_b = ted_b.T #### end of block that could probably be moved inside the symmetry.py module? - self.F_b = np.dot(np.dot(b_GF.L.T, self.F_b), b_GF.L) - # Now for the TED check. + self.disp = TransfDisp( + None, + self.zmat_obj, + None, + None, + None, + self.options, + None, + ) + eig_inv = self.disp._build_eig_inv(b_GF.L) + + # Use inv of normalized eig_inv instead of b_GF.L? + # self.F_b = np.dot(np.dot(b_GF.L.T, self.F_b), b_GF.L) + self.F_b = np.dot(np.dot(inv(eig_inv).T, self.F_b), inv(eig_inv)) + # Now for the TED check. The statement above could be problematic + # for the TED check. Might want to fold it into an "else" + # statement to the TED check. Also, might want to use + # normalized eig_inv for TED check, it's just a scale factor + # to the diagonal G-matrix anyways... if self.options.ted_check: - self.G = np.dot(np.dot(LA.inv(b_GF.L), g_mat.G), LA.inv(b_GF.L).T) + self.G = np.dot(np.dot(LA.inv(b_GF.L), G), LA.inv(b_GF.L).T) self.G[np.abs(self.G) < self.options.tol] = 0 self.F = np.dot(np.dot(b_GF.L.T, self.F_b), b_GF.L) self.F[np.abs(self.F) < self.options.tol] = 0 @@ -221,7 +241,6 @@ def run(self, sym_sort=[]): self.TED_obj, self.options, self.symm_obj.symtext, - cma=False, ) TED_GF.run() @@ -284,6 +303,7 @@ def run(self, sym_sort=[]): # Can we generalize compute_hessian to run this too? # Now switch state to cma_level = "A" cma_level = "A" + self.options.cart_fc_b = False fc = np.array([]) if self.options.reduced_disp: @@ -304,6 +324,7 @@ def run(self, sym_sort=[]): eigs=b_GF.L, fc=fc, ) + # print(self.F_a) # Recompute the G-matrix with the new geometry, and then transform # the G-matrix using the lower level of theory eigenvalue matrix. @@ -315,13 +336,19 @@ def run(self, sym_sort=[]): self.G = g_mat.G - self.G = np.dot(np.dot(self.disp.eig_inv, self.G), self.disp.eig_inv.T) + if len(self.sym_sort) > 1: + _, self.G = self.symm_obj.GF_sym_sort( + np.zeros(self.F_b.shape), self.G, self.sym_sort + ) + + self.G = np.dot(np.dot(eig_inv, self.G), eig_inv.T) self.G[np.abs(self.G) < self.options.tol] = 0 - if self.options.benchmark_full: - cma = True - else: - cma = False + np.set_printoptions(precision=7, edgeitems=60, linewidth=10000) + print("Normal Mode G") + print(self.G) + print("Diag F") + print(self.F_a) # Final GF Matrix run print("Final Harmonic Frequencies:") @@ -332,7 +359,6 @@ def run(self, sym_sort=[]): self.TED_obj, self.options, self.symm_obj.symtext, - cma=cma, ) a_GF.run() @@ -355,12 +381,25 @@ def run(self, sym_sort=[]): + " (hartrees) " ) + print("Frequency Shift (cm^-1): ") + print(a_GF.freq - b_GF.freq) + for i in a_GF.freq - b_GF.freq: + print(i) + print("RMSD Freq Shift (cm^-1): ") + print(np.sqrt(np.mean((a_GF.freq - b_GF.freq) ** 2))) + print("MAX Freq Shift (cm^-1): ") + print(np.max(np.abs(a_GF.freq - b_GF.freq))) + + # Write a molden file + molden = MoldenWriter(self.zmat_obj, self.disp, a_GF.freq) + molden.run() + # This code converts the force constants back into cartesian - # coordinates and writes out an "output.default.hess" file, which - # is of the same format as FCMFINAL of CFOUR. + # coordinates and writes out "fc_a.dat" and "fc_a.grad" files, which + # are of the same format as FCMFINAL of CFOUR for the force constants. - self.F_a = np.dot(np.dot(self.disp.eig_inv.T, self.F_a), self.disp.eig_inv) - self.gradient = np.dot(self.grad_a, self.disp.eig_inv) + self.F_a = np.dot(np.dot(eig_inv.T, self.F_a), eig_inv) + self.gradient = np.dot(self.grad_a, eig_inv) cart_conv = FcConv( self.F_a, @@ -379,19 +418,6 @@ def run(self, sym_sort=[]): self.F_cart = cart_conv.F - print("Frequency Shift (cm^-1): ") - print(a_GF.freq - b_GF.freq) - for i in a_GF.freq - b_GF.freq: - print(i) - print("RMSD Freq Shift (cm^-1): ") - print(np.sqrt(np.mean((a_GF.freq - b_GF.freq) ** 2))) - print("MAX Freq Shift (cm^-1): ") - print(np.max(np.abs(a_GF.freq - b_GF.freq))) - - # Write a molden file - molden = MoldenWriter(self.zmat_obj, self.disp, a_GF.freq) - molden.run() - t2 = time.time() print("This program took " + str(t2 - t1) + " seconds to run.") @@ -421,7 +447,7 @@ def compute_hessian( self.s_vec.run( ref_carts, b_proj, - proj=proj, + proj=self.proj, second_order=options.second_order, ) @@ -433,44 +459,53 @@ def compute_hessian( self.TED_obj = TED(self.proj, zmat, options) - if os.path.exists(rootdir + "/fc_" + cma_level.lower() + ".grad"): - g_read_obj = GrRead("fc_" + cma_level.lower() + ".grad") - # Need to pass in general carts here. - g_read_obj.run(ref_carts) - num_deg_free = self.proj.shape[1] - if deriv_level and self.options.cart_fc_b: - num_deg_free = len(ref_carts.flatten()) options.init_bool = False cart_fc = False - if os.path.exists(rootdir + "/fc_" + cma_level.lower() + ".dat"): - f_read_obj = FcRead("fc_" + cma_level.lower() + ".dat") + if coord_type.lower() == "cartesian": + self.options.cart_fc_b = True + num_deg_free = len(ref_carts.flatten()) cart_fc = True + suff = "cart_" + elif coord_type.lower() == "internal": + suff = "int_" + if os.path.exists(rootdir + "/fc_" + suff + cma_level.lower() + ".dat"): + f_read_obj = FcRead("fc_" + suff + cma_level.lower() + ".dat") f_read_obj.run() - f_conv_obj = FcConv( - f_read_obj.fc_mat, - self.s_vec, - zmat, - "internal", - False, - self.TED_obj, - options, - ) - if self.options.second_order: - f_conv_obj.run(grad=g_read_obj.cart_grad) + grad = np.array([]) + if self.options.cart_fc_b: + f_conv_obj = FcConv( + f_read_obj.fc_mat, + self.s_vec, + zmat, + "internal", + False, + self.proj, + options, + ) + if self.options.second_order and os.path.exists( + rootdir + "/fc_" + suff + cma_level.lower() + ".grad" + ): + g_read_obj = GrRead("fc_" + suff + cma_level.lower() + ".grad") + # Need to pass in general carts here. + g_read_obj.run(ref_carts) + f_conv_obj.run(grad=g_read_obj.cart_grad) + grad = f_conv_obj.grad + else: + f_conv_obj.run() + F = f_conv_obj.F else: - f_conv_obj.run() - F = f_conv_obj.F + F = f_read_obj.fc_mat else: options.init_bool = True # First generate displacements in internal coordinates - if cma_level == "B": + if cma_level.upper() == "B": eigs = np.eye(len(self.proj.T)) if self.options.cart_fc_b: eigs = np.eye(len(ref_carts.flatten())) - cart_fc = True + # cart_fc = True algo = Algorithm( num_deg_free, @@ -480,23 +515,25 @@ def compute_hessian( ) algo.run() print("Pre sym indices:") + print(len(algo.indices)) print(algo.indices) - # raise RuntimeError if ( not options.deriv_level_b and not options.molsym_symmetry and cma_level == "B" ): - # Sym_sort doesn't seem to be working - print("symmetric displacements:") - if len(self.sym_sort) > 1: + if len(self.sym_sort) > 1 and coord_type.lower() == "internal": + print("symmetric displacements:") algo.indices = self.symm_obj.create_sym_sort_disps( self.sym_sort, algo.indices ) else: self.symm_obj.indices_by_irrep = algo.indices_by_irrep + print("Post sym indices:") + print(len(algo.indices)) print(algo.indices) + if cma_level == "A" and len(self.extra_indices): algo.indices += self.extra_indices self.disp = TransfDisp( @@ -545,6 +582,7 @@ def compute_hessian( raise RuntimeError else: if not os.path.exists(rootdir + "/Disps" + cma_level.upper()): + print(os.getcwd()) print( "You need to have a Disps" + cma_level.upper() @@ -560,7 +598,7 @@ def compute_hessian( reap_obj = Reap( options, - len(eigs), + num_deg_free, algo.indices, self.symm_obj, cma_level, @@ -595,29 +633,42 @@ def compute_hessian( ) fc.run() - if options.second_order and cart_fc: - f_conv_obj = FcConv( - fc.FC, - self.s_vec, - zmat, - "internal", - False, - self.proj, - options, - ) + f_conv_obj = FcConv( + fc.FC, + self.s_vec, + zmat, + "internal", + False, + self.proj, + options, + ) + f_conv_obj.N = len(fc.FC) + if options.second_order and self.options.cart_fc_b: + f_conv_obj.print_const(fc_name="fc_cart_" + cma_level.lower() + ".dat") + fc_name = "fc_cart_" + cma_level.lower() + ".grad" + fc_output = "" + g_print = fc.gradient.flatten() + for i in range(len(g_print) // 3): + fc_output += "{:20.10f}".format(g_print[3 * i]) + fc_output += "{:20.10f}".format(g_print[3 * i + 1]) + fc_output += "{:20.10f}".format(g_print[3 * i + 2]) + fc_output += "\n" + if len(g_print) % 3: + for i in range(len(g_print) % 3): + fc_output += "{:20.10f}".format( + g_print[3 * (len(g_print) // 3) + i] + ) + fc_output += "\n" + with open(fc_name, "w+") as file: + file.write(fc_output) + f_conv_obj.run(grad=fc.gradient) fc.FC = f_conv_obj.F + elif not self.options.cart_fc_b: + f_conv_obj.print_const(fc_name="fc_int_" + cma_level.lower() + ".dat") F = fc.FC + grad = fc.gradient F[np.abs(F) < self.options.tol] = 0 - del_tol = 1.0e-3 - for row in F: - abs_row = np.abs(row) - row[abs_row < np.max(abs_row) * del_tol] = 0 - F = F.T - for row in F: - abs_row = np.abs(row) - row[abs_row < np.max(abs_row) * del_tol] = 0 - F = F.T - return F, fc.gradient + return F, grad diff --git a/concordantmodes/directory_tree.py b/concordantmodes/directory_tree.py index 6927848..9b39e9d 100644 --- a/concordantmodes/directory_tree.py +++ b/concordantmodes/directory_tree.py @@ -192,6 +192,7 @@ def run(self): if self.prog_name not in self.PROG_LIST: raise RuntimeError(f"Unsupported program: {self.prog_name}") + print(root) data = self.template.read_text().splitlines(keepends=True) n_atoms = len(self.zmat.atom_list) diff --git a/concordantmodes/f_convert.py b/concordantmodes/f_convert.py index c8a8bf8..79b29a7 100644 --- a/concordantmodes/f_convert.py +++ b/concordantmodes/f_convert.py @@ -128,7 +128,7 @@ class FcConv: def __init__(self, fc_mat, s_vec, zmat, coord, print_f, proj, options): self.coord = coord - self.F = fc_mat + self.F = fc_mat.copy() self.print_f = print_f self.s_vec = s_vec self.proj = proj @@ -171,8 +171,10 @@ def run(self, grad=np.array([])): B = self.s_vec.B else: B = np.dot(self.proj.T, self.s_vec.B) + G = np.dot(B, B.T) self.A_T = np.dot(LA.inv(G), B) + if self.options.units == "MdyneAng": self.F /= self.BOHR_ANG self.F *= self.MDYNE_HART @@ -193,12 +195,12 @@ def run(self, grad=np.array([])): C2 = np.einsum("rpj,qj->rpq", C2, self.A_T) V2 = np.einsum("q,qpr->pr", self.v_q, C2) - grad = np.dot(grad, self.A_T.T) + self.grad = np.dot(grad, self.A_T.T) self.F -= V2 if self.print_f: - self.print_const(fc_name="fc_int.dat", grad=grad) + self.print_const(fc_name="fc_int.dat", grad=self.grad) elif self.coord.lower() == "cartesian": if not len(self.proj): @@ -225,7 +227,7 @@ def run(self, grad=np.array([])): if self.print_f: self.print_const(grad=grad) - def print_const(self, fc_name="fc_a.dat", grad=np.array([])): + def print_const(self, fc_name="fc_cart_a.dat", grad=np.array([])): """ Write transformed force constants and gradients to disk. @@ -280,5 +282,20 @@ def print_const(self, fc_name="fc_a.dat", grad=np.array([])): ) fc_output += "\n" # Introduce grad_name - with open("fc_a.grad", "w+") as file: + with open("fc_cart_a.grad", "w+") as file: file.write(gr_output) + + def _build_mass_matrix(self): + """ + Construct inverse mass-weight matrix. + """ + + masses = np.asarray(self.zmat.masses, dtype=float) + + inv_masses = np.where( + np.asarray(self.zmat.atom_list) == "X", + 0.0, + 1.0 / masses, + ) + + return np.diag(np.repeat(inv_masses, 3)) diff --git a/concordantmodes/g_matrix.py b/concordantmodes/g_matrix.py index d7fae49..fd59438 100644 --- a/concordantmodes/g_matrix.py +++ b/concordantmodes/g_matrix.py @@ -125,8 +125,6 @@ def run(self): """ B = self.s_vectors.B - # Compute and temper G - tol = 1e-12 u = np.array(self.zmat.masses) for i in range(len(u)): if self.zmat.atom_list[i] == "X": @@ -136,17 +134,8 @@ def run(self): u = np.repeat(u, 3) u = np.diag(u) self.G = B.dot(u.dot(B.T)) - self.G[np.abs(self.G) < tol] = 0 + self.G[np.abs(self.G) < self.options.tol] = 0 if len(self.proj) and self.options.coords != "ZMAT": self.G = np.dot(self.proj.T, np.dot(self.G, self.proj)) - del_tol = 1.0e-3 - for row in self.G: - abs_row = np.abs(row) - row[abs_row < np.max(abs_row) * del_tol] = 0 - self.G = self.G.T - for row in self.G: - abs_row = np.abs(row) - row[abs_row < np.max(abs_row) * del_tol] = 0 - self.G = self.G.T diff --git a/concordantmodes/gf_method.py b/concordantmodes/gf_method.py index 51d7eee..3e3550d 100644 --- a/concordantmodes/gf_method.py +++ b/concordantmodes/gf_method.py @@ -80,10 +80,6 @@ class GFMethod: analysis routines. Required when symmetry block diagonalization is enabled. - cma : bool, optional - Indicates whether the calculation is being performed within - the Concordant Modes Algorithm workflow. - sym_sort : list, optional User-defined symmetry sorting information. @@ -149,14 +145,13 @@ class GFMethod: internal coordinate to every normal mode. """ - def __init__(self, G, F, zmat, ted, options, symtext=None, cma=None, sym_sort=[]): + def __init__(self, G, F, zmat, ted, options, symtext=None, sym_sort=[]): self.G = G self.F = F self.zmat = zmat self.ted = ted self.options = options self.symtext = symtext - self.cma = cma self.sym_sort = sym_sort self.AMU_ELMASS = 5.48579909065 * (10 ** (-4)) self.HARTREE_WAVENUM = 219474.6313708 diff --git a/concordantmodes/options.py b/concordantmodes/options.py index b0e5412..8fa398f 100644 --- a/concordantmodes/options.py +++ b/concordantmodes/options.py @@ -22,7 +22,9 @@ def __init__(self, **kwargs): self.deriv_level_b = kwargs.pop("deriv_level_b", 0) self.deriv_level_c = kwargs.pop("deriv_level_c", 0) self.dir_reap = kwargs.pop("dir_reap", True) - self.disp = kwargs.pop("disp", 0.02) + self.disp_a = kwargs.pop("disp_a", 0.01) + self.disp_b = kwargs.pop("disp_b", 0.01) + self.disp_c = kwargs.pop("disp_c", 0.01) self.disp_check = kwargs.pop("disp_check", False) self.disp_tol = kwargs.pop("disp_tol", 0.0001) self.energy_regex_a = kwargs.pop("energy_regex_a", "") @@ -40,7 +42,6 @@ def __init__(self, **kwargs): self.init_bool = kwargs.pop("init_bool", False) self.man_proj = kwargs.pop("man_proj", False) self.memory = kwargs.pop("memory", "32GB") - # self.memory = kwargs.pop("memory", "32") self.mode_coupling_check = kwargs.pop("mode_coupling_check", False) self.molly_regex_b = kwargs.pop("molly_regex_b", "") self.nslots = kwargs.pop("nslots", 1) @@ -55,6 +56,7 @@ def __init__(self, **kwargs): self.program_c = kwargs.pop("program_c", "molpro") self.proj_tol = kwargs.pop("proj_tol", 1.0e-14) self.queue = kwargs.pop("queue", "batch") + self.red_tol = kwargs.pop("red_tol", 1.0e-4) self.reduced_disp = kwargs.pop("reduced_disp", False) self.reduced_disp_size = kwargs.pop("reduced_disp_size", 0.0025) self.second_order = kwargs.pop("second_order", False) @@ -68,7 +70,7 @@ def __init__(self, **kwargs): self.success_regex_c = kwargs.pop("success_regex_c", "") self.molsym_symmetry = kwargs.pop("molsym_symmetry", False) self.tight_disp = kwargs.pop("tight_disp", False) - self.time_limit = kwargs.pop("time_limit", "00:30:00") + self.time_limit = kwargs.pop("time_limit", "04:00:00") self.tol = kwargs.pop("tol", 1.0e-14) self.topo_analysis = kwargs.pop("topo_analysis", False) self.topo_max_it = kwargs.pop("topo_max_it", 20) diff --git a/concordantmodes/reap.py b/concordantmodes/reap.py index 059a6a8..23780f7 100644 --- a/concordantmodes/reap.py +++ b/concordantmodes/reap.py @@ -202,7 +202,7 @@ def run(self): by the :class:`ForceConstant` class to construct finite-difference gradients and Hessians. """ - if self.deriv_level: + if self.deriv_level == 1: return self._run_gradients() return self._run_energies() @@ -286,6 +286,8 @@ def _run_gradients(self): ref_grad = self._read_gradient(1) + print(self.indices) + for idx in self.indices: i = idx[0] @@ -372,12 +374,13 @@ def _maybe_convert_gradients(self): The transformed gradients replace the Cartesian gradients stored in ``p_grad_array`` and ``m_grad_array``. """ + if not ( self.options.conv_grad and self.zmat is not None and len(self.proj) and self.disp is not None - and not self.deriv_level + and self.deriv_level ): return diff --git a/concordantmodes/s_vectors.py b/concordantmodes/s_vectors.py index c8e3abb..c9a0a89 100644 --- a/concordantmodes/s_vectors.py +++ b/concordantmodes/s_vectors.py @@ -237,8 +237,8 @@ def run(self, carts, B_proj, proj=np.array([]), second_order=False): e_2 = self.compute_e(x2, x3) e_3 = self.compute_e(x3, x4) ax = self.calc_alpha_x(e_1, e_2, e_3) - phi_1 = self.compute_phi(-e_1, e_2) - phi_2 = self.compute_phi(-e_2, e_3) + phi_1 = self.compute_phi(e_1, -e_2) + phi_2 = self.compute_phi(e_2, -e_3) lx1 = self.compute_LINX1(e_1, e_2, e_3, r_1, phi_1, phi_2, ax) lx2 = self.compute_LINX2(e_1, e_2, e_3, r_1, r_2, phi_1, phi_2, ax) @@ -260,13 +260,14 @@ def run(self, carts, B_proj, proj=np.array([]), second_order=False): e_1 = self.compute_e(x1, x2) e_2 = self.compute_e(x2, x3) e_3 = self.compute_e(x3, x4) + ax = self.calc_alpha_x(e_1, e_2, e_3) ay = self.calc_alpha_y(e_1, e_2, e_3) - phi_1 = self.compute_phi(-e_1, e_2) - phi_2 = self.compute_phi(-e_2, e_3) + phi_1 = self.compute_phi(e_1, -e_2) + phi_2 = self.compute_phi(e_2, -e_3) ly1 = self.compute_LINY1(e_1, e_2, e_3, r_1, phi_1, ay) - ly2 = self.compute_LINY2(e_1, e_2, e_3, r_1, r_2, phi_1, ay) - ly4 = self.compute_LINY4(e_1, e_2, e_3, r_2, r_3, phi_1, ay) + ly2 = self.compute_LINY2(e_1, e_2, e_3, r_1, r_2, phi_1, phi_2, ax, ay) + ly4 = self.compute_LINY4(e_1, e_2, e_3, r_3, phi_1, ay) ly3 = -ly1 - ly2 - ly4 values = [ly1, ly2, ly3, ly4] @@ -283,13 +284,23 @@ def run(self, carts, B_proj, proj=np.array([]), second_order=False): self.B = np.array(rows) - tol = 1e-4 + # if len(self.proj): + # print("Redundancy Check:") + # # B_projected = np.dot(self.proj.T,self.B) + # B_projected = self.B + # proj, eigs, _ = LA.svd(B_projected) + # for i in eigs: + # print(i) + # print(proj[:,-1]) + # raise RuntimeError + + tol = self.options.red_tol # Now we acquire a linearly independant set of internal coordinates from the diagonalized # BB^T Matrix if not self.options.man_proj: if self.options.coords.upper() != "ZMAT": proj, eigs, _ = LA.svd(self.B) - proj[np.abs(proj) < tol] = 0 + # proj[np.abs(proj) < tol] = 0 print("proj singular values:") print(eigs) if B_proj: @@ -375,60 +386,52 @@ def compute_LIN(self, e_1, e_2, e_3, r, theta): return s def compute_LINX1(self, e_1, e_2, e_3, r, phi_1, phi_2, ax): - a = -ax * e_1 / (r * np.sin(phi_1) ** 2) - b = -e_2 * (ax * (np.tan(phi_1) ** -1) + np.cos(phi_2)) / (r * np.sin(phi_1)) - c = -e_3 / (r * np.sin(phi_1)) - s = a + b + c + denom = r * np.sin(phi_1) + a = -ax * e_1 / np.sin(phi_1) + b = -e_2 * (ax / np.tan(phi_1) + np.cos(phi_2)) + s = a + b - e_3 + s /= denom return s def compute_LINX2(self, e_1, e_2, e_3, r_1, r_2, phi_1, phi_2, ax): - a = ( - e_1 - * (ax * (r_2 - r_1 * np.cos(phi_1)) - r_1 * np.sin(phi_1) * np.cos(phi_2)) - / (r_1 * r_2 * np.sin(phi_1) ** 2) - ) - b = ( - -e_2 - * ( - ax * (np.tan(phi_1) ** -1) * (r_1 * np.cos(phi_1) - r_2) - + np.cos(phi_2) * (2 * r_1 * np.cos(phi_1) - r_2) - ) - / (r_1 * r_2 * np.sin(phi_1)) - ) - c = -e_3 * (r_1 * np.cos(phi_1) - r_2) / (r_1 * r_2 * np.sin(phi_1)) - s = a + b + c + cos_1 = np.cos(phi_1) + sin_1 = np.sin(phi_1) + denom = r_1 * r_2 * sin_1 + piece_1 = ax * (r_2 - r_1 * cos_1) / sin_1 + a = e_1 * (piece_1 - r_1 * np.cos(phi_2)) + piece_1 = ax * (r_1 * cos_1 - r_2) / np.tan(phi_1) + piece_2 = np.cos(phi_2) * (2 * r_1 * cos_1 - r_2) + b = -e_2 * (piece_1 + piece_2) + c = e_3 * (r_2 - r_1 * cos_1) + s = (a + b + c) / denom return s def compute_LINX4(self, e_1, e_2, e_3, r, phi, ax): a = e_1 / (r * np.sin(phi)) - b = e_2 * (np.tan(phi) ** -1) / r + b = e_2 / (r * np.tan(phi)) c = ax * e_3 / r s = a + b + c return s def compute_LINY1(self, e_1, e_2, e_3, r, phi, ay): - a = -ay * (np.tan(phi) ** -1) * (e_1 * np.cos(phi) + e_2) / (r * np.sin(phi)) - b = np.cross(e_2, -e_3) / (r * np.sin(phi)) - c = -e_1 * ay / r - s = a + b + c + denom = r * np.sin(phi) + a = ay * (e_1 + np.cos(phi) * e_2) / np.sin(phi) + b = np.cross(-e_2, -e_3) + s = -(a + b) / denom return s - def compute_LINY2(self, e_1, e_2, e_3, r_1, r_2, phi, ay): - a = ( - -np.cos(phi) - * ay - * (e_1 * (r_1 - r_2 * np.cos(phi)) - e_2 * (r_2 - r_1 * np.cos(phi))) - / (r_1 * r_2 * np.sin(phi) ** 2) - ) - b = (r_2 * np.cross(e_2, -e_3) + r_1 * np.cross(-e_3, e_1)) / ( - r_1 * r_2 * np.sin(phi) - ) - c = ay * (e_1 * r_2 + e_2 * r_1) / (r_1 * r_2) - s = a + b + c + def compute_LINY2(self, e_1, e_2, e_3, r_1, r_2, phi_1, phi_2, ax, ay): + sin_1 = np.sin(phi_1) + cos_1 = np.cos(phi_1) + denom = r_1 * r_2 * sin_1 + a = r_2 * ay * (e_1 + cos_1 * e_2) / sin_1 + b = r_1 * (ax / np.tan(phi_1) + np.cos(phi_2)) * np.cross(e_1, -e_2) + c = r_2 * np.cross(-e_2, -e_3) + s = (a + b + c) / denom return s - def compute_LINY4(self, e_1, e_2, e_3, r_1, r_2, phi, ay): - s = ((np.sin(phi) ** -1) * np.cross(e_1, e_2) + e_3 * ay) / r_2 + def compute_LINY4(self, e_1, e_2, e_3, r, phi, ay): + s = -(np.cross(e_1, -e_2) / np.sin(phi) - e_3 * ay) / r return s def calc_OOP(self, x1, x2, x3, x4): @@ -475,14 +478,16 @@ def compute_phi(self, e_1, e_2): return phi def calc_alpha_x(self, e_1, e_2, e_3): - theta = self.compute_phi(e_1, e_2) - s = np.dot(np.cross(e_1, e_2), np.cross(-e_2, e_3)) - ax = s / np.sin(theta) + theta_1 = self.compute_phi(e_1, -e_2) + theta_2 = self.compute_phi(e_2, -e_3) + a = np.cos(theta_1) * np.cos(theta_2) + b = np.dot(e_1, -e_3) + ax = (a + b) / np.sin(theta_1) return ax def calc_alpha_y(self, e_1, e_2, e_3): - theta = self.compute_phi(e_1, e_2) - s = np.dot(e_1, np.cross(-e_2, e_3)) + theta = self.compute_phi(e_1, -e_2) + s = np.dot(e_1, np.cross(e_2, -e_3)) ay = s / np.sin(theta) return ay @@ -497,7 +502,7 @@ def calc_r_com(self, cart1, cart2, mass1, mass2): return rc def num_differentiate(self, B_list_p, B_list_m): - disp_size = self.options.disp + disp_size = self.options.disp_b # Numerical first derivative B = (B_list_p - B_list_m) / (2 * disp_size) diff --git a/concordantmodes/sapelo_template.py b/concordantmodes/sapelo_template.py index f380946..632ad34 100644 --- a/concordantmodes/sapelo_template.py +++ b/concordantmodes/sapelo_template.py @@ -13,7 +13,7 @@ def __init__(self, options, job_num, prog_name, prog): self.progdict = { "molpro": "molpro -n $NSLOTS --nouse-logfile --no-xml-output -o \ output.dat input.dat", - "psi4": "psi4 -n $NSLOTS", + "psi4": "psi4 -n $THREADS", "cfour": prog + "+vectorization", "orca": "", } @@ -41,7 +41,8 @@ def __init__(self, options, job_num, prog_name, prog): #SBATCH --ntasks=4 # Number of MPI ranks #SBATCH --ntasks-per-node=4 # How many tasks on each node #SBATCH --cpus-per-task=1 # Number of cores per MPI rank -#SBATCH --mem=32GB # Memory per processor +#SBATCH --mem=120GB # Memory per processor +#SBATCH --gres=lscratch:100 #SBATCH --time={time_limit} #SBATCH --output="%x.%j".out # Standard output log #SBATCH --error="%x.%j".err # Standard error log @@ -53,36 +54,37 @@ def __init__(self, options, job_num, prog_name, prog): module load intel/2022a # to change scratch dir to use local machine scratch -export SCRATCH_DIR=/scratch/$USER/tmp/$SLURM_JOB_ID +export SCRATCH_DIR=/lscratch/$USER/tmp/$SLURM_JOB_ID mkdir -p $SCRATCH_DIR export APPTAINER_BIND="$SLURM_SUBMIT_DIR,$SCRATCH_DIR" export TMPDIR=$SCRATCH_DIR -mpirun -n $NSLOTS apptainer exec /work/jttlab/containers/molpro-2021-gapr.sif molpro.exe input.dat --output $SLURM_SUBMIT_DIR/output.dat --nouse-logfile --directory $SCRATCH_DIR +mpirun -n $NSLOTS apptainer exec /work/hfslab/containers/molpro-2021-gapr.sif molpro.exe input.dat --output $SLURM_SUBMIT_DIR/output.dat --nouse-logfile --directory $SCRATCH_DIR rm $SCRATCH_DIR -r #ignored line -- do not remove """ + # SBATCH --ntasks-per-node=1 # How many tasks on each node + # SBATCH --nodes=1 # Number of nodes + # export NSLOTS=1 elif self.prog_name == "psi4": self.sapelo_template = """#!/bin/sh #SBATCH --job-name=Concordant # Job name #SBATCH --partition=batch # Partition (queue) name #SBATCH --constraint=EPYC|Intel -#SBATCH --nodes=1 # Number of nodes -#SBATCH --ntasks=4 # Number of MPI ranks -#SBATCH --ntasks-per-node=4 # How many tasks on each node -#SBATCH --cpus-per-task=1 # Number of cores per MPI rank -#SBATCH --mem=32GB # Memory per processor +#SBATCH --ntasks=1 # Number of MPI ranks +#SBATCH --cpus-per-task=8 # Number of cores per MPI rank +#SBATCH --mem=120GB # Memory per processor +#SBATCH --gres=lscratch:100 #SBATCH --time={time_limit} #SBATCH --output="%x.%j".out # Standard output log #SBATCH --error="%x.%j".err # Standard error log cd $SLURM_SUBMIT_DIR -export NSLOTS=4 -export THREADS=1 +export THREADS=8 set -eE trap 'cleanup' EXIT @@ -91,13 +93,15 @@ def __init__(self, options, job_num, prog_name, prog): echo "Exiting. Performing Cleanup" rm $PSI_SCRATCH -r }} - -export PSI_SCRATCH=/scratch/$USER/tmp/$SLURM_JOB_ID +source ~/.bashrc +conda activate psi4_dlpno +export PSI_SCRATCH=/lscratch/$USER/tmp/$SLURM_JOB_ID mkdir -p $PSI_SCRATCH -psi4 -n $NSLOTS -o output.dat +psi4 -n $THREADS -o output.dat #ignored line -- do not remove """ + # psi4 -n $NSLOTS -o output.dat # rm $PSI_SCRATCH -r elif self.prog_name == "orca": self.sapelo_template = """#!/bin/bash @@ -108,7 +112,8 @@ def __init__(self, options, job_num, prog_name, prog): #SBATCH --ntasks=4 # Number of MPI ranks #SBATCH --ntasks-per-node=4 # How many tasks on each node #SBATCH --cpus-per-task=1 # Number of cores per MPI rank -#SBATCH --mem=10G # total Memory no more per processor +#SBATCH --mem=120G # total Memory no more per processor +#SBATCH --gres=lscratch:100 #SBATCH --time={time_limit} #SBATCH --output="%x.%j".out # Standard output log #SBATCH --error="%x.%j".err # Standard error log @@ -119,7 +124,7 @@ def __init__(self, options, job_num, prog_name, prog): export NSLOTS=4 export THREADS=1 # can try adjusting but I don't think orca uses much omp threading if any -scratch_dir=/scratch/$USER/tmp/$SLURM_JOB_ID +scratch_dir=/lscratch/$USER/tmp/$SLURM_JOB_ID mkdir -p $scratch_dir module=ORCA/6.1.0-OpenMPI-4.1.8-GCC-13.3.0-avx2 @@ -153,6 +158,77 @@ def __init__(self, options, job_num, prog_name, prog): rm $scratch_dir -r +""" + elif self.prog_name == "cfour": + self.sapelo_template = """#!/bin/bash +#SBATCH --partition=batch +#SBATCH --constraint="EPYC|Intel" +#SBATCH --job-name=azulene-1 +#SBATCH --ntasks=1 +#SBATCH --cpus-per-task=1 +#SBATCH --time=4:00:00 +#SBATCH --mem=60GB +#SBATCH --gres=lscratch:100 +#SBATCH --output="%x.%j".out # Standard output log +#SBATCH --error="%x.%j".err # Standard error log + +cd $SLURM_SUBMIT_DIR +export NSLOTS=1 + +module=cfour/2.1-intel-2023a-serial +export OMP_NUM_THREADS=$NSLOTS + +scratch_dir=/lscratch/$USER/tmp/$SLURM_JOB_ID +mkdir -p $scratch_dir + + +# make sure MRCC is around just in case +export PATH=$PATH:/work/hfslab/mrcc/2020/ +prefix=/apps/eb/$module/ +module load $module + +# Copy job data +if [[ -e input.dat && ! -e ZMAT ]]; then + cp input.dat $scratch_dir/ZMAT +else + cp $SLURM_SUBMIT_DIR/ZMAT $scratch_dir +fi + +cp $prefix/basis/GENBAS $scratch_dir +cp $prefix/basis/ECPDATA $scratch_dir +if [ -e JAINDX ]; then cp JAINDX $scratch_dir ; fi +if [ -e JOBARC ]; then cp JOBARC $scratch_dir ; fi +if [ -e FCMINT ]; then cp FCMINT $scratch_dir ; fi +if [ -e GENBAS ]; then cp GENBAS $scratch_dir ; fi +if [ -e ECPDATA ]; then cp ECPDATA $scratch_dir ; fi +if [ -e OPTARC ]; then cp OPTARC $scratch_dir ; fi +if [ -e ISOTOPES ]; then cp ISOTOPES $scratch_dir ; fi +if [ -e ISOMASS ]; then cp ISOMASS $scratch_dir ; fi +if [ -e initden.dat ]; then cp initden.dat $scratch_dir ; fi +if [ -e OLDMOS ]; then cp OLDMOS $scratch_dir ; fi + +echo " Running cfour on `hostname`" +echo " Running calculation..." + +cd $scratch_dir +xcfour >& $SLURM_SUBMIT_DIR/output.dat +xja2fja + +echo " Saving data and cleaning up..." +if [ -e ZMATnew ]; then cp -f ZMATnew $SLURM_SUBMIT_DIR/ZMATnew ; fi +if [ -e GRD ]; then cp -f GRD $SLURM_SUBMIT_DIR/GRD ; fi +if [ -e FCMFINAL ]; then cp -f FCMFINAL $SLURM_SUBMIT_DIR/FCMFINAL ; fi + +# Create a job data archive file +tar --transform "s,^,Job_Data_$SLURM_JOB_ID/," -vcf $SLURM_SUBMIT_DIR/Job_Data_$SLURM_JOB_ID.tar OPTARC FCMINT FCMFINAL ZMATnew JMOL.plot JOBARC JAINDX FJOBARC DIPDER HESSIAN MOLDEN NEWMOS den.dat +if [ -e zmat001 ]; then tar --transform "s,^,Job_Data_$SLURM_JOB_ID/," -vrf $SLURM_SUBMIT_DIR/Job_Data_$SLURM_JOB_ID.tar zmat* ; fi +gzip $SLURM_SUBMIT_DIR/Job_Data_$SLURM_JOB_ID.tar + +echo " Job complete on `hostname`." + +rm $scratch_dir -r + +# ignored line -- do not remove """ def run(self): diff --git a/concordantmodes/symmetry.py b/concordantmodes/symmetry.py index 904af47..9ab3e12 100644 --- a/concordantmodes/symmetry.py +++ b/concordantmodes/symmetry.py @@ -292,12 +292,17 @@ def create_flat_sym_sort(self, sym_sort): self.flat_sym_sort = np.array([]) self.flat_sym_sort_inv = np.array([]) for i in range(len(sym_sort)): - self.flat_sym_sort = np.append(self.flat_sym_sort, sym_sort[i]) + irrep = sym_sort[i].copy() + if len(np.array(sym_sort[i]).shape) > 1: + print(np.transpose(irrep)) + irrep = np.transpose(irrep) + self.flat_sym_sort = np.append(self.flat_sym_sort, irrep) self.flat_sym_sort = self.flat_sym_sort.astype(int) print("Flattened sym_sort: ") print(self.flat_sym_sort) + print(sym_sort) for i in range(len(self.flat_sym_sort)): self.flat_sym_sort_inv = np.append( self.flat_sym_sort_inv, np.where(self.flat_sym_sort == i)[0][0] @@ -305,58 +310,90 @@ def create_flat_sym_sort(self, sym_sort): # flat_sym_sort_inv = flat_sym_sort_inv[flat_sym_sort] self.flat_sym_sort_inv = self.flat_sym_sort_inv.astype(int) - def GF_sym_sort(self, F, g_mat, sym_sort): + # Need to include degenerate logic + def GF_sym_sort(self, F, G, sym_sort): Fbuff1 = np.array([]) Fbuff2 = {} Gbuff1 = np.array([]) Gbuff2 = {} - for i in range(len(sym_sort)): + + F_sym = F[self.flat_sym_sort].copy() + F_sym = F_sym[:, self.flat_sym_sort] + np.set_printoptions(threshold=np.inf, precision=8, linewidth=1000) + print("Sym Force Constants:") + print(F_sym) + + g_sym = G[self.flat_sym_sort].copy() + g_sym = g_sym[:, self.flat_sym_sort] + g_sym[np.abs(g_sym) < 1e-9] = 0 + print("Sym G-Matrix:") + print(sym_sort) + print(g_sym) + + for i, irrep in enumerate(sym_sort): Fbuff1 = F.copy() - Fbuff1 = Fbuff1[sym_sort[i]] - Fbuff1 = np.array([Fbuff1[:, sym_sort[i]]]) - Fbuff2[str(i)] = Fbuff1.copy() - Gbuff1 = g_mat.G.copy() - Gbuff1 = Gbuff1[sym_sort[i]] - Gbuff1 = np.array([Gbuff1[:, sym_sort[i]]]) - Gbuff2[str(i)] = Gbuff1.copy() - Fbuff3 = Fbuff2[str(0)][0].copy() - Gbuff3 = Gbuff2[str(0)][0].copy() + Gbuff1 = G.copy() + # Separate logic here to average the FC blocks of degenerate blocks + if len(np.shape(irrep)) > 1: + print("F averaging:") + Fbuff2[str(i)] = self._average_blocks(Fbuff1, F, irrep) + print("G averaging:") + Gbuff2[str(i)] = self._average_blocks(Gbuff1, G, irrep) + else: + sym_int = np.array(irrep).astype(int) + print("sym_int: ") + print(sym_int) + + Fbuff1 = F.copy() + Fbuff1 = Fbuff1[sym_int] + Fbuff1 = np.array(Fbuff1[:, sym_int]) + print("Sym block F:") + print(Fbuff1) + Fbuff2[str(i)] = Fbuff1.copy() + Gbuff1 = G.copy() + Gbuff1 = Gbuff1[sym_int] + Gbuff1 = np.array(Gbuff1[:, sym_int]) + Gbuff2[str(i)] = Gbuff1.copy() + Fbuff3 = Fbuff2[str(0)].copy() + Gbuff3 = Gbuff2[str(0)].copy() for i in range(len(sym_sort) - 1): - Fbuff3 = np.block( - [ - [Fbuff3, np.zeros((len(Fbuff3), len(Fbuff2[str(i + 1)][0])))], + if len(sym_sort[i + 1]): + Fbuff3 = np.block( [ - np.zeros((len(Fbuff2[str(i + 1)][0]), len(Fbuff3))), - Fbuff2[str(i + 1)][0], - ], - ] - ) - Gbuff3 = np.block( - [ - [Gbuff3, np.zeros((len(Gbuff3), len(Gbuff2[str(i + 1)][0])))], + [Fbuff3, np.zeros((len(Fbuff3), len(Fbuff2[str(i + 1)])))], + [ + np.zeros((len(Fbuff2[str(i + 1)]), len(Fbuff3))), + Fbuff2[str(i + 1)], + ], + ] + ) + Gbuff3 = np.block( [ - np.zeros((len(Gbuff2[str(i + 1)][0]), len(Gbuff3))), - Gbuff2[str(i + 1)][0], - ], - ] - ) + [Gbuff3, np.zeros((len(Gbuff3), len(Gbuff2[str(i + 1)])))], + [ + np.zeros((len(Gbuff2[str(i + 1)]), len(Gbuff3))), + Gbuff2[str(i + 1)], + ], + ] + ) F = Fbuff3[self.flat_sym_sort_inv] F = F[:, self.flat_sym_sort_inv] - g_mat.G = Gbuff3[self.flat_sym_sort_inv] - g_mat.G = g_mat.G[:, self.flat_sym_sort_inv] + G = Gbuff3[self.flat_sym_sort_inv] + G = G[:, self.flat_sym_sort_inv] F_sym = F[self.flat_sym_sort].copy() F_sym = F_sym[:, self.flat_sym_sort] + np.set_printoptions(threshold=np.inf, precision=8, linewidth=1000) print("Sym Force Constants:") print(F_sym) - g_sym = g_mat.G[self.flat_sym_sort].copy() + g_sym = G[self.flat_sym_sort].copy() g_sym = g_sym[:, self.flat_sym_sort] g_sym[np.abs(g_sym) < 1e-9] = 0 print("Sym G-Matrix:") print(sym_sort) print(g_sym) - return F, g_mat.G + return F, G def cma2_sym_sort(self, sym_sort, od_inds, irreps_b, F_inter, xi, xi_tol_i): total_off_diags_buff = 0 @@ -376,26 +413,67 @@ def cma2_sym_sort(self, sym_sort, od_inds, irreps_b, F_inter, xi, xi_tol_i): total_off_diags_buff += (len(irrep) ** 2 - len(irrep)) / 2 return od_inds, total_off_diags_buff + def _average_blocks(self, Mbuff, M, irrep): + + sym_sort_T = np.array(irrep).T + Mbuff_degen = np.zeros(len(sym_sort_T), dtype=object) + M_sign = {} + + for j in range(len(sym_sort_T)): + Mbuff = Mbuff[sym_sort_T[j].astype(int)] + Mbuff = Mbuff[:, sym_sort_T[j].astype(int)] + Mbuff_degen[j] = Mbuff + M_sign[str(j)] = np.sign(Mbuff) + Mbuff = M.copy() + + np.set_printoptions(threshold=np.inf, precision=6, linewidth=1000) + + M_ave = np.sum(np.abs(Mbuff_degen), axis=0) / len(sym_sort_T) + + for j in range(len(sym_sort_T)): + Mbuff_degen[j] = np.multiply(M_ave, M_sign[str(j)]) + + Mbuff = Mbuff_degen[0].copy() + + for j in range(len(sym_sort_T) - 1): + Mbuff = np.block( + [ + [Mbuff, np.zeros((len(Mbuff), len(Mbuff_degen[j + 1])))], + [ + np.zeros((len(Mbuff_degen[j + 1]), len(Mbuff))), + Mbuff_degen[j + 1], + ], + ] + ) + + return Mbuff + def create_sym_sort_disps(self, sym_sort, indices): sym_disps = [] for i in sym_sort: + if len(np.array(i).shape) > 1: + i = np.array(i).flatten() for j in indices: if j[0] in i and j[1] in i: sym_disps.append([j[0], j[1]]) return sym_disps - def mode_symmetry_sort(self, TED, sym_sort, freqs): + def mode_symmetry_sort(self, TED, sym_sort, freqs, percent_tol=90.0): ref_TED_b = TED sym_modes = [] for irrep in sym_sort: irrep_modes = [] + if len(np.array(irrep).shape) > 1: + buff_irrep = np.array(irrep).flatten() + else: + buff_irrep = irrep for i in range(len(ref_TED_b.T)): Sum = 0 - for j in irrep: + for j in buff_irrep: Sum += ref_TED_b.T[i, j] - if Sum > 80.0: + if Sum > percent_tol: irrep_modes.append(i) - if len(irrep_modes) != len(irrep): + if len(irrep_modes) != len(buff_irrep): print("Something's wrong with the irrep symmetry sorter:") raise RuntimeError sym_modes.append(irrep_modes) @@ -403,10 +481,13 @@ def mode_symmetry_sort(self, TED, sym_sort, freqs): sym_freqs = copy.deepcopy(sym_modes) del_list = [] for i in range(len(sym_modes)): - for j in range(len(sym_modes[i])): - index = sym_modes[i][j] - sym_freqs[i][j] = freqs[index].copy() - sym_freqs[i].reverse() + if len(sym_modes[i]) > 0: + for j in range(len(sym_modes[i])): + index = sym_modes[i][j] + sym_freqs[i][j] = freqs[index].copy() + sym_freqs[i].reverse() + else: + pass del_list.reverse() if len(del_list): for i in del_list: diff --git a/concordantmodes/tests/test_transf_disp.py b/concordantmodes/tests/test_transf_disp.py index 7fdbbe2..bc6d862 100644 --- a/concordantmodes/tests/test_transf_disp.py +++ b/concordantmodes/tests/test_transf_disp.py @@ -49,6 +49,9 @@ def test_transf_disp(): os.chdir(suite.root) + print(np.asarray(disp_ref)) + print(disps.m_disp[3][3]) + assert np.allclose(np.asarray(disp_ref), disps.m_disp[3][3], rtol=0.0, atol=1e-5) diff --git a/concordantmodes/transf_disp.py b/concordantmodes/transf_disp.py index 19732ea..768d933 100644 --- a/concordantmodes/transf_disp.py +++ b/concordantmodes/transf_disp.py @@ -152,7 +152,6 @@ def __init__( self.ref_carts = self.zmat.cartesians_a.copy() self.ref_carts = np.array(self.ref_carts).astype(float) self.u = np.identity(3 * len(zmat.atom_list)) - self.disp = self.options.disp self.proj = proj self.eigs = eigs self.disp_cart = {} @@ -162,13 +161,22 @@ def __init__( self.deriv_level = deriv_level self.coord_type = coord_type self.cma_level = cma_level + if cma_level.upper() == "A": + self.disp = self.options.disp_a + elif cma_level.upper() == "B": + self.disp = self.options.disp_b + elif cma_level.upper() == "C": + self.disp = self.options.disp_c + else: + print("Not a valid cma_level specification.") + raise RuntimeError def run(self, fc=None): np.set_printoptions(precision=8, linewidth=240) fc = np.asarray(fc) if fc is not None else np.array([]) - self._build_eig_inv() + self.eig_inv = self._build_eig_inv(self.eigs) if self.coord_type == "internal": self._run_internal(fc) @@ -179,21 +187,23 @@ def run(self, fc=None): else: raise RuntimeError("coord_type must be either 'cartesian' or 'internal'.") - def _build_eig_inv(self, proj_tol=1.0e-3): + def _build_eig_inv(self, eigs, proj_tol=1.0e-3): """ Invert and normalize the eigenvector matrix. """ - self.eig_inv = LA.inv(self.eigs) + eig_inv = LA.inv(eigs) - for i, row in enumerate(self.eig_inv): + for i, row in enumerate(eig_inv): row /= LA.norm(row) thresh = np.max(np.abs(row)) * proj_tol row[np.abs(row) < thresh] = 0.0 - self.eig_inv[i] = row + eig_inv[i] = row + + return eig_inv def _build_mass_matrix(self): """ @@ -267,19 +277,15 @@ def _build_displacements(self, fc): [scale / abs(fc[i, i]) ** 0.25 for i in range(len(self.disp))] ) - print("Reduced displacements") - print(self.disp) - # - # CMA scaling + # Scaling the initial disps such that the largest displaced coordinate + # is normalized to the target displacement size # elif self.options.scaled_disp and self.cma_level == "B": for i in range(len(self.disp)): self.disp[i] /= np.max(self.proj.T[i]) - self.disp[i] /= np.max(self.eig_inv[i]) - self.disp[i] *= LA.norm(self.eig_inv[i]) def _build_second_order_A2(self): @@ -313,6 +319,7 @@ def _generate_internal_first_deriv(self, A2): disp = np.zeros(n) disp[i] = self.disp[i] + print(disp) p_disp[i] = self.coord_convert( disp, @@ -381,7 +388,7 @@ def _generate_internal_second_deriv(self, A2): # Now we have the cartesian functions. def _run_cartesian(self): - self.disp_mag = self.options.disp + self.disp_mag = self.options.disp_b if self.deriv_level == 1: self._generate_cartesian_first_deriv() @@ -426,10 +433,10 @@ def _generate_cartesian_second_deriv(self): minus = ref.copy() plus[i] += self.disp - plus[j] += self.disp - minus[i] -= self.disp - minus[j] -= self.disp + if i != j: + plus[j] += self.disp + minus[j] -= self.disp p_disp[i, j] = plus.reshape(-1, 3) m_disp[i, j] = minus.reshape(-1, 3) @@ -685,13 +692,14 @@ def coord_convert( new_carts += cart_disp_shaped coord_check = self.int_c(new_carts, self.eig_inv, self.proj) n_disp = new_n - coord_check - - if tight_disp: - sVec = s_vec(zmat, options) - sVec.run(new_carts, False) - A = self.compute_A( - sVec.B, self.proj, self.eig_inv, self.zmat.mass_weight - ) + # print(n_disp) + + # if tight_disp: + # sVec = s_vec(zmat, options) + # sVec.run(new_carts, False) + # A = self.compute_A( + # sVec.B, self.proj, self.eig_inv, self.zmat.mass_weight + # ) if LA.norm(n_disp) < tolerance: break if LA.norm(n_disp) > tolerance: @@ -712,20 +720,26 @@ def compute_A(self, B, proj, eig_inv, u, cma_level="B"): L = inv(eig_inv) - A = LA.pinv(B) # (3N x s) - A = np.dot(A, proj) # (3N x S) - A = A.T # (S x 3N) + # A = LA.pinv(B) # (3N x s) + # A = np.dot(A, proj) # (3N x S) + # A = A.T # (S x 3N) # This could be necessary # for intensities. - # B = np.dot(proj.T,B) + B = np.dot(proj.T, B) + + # Toggle this # u = np.eye(len(B.T)) - # # A = inv(B.dot(np.sqrt(u)).dot(B.T)) # (s x s) - # A = inv(B.dot(u).dot(B.T)) # (s x s) - # A = (B.T).dot(A) # (3N x s) - # # A = np.sqrt(u).dot(A) - # # A = u.dot(A) - # A = A.T # (S x 3N) + + # A = inv(B.dot(np.sqrt(u)).dot(B.T)) # (s x s) + A = inv(B.dot(u).dot(B.T)) # (s x s) + # print(u) + # print("inv G-mat:") + # print(A) + A = (B.T).dot(A) # (3N x s) + # A = np.sqrt(u).dot(A) + A = u.dot(A) + A = A.T # (S x 3N) # This step modifies A to convert from normal coords to carts. A = np.dot(L.T, A) # (Q x 3N) diff --git a/concordantmodes/zmat.py b/concordantmodes/zmat.py index 58aa0e9..090e2ba 100644 --- a/concordantmodes/zmat.py +++ b/concordantmodes/zmat.py @@ -268,7 +268,7 @@ def zmat_read(self, zmat_name): temp = re.findall(self.cartesian_regex, cart_output_b[i]) atom = re.findall(self.cartesian_atom_regex, cart_output_b[i]) self.cartesians_b.append(temp[0]) - self.atom_list.append(atom[0]) + self.atom_list.append(atom[0].upper()) self.cartesians_b = np.array(self.cartesians_b).astype(float) # The masses are assigned to the respective atom from the masses.py file @@ -386,241 +386,20 @@ def zmat_process(self, zmat_output): ) self.torsion_variables.append("D" + str(i - first_index)) elif self.options.coords.upper() == "DELOCALIZED": - count = 0 - if self.options.covalent_radii: - c_r = CovalentRadii() - indices = [] - transdisp_inter = TransfDisp( - None, - self, - 1, - False, - np.array([]), - self.options, - indices, - ) - inter_atomic_len = np.zeros( - (len(self.cartesians_b), len(self.cartesians_b)) - ) - N = len(self.cartesians_b) - adj_mat = np.zeros((N, N)) - for i in range(len(self.cartesians_b)): - for j in range(i): - inter_atomic_len[j, i] = transdisp_inter.calc_bond( - self.cartesians_b[i], self.cartesians_b[j] - ) - if inter_atomic_len[j, i] < self.options.bond_threshold * ( - c_r.get(self.atom_list[i]) + c_r.get(self.atom_list[j]) - ): - count += 1 - adj_mat[i, j] = 1 - adj_mat[j, i] = 1 - self.bond_indices = np.append(self.bond_indices, 0) - self.bond_indices[-1] = np.array( - [str(j + 1), str(i + 1)], dtype=object - ) - self.bond_variables.append("R" + str(count)) - print("Interatomic Distance Matrix:") - print(inter_atomic_len) - print("Adjacency Matrix:") - print(adj_mat) - for i in range(len(adj_mat)): - print("Degree of vertex " + str(i)) - print(np.sum(adj_mat[i])) - adj_mat2 = np.dot(adj_mat, adj_mat) - for i in range(len(adj_mat2)): - adj_mat2[i, i] = 0 - print("Covalent Radius Scale Factor:") - print(self.options.bond_threshold) - print("Resulting bond indices:") - print(self.bond_indices) - else: - for i in range(len(zmat_output)): - if re.search(self.bond_regex, zmat_output[i]): - count += 1 - List = re.findall(self.bond_regex, zmat_output[i])[0] - self.bond_indices = np.append(self.bond_indices, 0) - self.bond_indices[-1] = np.array(List, dtype=object) - self.bond_variables.append("R" + str(count)) + + # Form all possible bonds, whether from qcelemental or manually input + self._build_bonds(zmat_output) # Form all possible angles from bonds - count = 0 - for i in range(len(self.bond_indices)): - for j in range(len(self.bond_indices) - i - 1): - a = np.setdiff1d(self.bond_indices[i], self.bond_indices[i + j + 1]) - b = np.intersect1d( - self.bond_indices[i], self.bond_indices[i + j + 1] - ) - c = np.setdiff1d(self.bond_indices[i + j + 1], self.bond_indices[i]) - if len(a) and len(b) and len(c): - d = np.array([a[0], b[0], c[0]]) - self.angle_indices = np.append(self.angle_indices, 0) - self.angle_indices[-1] = np.array(d, dtype=object) - count += 1 - self.angle_variables.append("A" + str(count)) + self._build_angles() # Form all possible torsions from angles - tor_count = 0 - oop_count = 0 - for i in range(len(self.angle_indices)): - for j in range(len(self.bond_indices)): - a = np.setdiff1d(self.angle_indices[i], self.bond_indices[j]) - b = np.intersect1d(self.angle_indices[i], self.bond_indices[j]) - c = np.setdiff1d(self.bond_indices[j], self.angle_indices[i]) - if len(c) == 1: - d = np.where(self.bond_indices[j] == c)[0][0] - f = 1 - d - g = self.bond_indices[j][f] - h = np.where(self.angle_indices[i] == g)[0][0] - if h == 1: - # This is an out of plane bend - oop = np.array( - [ - self.bond_indices[j][d], - self.bond_indices[j][f], - a[0], - a[1], - ], - dtype=object, - ) - - cont_bool = self.np_contains(self.oop_indices, oop) - if not cont_bool: - self.oop_indices = np.append(self.oop_indices, 0) - self.oop_indices[-1] = np.array(oop, dtype=object) - else: - # This is a torsion - if h: - tor = np.append( - self.angle_indices[i].copy(), - self.bond_indices[j][d], - ) - else: - tor = np.append( - self.bond_indices[j][d], - self.angle_indices[i].copy(), - ) - cont_bool = self.np_contains( - self.torsion_indices, tor, tor=True - ) - if not cont_bool: - self.torsion_indices = np.append( - self.torsion_indices, 0 - ) - self.torsion_indices[-1] = np.array(tor, dtype=object) - - for i in range(len(self.torsion_indices)): - self.torsion_variables.append("D" + str(i + 1)) - for i in range(len(self.oop_indices)): - self.oop_variables.append("O" + str(i + 1)) + self._build_four_center() + # Perform a topological analysis, this is the first step to the + # automatic generation of Natural Internal Coordinates if self.options.topo_analysis: - X_len_walks_dict = {} - X_len_walks_dict["2_length_walks"] = self.bond_indices.copy() - X_len_walks_dict["3_length_walks"] = self.angle_indices.copy() - X_len_walks_dict["4_length_walks"] = self.torsion_indices.copy() - - count = 4 - - prev_walks = self.torsion_indices.copy() - - while True: - new_walks = np.array([]) - for i in range(len(prev_walks)): - for j in range(len(self.bond_indices)): - a = np.where(prev_walks[i] == self.bond_indices[j][0])[0] - b = np.where(prev_walks[i] == self.bond_indices[j][1])[0] - if len(a) and not len(b): - if not a[0]: - new_walk = np.append( - self.bond_indices[j][1], prev_walks[i].copy() - ) - new_walks = np.append(new_walks, new_walk) - elif a[0] == count - 1: - new_walk = np.append( - prev_walks[i].copy(), self.bond_indices[j][1] - ) - new_walks = np.append(new_walks, new_walk) - if len(b) and not len(a): - if not b[0]: - new_walk = np.append( - self.bond_indices[j][0], prev_walks[i].copy() - ) - new_walks = np.append(new_walks, new_walk) - elif b[0] == count - 1: - new_walk = np.append( - prev_walks[i].copy(), self.bond_indices[j][0] - ) - new_walks = np.append(new_walks, new_walk) - - count += 1 - new_walks = new_walks.reshape((-1, count)) - print(new_walks) - new_walks = np.unique(new_walks, axis=0) - - del_list = np.array([]) - for i in range(len(new_walks)): - for j in range(len(new_walks) - i - 1): - a = np.array([new_walks[i], np.flip(new_walks[i + j + 1])]) - a = np.unique(a, axis=0) - if len(a) == 1: - del_list = np.append(del_list, [i + j + 1]) - - del_list = del_list.astype(int) - new_walks = np.delete(new_walks, del_list, axis=0) - prev_walks = new_walks - if count > self.options.topo_max_it or not len(new_walks): - print( - "Walk generator has terminated at walk lengths of " - + str(count) - ) - break - X_len_walks_dict[str(count) + "_length_walks"] = new_walks - print(str(count) + "_length_walks") - print(len(new_walks)) - print(new_walks) - - dict_len = len(X_len_walks_dict) - 1 - - cycles_dict = {} - for i in range(dict_len): - print(str(i + 3)) - cycles_dict[str(i + 3)] = np.array([]) - for j in range(len(X_len_walks_dict[str(i + 3) + "_length_walks"])): - a = np.array( - [ - X_len_walks_dict[str(i + 3) + "_length_walks"][j][0], - X_len_walks_dict[str(i + 3) + "_length_walks"][j][-1], - ] - ) - for k in range(len(self.bond_indices)): - b = np.intersect1d(a, self.bond_indices[k]) - if len(b) == 2: - cycle = X_len_walks_dict[str(i + 3) + "_length_walks"][ - j - ].copy() - cycles_dict[str(i + 3)] = np.append( - cycles_dict[str(i + 3)], cycle - ) - cycles_dict[str(i + 3)] = cycles_dict[str(i + 3)].reshape( - (-1, i + 3) - ) - del_array = np.array([]) - if len(cycles_dict[str(i + 3)]): - for j in range(len(cycles_dict[str(i + 3)])): - for k in range(len(cycles_dict[str(i + 3)]) - j - 1): - a = np.intersect1d( - cycles_dict[str(i + 3)][j], - cycles_dict[str(i + 3)][k + j + 1], - ) - if len(a) == len(cycles_dict[str(i + 3)][0]): - del_array = np.append(del_array, [k + j + 1]) - del_array = del_array.astype(int) - del_array = np.unique(del_array) - cycles_dict[str(i + 3)] = np.delete( - cycles_dict[str(i + 3)], del_array, axis=0 - ) - print(cycles_dict[str(i + 3)]) + self._generate_topology() elif self.options.coords.upper() == "CUSTOM": # This option will allow the user to specify a custom array of @@ -992,13 +771,57 @@ def zmat_print(self): - self.variable_dictionary_b[self.variables[i]] ) ) + print("Angstrom Bond Length Values:") + for i in range(len(self.bond_indices)): + print( + str(self.index_dictionary[self.variables[i]]) + + " " + + self.variables[i] + + " = " + + str(self.variable_dictionary_a[self.variables[i]] * self.Bohr_Ang) + ) if self.options.geom_check: Sum = 0 + bond_diff = [] for i in range(len(self.bond_indices)): - Sum += ( + diff = ( self.variable_dictionary_a[self.variables[i]] - self.variable_dictionary_b[self.variables[i]] - ) ** 2 + ) + Sum += diff**2 + bond_diff.append(diff) + ang_diff = [] + for i in range(len(self.variables) - len(self.bond_indices)): + diff = ( + self.variable_dictionary_a[ + self.variables[i + len(self.bond_indices)] + ] + - self.variable_dictionary_b[ + self.variables[i + len(self.bond_indices)] + ] + ) + # Sum += diff**2 + ang_diff.append(diff) + print("max signed bond diff angstrom") + print(np.max(bond_diff) * self.Bohr_Ang) + print("max signed bond diff bohr") + print(np.max(bond_diff)) + print("min signed bond diff angstrom") + print(np.min(bond_diff) * self.Bohr_Ang) + print("min signed bond diff bohr") + print(np.min(bond_diff)) + print("max abs bond diff angstrom") + print(np.max(np.abs(bond_diff)) * self.Bohr_Ang) + print("max abs bond diff bohr") + print(np.max(np.abs(bond_diff))) + print(bond_diff) + print("max signed ang diff") + print(np.max(ang_diff)) + print("min signed ang diff") + print(np.min(ang_diff)) + print("max abs ang diff") + print(np.max(np.abs(ang_diff))) + print(ang_diff) # print("squared sum: ") # print(Sum) @@ -1025,3 +848,232 @@ def np_contains(self, array1, array2, tor=False): cont_bool = True return cont_bool + + def _build_bonds(self, zmat_output): + bond_count = 0 + if self.options.covalent_radii: + c_r = CovalentRadii() + indices = [] + transdisp_inter = TransfDisp( + None, + self, + 1, + False, + np.array([]), + self.options, + indices, + ) + inter_atomic_len = np.zeros( + (len(self.cartesians_b), len(self.cartesians_b)) + ) + N = len(self.cartesians_b) + adj_mat = np.zeros((N, N)) + for i in range(len(self.cartesians_b)): + for j in range(i): + inter_atomic_len[j, i] = transdisp_inter.calc_bond( + self.cartesians_b[i], self.cartesians_b[j] + ) + if inter_atomic_len[j, i] < self.options.bond_threshold * ( + c_r.get(self.atom_list[i]) + c_r.get(self.atom_list[j]) + ): + bond_count += 1 + adj_mat[i, j] = 1 + adj_mat[j, i] = 1 + self.bond_indices = np.append(self.bond_indices, 0) + self.bond_indices[-1] = np.array( + [str(j + 1), str(i + 1)], dtype=object + ) + self.bond_variables.append("R" + str(bond_count)) + print("Interatomic Distance Matrix:") + print(inter_atomic_len) + print("Adjacency Matrix:") + print(adj_mat) + for i in range(len(adj_mat)): + print("Degree of vertex " + str(i)) + print(np.sum(adj_mat[i])) + adj_mat2 = np.dot(adj_mat, adj_mat) + for i in range(len(adj_mat2)): + adj_mat2[i, i] = 0 + print("Covalent Radius Scale Factor:") + print(self.options.bond_threshold) + else: + for i in range(len(zmat_output)): + if re.search(self.bond_regex, zmat_output[i]): + bond_count += 1 + List = re.findall(self.bond_regex, zmat_output[i])[0] + self.bond_indices = np.append(self.bond_indices, 0) + self.bond_indices[-1] = np.array(List, dtype=object) + self.bond_variables.append("R" + str(bond_count)) + + def _build_angles(self): + ang_count = 0 + for i in range(len(self.bond_indices)): + for j in range(len(self.bond_indices) - i - 1): + a = np.setdiff1d(self.bond_indices[i], self.bond_indices[i + j + 1]) + b = np.intersect1d(self.bond_indices[i], self.bond_indices[i + j + 1]) + c = np.setdiff1d(self.bond_indices[i + j + 1], self.bond_indices[i]) + if len(a) and len(b) and len(c): + d = np.array([a[0], b[0], c[0]]) + self.angle_indices = np.append(self.angle_indices, 0) + self.angle_indices[-1] = np.array(d, dtype=object) + ang_count += 1 + self.angle_variables.append("A" + str(ang_count)) + + def _build_four_center(self): + for i in range(len(self.angle_indices)): + for j in range(len(self.bond_indices)): + a = np.setdiff1d(self.angle_indices[i], self.bond_indices[j]) + b = np.intersect1d(self.angle_indices[i], self.bond_indices[j]) + c = np.setdiff1d(self.bond_indices[j], self.angle_indices[i]) + if len(c) == 1: + d = np.where(self.bond_indices[j] == c)[0][0] + f = 1 - d + g = self.bond_indices[j][f] + h = np.where(self.angle_indices[i] == g)[0][0] + if h == 1: + # This is an out of plane bend + oop = np.array( + [ + self.bond_indices[j][d], + self.bond_indices[j][f], + a[0], + a[1], + ], + dtype=object, + ) + + cont_bool = self.np_contains(self.oop_indices, oop) + if not cont_bool: + self.oop_indices = np.append(self.oop_indices, 0) + self.oop_indices[-1] = np.array(oop, dtype=object) + else: + # This is a torsion + if h: + tor = np.append( + self.angle_indices[i].copy(), + self.bond_indices[j][d], + ) + else: + tor = np.append( + self.bond_indices[j][d], + self.angle_indices[i].copy(), + ) + cont_bool = self.np_contains( + self.torsion_indices, tor, tor=True + ) + if not cont_bool: + self.torsion_indices = np.append(self.torsion_indices, 0) + self.torsion_indices[-1] = np.array(tor, dtype=object) + + for i in range(len(self.torsion_indices)): + self.torsion_variables.append("D" + str(i + 1)) + for i in range(len(self.oop_indices)): + self.oop_variables.append("O" + str(i + 1)) + + def _generate_topology(self): + X_len_walks_dict = {} + X_len_walks_dict["2_length_walks"] = self.bond_indices.copy() + X_len_walks_dict["3_length_walks"] = self.angle_indices.copy() + X_len_walks_dict["4_length_walks"] = self.torsion_indices.copy() + + count = 4 + + prev_walks = self.torsion_indices.copy() + + while True: + new_walks = np.array([]) + for i in range(len(prev_walks)): + for j in range(len(self.bond_indices)): + a = np.where(prev_walks[i] == self.bond_indices[j][0])[0] + b = np.where(prev_walks[i] == self.bond_indices[j][1])[0] + if len(a) and not len(b): + if not a[0]: + new_walk = np.append( + self.bond_indices[j][1], prev_walks[i].copy() + ) + new_walks = np.append(new_walks, new_walk) + elif a[0] == count - 1: + new_walk = np.append( + prev_walks[i].copy(), self.bond_indices[j][1] + ) + new_walks = np.append(new_walks, new_walk) + if len(b) and not len(a): + if not b[0]: + new_walk = np.append( + self.bond_indices[j][0], prev_walks[i].copy() + ) + new_walks = np.append(new_walks, new_walk) + elif b[0] == count - 1: + new_walk = np.append( + prev_walks[i].copy(), self.bond_indices[j][0] + ) + new_walks = np.append(new_walks, new_walk) + + count += 1 + new_walks = new_walks.reshape((-1, count)).tolist() + print(new_walks) + new_walks = np.unique(new_walks, axis=0) + + del_list = np.array([]) + for i in range(len(new_walks)): + for j in range(len(new_walks) - i - 1): + a = np.array([new_walks[i], np.flip(new_walks[i + j + 1])]) + a = np.unique(a, axis=0) + if len(a) == 1: + del_list = np.append(del_list, [i + j + 1]) + + del_list = del_list.astype(int) + new_walks = np.delete(new_walks, del_list, axis=0) + prev_walks = new_walks + if count > self.options.topo_max_it or not len(new_walks): + print("Walk generator has terminated at walk lengths of " + str(count)) + break + X_len_walks_dict[str(count) + "_length_walks"] = new_walks + print(str(count) + "_length_walks") + print(len(new_walks)) + print(new_walks) + + dict_len = len(X_len_walks_dict) - 1 + + print("Looking for rings") + cycles_dict = {} + for i in range(dict_len): + cycles_dict[str(i + 3)] = np.array([]) + for j in range(len(X_len_walks_dict[str(i + 3) + "_length_walks"])): + a = np.array( + [ + X_len_walks_dict[str(i + 3) + "_length_walks"][j][0], + X_len_walks_dict[str(i + 3) + "_length_walks"][j][-1], + ] + ) + for k in range(len(self.bond_indices)): + b = np.intersect1d(a, self.bond_indices[k]) + if len(b) == 2: + cycle = X_len_walks_dict[str(i + 3) + "_length_walks"][j].copy() + cycles_dict[str(i + 3)] = np.append( + cycles_dict[str(i + 3)], cycle + ) + cycles_dict[str(i + 3)] = cycles_dict[str(i + 3)].reshape((-1, i + 3)) + del_array = np.array([]) + if len(cycles_dict[str(i + 3)]): + for j in range(len(cycles_dict[str(i + 3)])): + for k in range(len(cycles_dict[str(i + 3)]) - j - 1): + a = np.intersect1d( + cycles_dict[str(i + 3)][j], + cycles_dict[str(i + 3)][k + j + 1], + ) + if len(a) == len(cycles_dict[str(i + 3)][0]): + del_array = np.append(del_array, [k + j + 1]) + del_array = del_array.astype(int) + del_array = np.unique(del_array) + cycles_dict[str(i + 3)] = np.delete( + cycles_dict[str(i + 3)], del_array, axis=0 + ) + print(f"{i+3}-membered ring detected!") + print(cycles_dict[str(i + 3)]) + + def _generate_walks(self): + pass + + def _find_cycles(self): + pass diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d0c3cbf --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle new file mode 100644 index 0000000..f942356 Binary files /dev/null and b/docs/build/doctrees/environment.pickle differ diff --git a/docs/build/doctrees/index.doctree b/docs/build/doctrees/index.doctree new file mode 100644 index 0000000..c04fd5c Binary files /dev/null and b/docs/build/doctrees/index.doctree differ diff --git a/docs/build/html/.buildinfo b/docs/build/html/.buildinfo new file mode 100644 index 0000000..2ee196d --- /dev/null +++ b/docs/build/html/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file records the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 14aed0bc4646fb98ca79531554330fa8 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/build/html/_sources/index.rst.txt b/docs/build/html/_sources/index.rst.txt new file mode 100644 index 0000000..4634b0b --- /dev/null +++ b/docs/build/html/_sources/index.rst.txt @@ -0,0 +1,17 @@ +.. concordantmodes documentation master file, created by + sphinx-quickstart on Mon Sep 7 12:59:32 2026. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +concordantmodes documentation +============================= + +This program utilizes the Concordant Modes theoretical framework to compute +highly accurate Hessians with linear scaling. The CMA protocol is detailed in +the literature (*J. Am. Chem. Soc.*, 2022, **144**, 51, 23271–23274; +*J. Chem. Theory Comput.*, 2024, **20**, 24, 10886–10898; *J Phys Chem A.* 2026 Apr 13;**130**(16):3249–3260). + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + diff --git a/docs/build/html/_static/alabaster.css b/docs/build/html/_static/alabaster.css new file mode 100644 index 0000000..7e75bf8 --- /dev/null +++ b/docs/build/html/_static/alabaster.css @@ -0,0 +1,663 @@ +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: Georgia, serif; + font-size: 17px; + background-color: #fff; + color: #000; + margin: 0; + padding: 0; +} + + +div.document { + width: 940px; + margin: 30px auto 0 auto; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 220px; +} + +div.sphinxsidebar { + width: 220px; + font-size: 14px; + line-height: 1.5; +} + +hr { + border: 1px solid #B1B4B6; +} + +div.body { + background-color: #fff; + color: #3E4349; + padding: 0 30px 0 30px; +} + +div.body > .section { + text-align: left; +} + +div.footer { + width: 940px; + margin: 20px auto 30px auto; + font-size: 14px; + color: #888; + text-align: right; +} + +div.footer a { + color: #888; +} + +p.caption { + font-family: inherit; + font-size: inherit; +} + + +div.relations { + display: none; +} + + +div.sphinxsidebar { + max-height: 100%; + overflow-y: auto; +} + +div.sphinxsidebar a { + color: #444; + text-decoration: none; + border-bottom: 1px dotted #999; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px solid #999; +} + +div.sphinxsidebarwrapper { + padding: 18px 10px; +} + +div.sphinxsidebarwrapper p.logo { + padding: 0; + margin: -10px 0 0 0px; + text-align: center; +} + +div.sphinxsidebarwrapper h1.logo { + margin-top: -10px; + text-align: center; + margin-bottom: 5px; + text-align: left; +} + +div.sphinxsidebarwrapper h1.logo-name { + margin-top: 0px; +} + +div.sphinxsidebarwrapper p.blurb { + margin-top: 0; + font-style: normal; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: Georgia, serif; + color: #444; + font-size: 24px; + font-weight: normal; + margin: 0 0 5px 0; + padding: 0; +} + +div.sphinxsidebar h4 { + font-size: 20px; +} + +div.sphinxsidebar h3 a { + color: #444; +} + +div.sphinxsidebar p.logo a, +div.sphinxsidebar h3 a, +div.sphinxsidebar p.logo a:hover, +div.sphinxsidebar h3 a:hover { + border: none; +} + +div.sphinxsidebar p { + color: #555; + margin: 10px 0; +} + +div.sphinxsidebar ul { + margin: 10px 0; + padding: 0; + color: #000; +} + +div.sphinxsidebar ul li.toctree-l1 > a { + font-size: 120%; +} + +div.sphinxsidebar ul li.toctree-l2 > a { + font-size: 110%; +} + +div.sphinxsidebar input { + border: 1px solid #CCC; + font-family: Georgia, serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox { + margin: 1em 0; +} + +div.sphinxsidebar .search > div { + display: table-cell; +} + +div.sphinxsidebar hr { + border: none; + height: 1px; + color: #AAA; + background: #AAA; + + text-align: left; + margin-left: 0; + width: 50%; +} + +div.sphinxsidebar .badge { + border-bottom: none; +} + +div.sphinxsidebar .badge:hover { + border-bottom: none; +} + +/* To address an issue with donation coming after search */ +div.sphinxsidebar h3.donation { + margin-top: 10px; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #004B6B; + text-decoration: underline; +} + +a:hover { + color: #6D4100; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: Georgia, serif; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; +} + +div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #DDD; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #EAEAEA; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + margin: 20px 0px; + padding: 10px 30px; + background-color: #EEE; + border: 1px solid #CCC; +} + +div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fafafa; +} + +div.admonition p.admonition-title { + font-family: Georgia, serif; + font-weight: normal; + font-size: 24px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.warning { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.danger { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.error { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.caution { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.attention { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.important { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.note { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.tip { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.hint { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.seealso { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.topic { + background-color: #EEE; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt, code { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.9em; +} + +.hll { + background-color: #FFC; + margin: 0 -12px; + padding: 0 12px; + display: block; +} + +img.screenshot { +} + +tt.descname, tt.descclassname, code.descname, code.descclassname { + font-size: 0.95em; +} + +tt.descname, code.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #EEE; + background: #FDFDFD; + font-size: 0.9em; +} + +table.footnote + table.footnote { + margin-top: -15px; + border-top: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.field-list p { + margin-bottom: 0.8em; +} + +/* Cloned from + * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 + */ +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +table.footnote td.label { + width: .1px; + padding: 0.3em 0 0.3em 0.5em; +} + +table.footnote td { + padding: 0.3em 0.5em; +} + +dl { + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +blockquote { + margin: 0 0 0 30px; + padding: 0; +} + +ul, ol { + /* Matches the 30px from the narrow-screen "li > ul" selector below */ + margin: 10px 0 10px 30px; + padding: 0; +} + +pre { + background: unset; + padding: 7px 30px; + margin: 15px 0px; + line-height: 1.3em; +} + +div.viewcode-block:target { + background: #ffd; +} + +dl pre, blockquote pre, li pre { + margin-left: 0; + padding-left: 30px; +} + +tt, code { + background-color: #ecf0f3; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, code.xref, a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fff; +} + +a.reference { + text-decoration: none; + border-bottom: 1px dotted #004B6B; +} + +a.reference:hover { + border-bottom: 1px solid #6D4100; +} + +/* Don't put an underline on images */ +a.image-reference, a.image-reference:hover { + border-bottom: none; +} + +a.footnote-reference { + text-decoration: none; + font-size: 0.7em; + vertical-align: top; + border-bottom: 1px dotted #004B6B; +} + +a.footnote-reference:hover { + border-bottom: 1px solid #6D4100; +} + +a:hover tt, a:hover code { + background: #EEE; +} + +@media screen and (max-width: 940px) { + + body { + margin: 0; + padding: 20px 30px; + } + + div.documentwrapper { + float: none; + background: #fff; + margin-left: 0; + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + } + + div.sphinxsidebar { + display: block; + float: none; + width: unset; + margin: 50px -30px -20px -30px; + padding: 10px 20px; + background: #333; + color: #FFF; + } + + div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, + div.sphinxsidebar h3 a { + color: #fff; + } + + div.sphinxsidebar a { + color: #AAA; + } + + div.sphinxsidebar p.logo { + display: none; + } + + div.document { + width: 100%; + margin: 0; + } + + div.footer { + display: none; + } + + div.bodywrapper { + margin: 0; + } + + div.body { + min-height: 0; + min-width: auto; /* fixes width on small screens, breaks .hll */ + padding: 0; + } + + .hll { + /* "fixes" the breakage */ + width: max-content; + } + + .rtd_doc_footer { + display: none; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .github { + display: none; + } + + ul { + margin-left: 0; + } + + li > ul { + /* Matches the 30px from the "ul, ol" selector above */ + margin-left: 30px; + } +} + + +/* misc. */ + +.revsys-inline { + display: none!important; +} + +/* Hide ugly table cell borders in ..bibliography:: directive output */ +table.docutils.citation, table.docutils.citation td, table.docutils.citation th { + border: none; + /* Below needed in some edge cases; if not applied, bottom shadows appear */ + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + + +/* relbar */ + +.related { + line-height: 30px; + width: 100%; + font-size: 0.9rem; +} + +.related.top { + border-bottom: 1px solid #EEE; + margin-bottom: 20px; +} + +.related.bottom { + border-top: 1px solid #EEE; +} + +.related ul { + padding: 0; + margin: 0; + list-style: none; +} + +.related li { + display: inline; +} + +nav#rellinks { + float: right; +} + +nav#rellinks li+li:before { + content: "|"; +} + +nav#breadcrumbs li+li:before { + content: "\00BB"; +} + +/* Hide certain items when printing */ +@media print { + div.related { + display: none; + } +} + +img.github { + position: absolute; + top: 0; + border: 0; + right: 0; +} \ No newline at end of file diff --git a/docs/build/html/_static/base-stemmer.js b/docs/build/html/_static/base-stemmer.js new file mode 100644 index 0000000..e6fa0c4 --- /dev/null +++ b/docs/build/html/_static/base-stemmer.js @@ -0,0 +1,476 @@ +// @ts-check + +/**@constructor*/ +BaseStemmer = function() { + /** @protected */ + this.current = ''; + this.cursor = 0; + this.limit = 0; + this.limit_backward = 0; + this.bra = 0; + this.ket = 0; + + /** + * @param {string} value + */ + this.setCurrent = function(value) { + this.current = value; + this.cursor = 0; + this.limit = this.current.length; + this.limit_backward = 0; + this.bra = this.cursor; + this.ket = this.limit; + }; + + /** + * @return {string} + */ + this.getCurrent = function() { + return this.current; + }; + + /** + * @param {BaseStemmer} other + */ + this.copy_from = function(other) { + /** @protected */ + this.current = other.current; + this.cursor = other.cursor; + this.limit = other.limit; + this.limit_backward = other.limit_backward; + this.bra = other.bra; + this.ket = other.ket; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.in_grouping = function(s, min, max) { + /** @protected */ + if (this.cursor >= this.limit) return false; + var ch = this.current.charCodeAt(this.cursor); + if (ch > max || ch < min) return false; + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false; + this.cursor++; + return true; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.go_in_grouping = function(s, min, max) { + /** @protected */ + while (this.cursor < this.limit) { + var ch = this.current.charCodeAt(this.cursor); + if (ch > max || ch < min) + return true; + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) + return true; + this.cursor++; + } + return false; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.in_grouping_b = function(s, min, max) { + /** @protected */ + if (this.cursor <= this.limit_backward) return false; + var ch = this.current.charCodeAt(this.cursor - 1); + if (ch > max || ch < min) return false; + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false; + this.cursor--; + return true; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.go_in_grouping_b = function(s, min, max) { + /** @protected */ + while (this.cursor > this.limit_backward) { + var ch = this.current.charCodeAt(this.cursor - 1); + if (ch > max || ch < min) return true; + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return true; + this.cursor--; + } + return false; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.out_grouping = function(s, min, max) { + /** @protected */ + if (this.cursor >= this.limit) return false; + var ch = this.current.charCodeAt(this.cursor); + if (ch > max || ch < min) { + this.cursor++; + return true; + } + ch -= min; + if ((s[ch >>> 3] & (0X1 << (ch & 0x7))) == 0) { + this.cursor++; + return true; + } + return false; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.go_out_grouping = function(s, min, max) { + /** @protected */ + while (this.cursor < this.limit) { + var ch = this.current.charCodeAt(this.cursor); + if (ch <= max && ch >= min) { + ch -= min; + if ((s[ch >>> 3] & (0X1 << (ch & 0x7))) != 0) { + return true; + } + } + this.cursor++; + } + return false; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.out_grouping_b = function(s, min, max) { + /** @protected */ + if (this.cursor <= this.limit_backward) return false; + var ch = this.current.charCodeAt(this.cursor - 1); + if (ch > max || ch < min) { + this.cursor--; + return true; + } + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) { + this.cursor--; + return true; + } + return false; + }; + + /** + * @param {number[]} s + * @param {number} min + * @param {number} max + * @return {boolean} + */ + this.go_out_grouping_b = function(s, min, max) { + /** @protected */ + while (this.cursor > this.limit_backward) { + var ch = this.current.charCodeAt(this.cursor - 1); + if (ch <= max && ch >= min) { + ch -= min; + if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) != 0) { + return true; + } + } + this.cursor--; + } + return false; + }; + + /** + * @param {string} s + * @return {boolean} + */ + this.eq_s = function(s) + { + /** @protected */ + if (this.limit - this.cursor < s.length) return false; + if (this.current.slice(this.cursor, this.cursor + s.length) != s) + { + return false; + } + this.cursor += s.length; + return true; + }; + + /** + * @param {string} s + * @return {boolean} + */ + this.eq_s_b = function(s) + { + /** @protected */ + if (this.cursor - this.limit_backward < s.length) return false; + if (this.current.slice(this.cursor - s.length, this.cursor) != s) + { + return false; + } + this.cursor -= s.length; + return true; + }; + + /** + * @param {Among[]} v + * @return {number} + */ + this.find_among = function(v) + { + /** @protected */ + var i = 0; + var j = v.length; + + var c = this.cursor; + var l = this.limit; + + var common_i = 0; + var common_j = 0; + + var first_key_inspected = false; + + while (true) + { + var k = i + ((j - i) >>> 1); + var diff = 0; + var common = common_i < common_j ? common_i : common_j; // smaller + // w[0]: string, w[1]: substring_i, w[2]: result, w[3]: function (optional) + var w = v[k]; + var i2; + for (i2 = common; i2 < w[0].length; i2++) + { + if (c + common == l) + { + diff = -1; + break; + } + diff = this.current.charCodeAt(c + common) - w[0].charCodeAt(i2); + if (diff != 0) break; + common++; + } + if (diff < 0) + { + j = k; + common_j = common; + } + else + { + i = k; + common_i = common; + } + if (j - i <= 1) + { + if (i > 0) break; // v->s has been inspected + if (j == i) break; // only one item in v + + // - but now we need to go round once more to get + // v->s inspected. This looks messy, but is actually + // the optimal approach. + + if (first_key_inspected) break; + first_key_inspected = true; + } + } + do { + var w = v[i]; + if (common_i >= w[0].length) + { + this.cursor = c + w[0].length; + if (w.length < 4) return w[2]; + var res = w[3](this); + this.cursor = c + w[0].length; + if (res) return w[2]; + } + i = w[1]; + } while (i >= 0); + return 0; + }; + + // find_among_b is for backwards processing. Same comments apply + /** + * @param {Among[]} v + * @return {number} + */ + this.find_among_b = function(v) + { + /** @protected */ + var i = 0; + var j = v.length + + var c = this.cursor; + var lb = this.limit_backward; + + var common_i = 0; + var common_j = 0; + + var first_key_inspected = false; + + while (true) + { + var k = i + ((j - i) >> 1); + var diff = 0; + var common = common_i < common_j ? common_i : common_j; + var w = v[k]; + var i2; + for (i2 = w[0].length - 1 - common; i2 >= 0; i2--) + { + if (c - common == lb) + { + diff = -1; + break; + } + diff = this.current.charCodeAt(c - 1 - common) - w[0].charCodeAt(i2); + if (diff != 0) break; + common++; + } + if (diff < 0) + { + j = k; + common_j = common; + } + else + { + i = k; + common_i = common; + } + if (j - i <= 1) + { + if (i > 0) break; + if (j == i) break; + if (first_key_inspected) break; + first_key_inspected = true; + } + } + do { + var w = v[i]; + if (common_i >= w[0].length) + { + this.cursor = c - w[0].length; + if (w.length < 4) return w[2]; + var res = w[3](this); + this.cursor = c - w[0].length; + if (res) return w[2]; + } + i = w[1]; + } while (i >= 0); + return 0; + }; + + /* to replace chars between c_bra and c_ket in this.current by the + * chars in s. + */ + /** + * @param {number} c_bra + * @param {number} c_ket + * @param {string} s + * @return {number} + */ + this.replace_s = function(c_bra, c_ket, s) + { + /** @protected */ + var adjustment = s.length - (c_ket - c_bra); + this.current = this.current.slice(0, c_bra) + s + this.current.slice(c_ket); + this.limit += adjustment; + if (this.cursor >= c_ket) this.cursor += adjustment; + else if (this.cursor > c_bra) this.cursor = c_bra; + return adjustment; + }; + + /** + * @return {boolean} + */ + this.slice_check = function() + { + /** @protected */ + if (this.bra < 0 || + this.bra > this.ket || + this.ket > this.limit || + this.limit > this.current.length) + { + return false; + } + return true; + }; + + /** + * @param {number} c_bra + * @return {boolean} + */ + this.slice_from = function(s) + { + /** @protected */ + var result = false; + if (this.slice_check()) + { + this.replace_s(this.bra, this.ket, s); + result = true; + } + return result; + }; + + /** + * @return {boolean} + */ + this.slice_del = function() + { + /** @protected */ + return this.slice_from(""); + }; + + /** + * @param {number} c_bra + * @param {number} c_ket + * @param {string} s + */ + this.insert = function(c_bra, c_ket, s) + { + /** @protected */ + var adjustment = this.replace_s(c_bra, c_ket, s); + if (c_bra <= this.bra) this.bra += adjustment; + if (c_bra <= this.ket) this.ket += adjustment; + }; + + /** + * @return {string} + */ + this.slice_to = function() + { + /** @protected */ + var result = ''; + if (this.slice_check()) + { + result = this.current.slice(this.bra, this.ket); + } + return result; + }; + + /** + * @return {string} + */ + this.assign_to = function() + { + /** @protected */ + return this.current.slice(0, this.limit); + }; +}; diff --git a/docs/build/html/_static/basic.css b/docs/build/html/_static/basic.css new file mode 100644 index 0000000..0028826 --- /dev/null +++ b/docs/build/html/_static/basic.css @@ -0,0 +1,906 @@ +/* + * Sphinx stylesheet -- basic theme. + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin-top: 10px; +} + +ul.search li { + padding: 5px 0; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: inherit; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/docs/build/html/_static/custom.css b/docs/build/html/_static/custom.css new file mode 100644 index 0000000..2a924f1 --- /dev/null +++ b/docs/build/html/_static/custom.css @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/docs/build/html/_static/doctools.js b/docs/build/html/_static/doctools.js new file mode 100644 index 0000000..807cdb1 --- /dev/null +++ b/docs/build/html/_static/doctools.js @@ -0,0 +1,150 @@ +/* + * Base JavaScript utilities for all Sphinx HTML documentation. + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})`, + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)), + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS + && !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) + return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/docs/build/html/_static/documentation_options.js b/docs/build/html/_static/documentation_options.js new file mode 100644 index 0000000..839c7bc --- /dev/null +++ b/docs/build/html/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '0.0.0', + LANGUAGE: 'English', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/docs/build/html/_static/english-stemmer.js b/docs/build/html/_static/english-stemmer.js new file mode 100644 index 0000000..056760e --- /dev/null +++ b/docs/build/html/_static/english-stemmer.js @@ -0,0 +1,1066 @@ +// Generated from english.sbl by Snowball 3.0.1 - https://snowballstem.org/ + +/**@constructor*/ +var EnglishStemmer = function() { + var base = new BaseStemmer(); + + /** @const */ var a_0 = [ + ["arsen", -1, -1], + ["commun", -1, -1], + ["emerg", -1, -1], + ["gener", -1, -1], + ["later", -1, -1], + ["organ", -1, -1], + ["past", -1, -1], + ["univers", -1, -1] + ]; + + /** @const */ var a_1 = [ + ["'", -1, 1], + ["'s'", 0, 1], + ["'s", -1, 1] + ]; + + /** @const */ var a_2 = [ + ["ied", -1, 2], + ["s", -1, 3], + ["ies", 1, 2], + ["sses", 1, 1], + ["ss", 1, -1], + ["us", 1, -1] + ]; + + /** @const */ var a_3 = [ + ["succ", -1, 1], + ["proc", -1, 1], + ["exc", -1, 1] + ]; + + /** @const */ var a_4 = [ + ["even", -1, 2], + ["cann", -1, 2], + ["inn", -1, 2], + ["earr", -1, 2], + ["herr", -1, 2], + ["out", -1, 2], + ["y", -1, 1] + ]; + + /** @const */ var a_5 = [ + ["", -1, -1], + ["ed", 0, 2], + ["eed", 1, 1], + ["ing", 0, 3], + ["edly", 0, 2], + ["eedly", 4, 1], + ["ingly", 0, 2] + ]; + + /** @const */ var a_6 = [ + ["", -1, 3], + ["bb", 0, 2], + ["dd", 0, 2], + ["ff", 0, 2], + ["gg", 0, 2], + ["bl", 0, 1], + ["mm", 0, 2], + ["nn", 0, 2], + ["pp", 0, 2], + ["rr", 0, 2], + ["at", 0, 1], + ["tt", 0, 2], + ["iz", 0, 1] + ]; + + /** @const */ var a_7 = [ + ["anci", -1, 3], + ["enci", -1, 2], + ["ogi", -1, 14], + ["li", -1, 16], + ["bli", 3, 12], + ["abli", 4, 4], + ["alli", 3, 8], + ["fulli", 3, 9], + ["lessli", 3, 15], + ["ousli", 3, 10], + ["entli", 3, 5], + ["aliti", -1, 8], + ["biliti", -1, 12], + ["iviti", -1, 11], + ["tional", -1, 1], + ["ational", 14, 7], + ["alism", -1, 8], + ["ation", -1, 7], + ["ization", 17, 6], + ["izer", -1, 6], + ["ator", -1, 7], + ["iveness", -1, 11], + ["fulness", -1, 9], + ["ousness", -1, 10], + ["ogist", -1, 13] + ]; + + /** @const */ var a_8 = [ + ["icate", -1, 4], + ["ative", -1, 6], + ["alize", -1, 3], + ["iciti", -1, 4], + ["ical", -1, 4], + ["tional", -1, 1], + ["ational", 5, 2], + ["ful", -1, 5], + ["ness", -1, 5] + ]; + + /** @const */ var a_9 = [ + ["ic", -1, 1], + ["ance", -1, 1], + ["ence", -1, 1], + ["able", -1, 1], + ["ible", -1, 1], + ["ate", -1, 1], + ["ive", -1, 1], + ["ize", -1, 1], + ["iti", -1, 1], + ["al", -1, 1], + ["ism", -1, 1], + ["ion", -1, 2], + ["er", -1, 1], + ["ous", -1, 1], + ["ant", -1, 1], + ["ent", -1, 1], + ["ment", 15, 1], + ["ement", 16, 1] + ]; + + /** @const */ var a_10 = [ + ["e", -1, 1], + ["l", -1, 2] + ]; + + /** @const */ var a_11 = [ + ["andes", -1, -1], + ["atlas", -1, -1], + ["bias", -1, -1], + ["cosmos", -1, -1], + ["early", -1, 5], + ["gently", -1, 3], + ["howe", -1, -1], + ["idly", -1, 2], + ["news", -1, -1], + ["only", -1, 6], + ["singly", -1, 7], + ["skies", -1, 1], + ["sky", -1, -1], + ["ugly", -1, 4] + ]; + + /** @const */ var /** Array */ g_aeo = [17, 64]; + + /** @const */ var /** Array */ g_v = [17, 65, 16, 1]; + + /** @const */ var /** Array */ g_v_WXY = [1, 17, 65, 208, 1]; + + /** @const */ var /** Array */ g_valid_LI = [55, 141, 2]; + + var /** boolean */ B_Y_found = false; + var /** number */ I_p2 = 0; + var /** number */ I_p1 = 0; + + + /** @return {boolean} */ + function r_prelude() { + B_Y_found = false; + /** @const */ var /** number */ v_1 = base.cursor; + lab0: { + base.bra = base.cursor; + if (!(base.eq_s("'"))) + { + break lab0; + } + base.ket = base.cursor; + if (!base.slice_del()) + { + return false; + } + } + base.cursor = v_1; + /** @const */ var /** number */ v_2 = base.cursor; + lab1: { + base.bra = base.cursor; + if (!(base.eq_s("y"))) + { + break lab1; + } + base.ket = base.cursor; + if (!base.slice_from("Y")) + { + return false; + } + B_Y_found = true; + } + base.cursor = v_2; + /** @const */ var /** number */ v_3 = base.cursor; + lab2: { + while(true) + { + /** @const */ var /** number */ v_4 = base.cursor; + lab3: { + golab4: while(true) + { + /** @const */ var /** number */ v_5 = base.cursor; + lab5: { + if (!(base.in_grouping(g_v, 97, 121))) + { + break lab5; + } + base.bra = base.cursor; + if (!(base.eq_s("y"))) + { + break lab5; + } + base.ket = base.cursor; + base.cursor = v_5; + break golab4; + } + base.cursor = v_5; + if (base.cursor >= base.limit) + { + break lab3; + } + base.cursor++; + } + if (!base.slice_from("Y")) + { + return false; + } + B_Y_found = true; + continue; + } + base.cursor = v_4; + break; + } + } + base.cursor = v_3; + return true; + }; + + /** @return {boolean} */ + function r_mark_regions() { + I_p1 = base.limit; + I_p2 = base.limit; + /** @const */ var /** number */ v_1 = base.cursor; + lab0: { + lab1: { + /** @const */ var /** number */ v_2 = base.cursor; + lab2: { + if (base.find_among(a_0) == 0) + { + break lab2; + } + break lab1; + } + base.cursor = v_2; + if (!base.go_out_grouping(g_v, 97, 121)) + { + break lab0; + } + base.cursor++; + if (!base.go_in_grouping(g_v, 97, 121)) + { + break lab0; + } + base.cursor++; + } + I_p1 = base.cursor; + if (!base.go_out_grouping(g_v, 97, 121)) + { + break lab0; + } + base.cursor++; + if (!base.go_in_grouping(g_v, 97, 121)) + { + break lab0; + } + base.cursor++; + I_p2 = base.cursor; + } + base.cursor = v_1; + return true; + }; + + /** @return {boolean} */ + function r_shortv() { + lab0: { + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab1: { + if (!(base.out_grouping_b(g_v_WXY, 89, 121))) + { + break lab1; + } + if (!(base.in_grouping_b(g_v, 97, 121))) + { + break lab1; + } + if (!(base.out_grouping_b(g_v, 97, 121))) + { + break lab1; + } + break lab0; + } + base.cursor = base.limit - v_1; + lab2: { + if (!(base.out_grouping_b(g_v, 97, 121))) + { + break lab2; + } + if (!(base.in_grouping_b(g_v, 97, 121))) + { + break lab2; + } + if (base.cursor > base.limit_backward) + { + break lab2; + } + break lab0; + } + base.cursor = base.limit - v_1; + if (!(base.eq_s_b("past"))) + { + return false; + } + } + return true; + }; + + /** @return {boolean} */ + function r_R1() { + return I_p1 <= base.cursor; + }; + + /** @return {boolean} */ + function r_R2() { + return I_p2 <= base.cursor; + }; + + /** @return {boolean} */ + function r_Step_1a() { + var /** number */ among_var; + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab0: { + base.ket = base.cursor; + if (base.find_among_b(a_1) == 0) + { + base.cursor = base.limit - v_1; + break lab0; + } + base.bra = base.cursor; + if (!base.slice_del()) + { + return false; + } + } + base.ket = base.cursor; + among_var = base.find_among_b(a_2); + if (among_var == 0) + { + return false; + } + base.bra = base.cursor; + switch (among_var) { + case 1: + if (!base.slice_from("ss")) + { + return false; + } + break; + case 2: + lab1: { + /** @const */ var /** number */ v_2 = base.limit - base.cursor; + lab2: { + { + /** @const */ var /** number */ c1 = base.cursor - 2; + if (c1 < base.limit_backward) + { + break lab2; + } + base.cursor = c1; + } + if (!base.slice_from("i")) + { + return false; + } + break lab1; + } + base.cursor = base.limit - v_2; + if (!base.slice_from("ie")) + { + return false; + } + } + break; + case 3: + if (base.cursor <= base.limit_backward) + { + return false; + } + base.cursor--; + if (!base.go_out_grouping_b(g_v, 97, 121)) + { + return false; + } + base.cursor--; + if (!base.slice_del()) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_Step_1b() { + var /** number */ among_var; + base.ket = base.cursor; + among_var = base.find_among_b(a_5); + base.bra = base.cursor; + lab0: { + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab1: { + switch (among_var) { + case 1: + /** @const */ var /** number */ v_2 = base.limit - base.cursor; + lab2: { + lab3: { + /** @const */ var /** number */ v_3 = base.limit - base.cursor; + lab4: { + if (base.find_among_b(a_3) == 0) + { + break lab4; + } + if (base.cursor > base.limit_backward) + { + break lab4; + } + break lab3; + } + base.cursor = base.limit - v_3; + if (!r_R1()) + { + break lab2; + } + if (!base.slice_from("ee")) + { + return false; + } + } + } + base.cursor = base.limit - v_2; + break; + case 2: + break lab1; + case 3: + among_var = base.find_among_b(a_4); + if (among_var == 0) + { + break lab1; + } + switch (among_var) { + case 1: + /** @const */ var /** number */ v_4 = base.limit - base.cursor; + if (!(base.out_grouping_b(g_v, 97, 121))) + { + break lab1; + } + if (base.cursor > base.limit_backward) + { + break lab1; + } + base.cursor = base.limit - v_4; + base.bra = base.cursor; + if (!base.slice_from("ie")) + { + return false; + } + break; + case 2: + if (base.cursor > base.limit_backward) + { + break lab1; + } + break; + } + break; + } + break lab0; + } + base.cursor = base.limit - v_1; + /** @const */ var /** number */ v_5 = base.limit - base.cursor; + if (!base.go_out_grouping_b(g_v, 97, 121)) + { + return false; + } + base.cursor--; + base.cursor = base.limit - v_5; + if (!base.slice_del()) + { + return false; + } + base.ket = base.cursor; + base.bra = base.cursor; + /** @const */ var /** number */ v_6 = base.limit - base.cursor; + among_var = base.find_among_b(a_6); + switch (among_var) { + case 1: + if (!base.slice_from("e")) + { + return false; + } + return false; + case 2: + { + /** @const */ var /** number */ v_7 = base.limit - base.cursor; + lab5: { + if (!(base.in_grouping_b(g_aeo, 97, 111))) + { + break lab5; + } + if (base.cursor > base.limit_backward) + { + break lab5; + } + return false; + } + base.cursor = base.limit - v_7; + } + break; + case 3: + if (base.cursor != I_p1) + { + return false; + } + /** @const */ var /** number */ v_8 = base.limit - base.cursor; + if (!r_shortv()) + { + return false; + } + base.cursor = base.limit - v_8; + if (!base.slice_from("e")) + { + return false; + } + return false; + } + base.cursor = base.limit - v_6; + base.ket = base.cursor; + if (base.cursor <= base.limit_backward) + { + return false; + } + base.cursor--; + base.bra = base.cursor; + if (!base.slice_del()) + { + return false; + } + } + return true; + }; + + /** @return {boolean} */ + function r_Step_1c() { + base.ket = base.cursor; + lab0: { + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab1: { + if (!(base.eq_s_b("y"))) + { + break lab1; + } + break lab0; + } + base.cursor = base.limit - v_1; + if (!(base.eq_s_b("Y"))) + { + return false; + } + } + base.bra = base.cursor; + if (!(base.out_grouping_b(g_v, 97, 121))) + { + return false; + } + lab2: { + if (base.cursor > base.limit_backward) + { + break lab2; + } + return false; + } + if (!base.slice_from("i")) + { + return false; + } + return true; + }; + + /** @return {boolean} */ + function r_Step_2() { + var /** number */ among_var; + base.ket = base.cursor; + among_var = base.find_among_b(a_7); + if (among_var == 0) + { + return false; + } + base.bra = base.cursor; + if (!r_R1()) + { + return false; + } + switch (among_var) { + case 1: + if (!base.slice_from("tion")) + { + return false; + } + break; + case 2: + if (!base.slice_from("ence")) + { + return false; + } + break; + case 3: + if (!base.slice_from("ance")) + { + return false; + } + break; + case 4: + if (!base.slice_from("able")) + { + return false; + } + break; + case 5: + if (!base.slice_from("ent")) + { + return false; + } + break; + case 6: + if (!base.slice_from("ize")) + { + return false; + } + break; + case 7: + if (!base.slice_from("ate")) + { + return false; + } + break; + case 8: + if (!base.slice_from("al")) + { + return false; + } + break; + case 9: + if (!base.slice_from("ful")) + { + return false; + } + break; + case 10: + if (!base.slice_from("ous")) + { + return false; + } + break; + case 11: + if (!base.slice_from("ive")) + { + return false; + } + break; + case 12: + if (!base.slice_from("ble")) + { + return false; + } + break; + case 13: + if (!base.slice_from("og")) + { + return false; + } + break; + case 14: + if (!(base.eq_s_b("l"))) + { + return false; + } + if (!base.slice_from("og")) + { + return false; + } + break; + case 15: + if (!base.slice_from("less")) + { + return false; + } + break; + case 16: + if (!(base.in_grouping_b(g_valid_LI, 99, 116))) + { + return false; + } + if (!base.slice_del()) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_Step_3() { + var /** number */ among_var; + base.ket = base.cursor; + among_var = base.find_among_b(a_8); + if (among_var == 0) + { + return false; + } + base.bra = base.cursor; + if (!r_R1()) + { + return false; + } + switch (among_var) { + case 1: + if (!base.slice_from("tion")) + { + return false; + } + break; + case 2: + if (!base.slice_from("ate")) + { + return false; + } + break; + case 3: + if (!base.slice_from("al")) + { + return false; + } + break; + case 4: + if (!base.slice_from("ic")) + { + return false; + } + break; + case 5: + if (!base.slice_del()) + { + return false; + } + break; + case 6: + if (!r_R2()) + { + return false; + } + if (!base.slice_del()) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_Step_4() { + var /** number */ among_var; + base.ket = base.cursor; + among_var = base.find_among_b(a_9); + if (among_var == 0) + { + return false; + } + base.bra = base.cursor; + if (!r_R2()) + { + return false; + } + switch (among_var) { + case 1: + if (!base.slice_del()) + { + return false; + } + break; + case 2: + lab0: { + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab1: { + if (!(base.eq_s_b("s"))) + { + break lab1; + } + break lab0; + } + base.cursor = base.limit - v_1; + if (!(base.eq_s_b("t"))) + { + return false; + } + } + if (!base.slice_del()) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_Step_5() { + var /** number */ among_var; + base.ket = base.cursor; + among_var = base.find_among_b(a_10); + if (among_var == 0) + { + return false; + } + base.bra = base.cursor; + switch (among_var) { + case 1: + lab0: { + lab1: { + if (!r_R2()) + { + break lab1; + } + break lab0; + } + if (!r_R1()) + { + return false; + } + { + /** @const */ var /** number */ v_1 = base.limit - base.cursor; + lab2: { + if (!r_shortv()) + { + break lab2; + } + return false; + } + base.cursor = base.limit - v_1; + } + } + if (!base.slice_del()) + { + return false; + } + break; + case 2: + if (!r_R2()) + { + return false; + } + if (!(base.eq_s_b("l"))) + { + return false; + } + if (!base.slice_del()) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_exception1() { + var /** number */ among_var; + base.bra = base.cursor; + among_var = base.find_among(a_11); + if (among_var == 0) + { + return false; + } + base.ket = base.cursor; + if (base.cursor < base.limit) + { + return false; + } + switch (among_var) { + case 1: + if (!base.slice_from("sky")) + { + return false; + } + break; + case 2: + if (!base.slice_from("idl")) + { + return false; + } + break; + case 3: + if (!base.slice_from("gentl")) + { + return false; + } + break; + case 4: + if (!base.slice_from("ugli")) + { + return false; + } + break; + case 5: + if (!base.slice_from("earli")) + { + return false; + } + break; + case 6: + if (!base.slice_from("onli")) + { + return false; + } + break; + case 7: + if (!base.slice_from("singl")) + { + return false; + } + break; + } + return true; + }; + + /** @return {boolean} */ + function r_postlude() { + if (!B_Y_found) + { + return false; + } + while(true) + { + /** @const */ var /** number */ v_1 = base.cursor; + lab0: { + golab1: while(true) + { + /** @const */ var /** number */ v_2 = base.cursor; + lab2: { + base.bra = base.cursor; + if (!(base.eq_s("Y"))) + { + break lab2; + } + base.ket = base.cursor; + base.cursor = v_2; + break golab1; + } + base.cursor = v_2; + if (base.cursor >= base.limit) + { + break lab0; + } + base.cursor++; + } + if (!base.slice_from("y")) + { + return false; + } + continue; + } + base.cursor = v_1; + break; + } + return true; + }; + + this.stem = /** @return {boolean} */ function() { + lab0: { + /** @const */ var /** number */ v_1 = base.cursor; + lab1: { + if (!r_exception1()) + { + break lab1; + } + break lab0; + } + base.cursor = v_1; + lab2: { + { + /** @const */ var /** number */ v_2 = base.cursor; + lab3: { + { + /** @const */ var /** number */ c1 = base.cursor + 3; + if (c1 > base.limit) + { + break lab3; + } + base.cursor = c1; + } + break lab2; + } + base.cursor = v_2; + } + break lab0; + } + base.cursor = v_1; + r_prelude(); + r_mark_regions(); + base.limit_backward = base.cursor; base.cursor = base.limit; + /** @const */ var /** number */ v_3 = base.limit - base.cursor; + r_Step_1a(); + base.cursor = base.limit - v_3; + /** @const */ var /** number */ v_4 = base.limit - base.cursor; + r_Step_1b(); + base.cursor = base.limit - v_4; + /** @const */ var /** number */ v_5 = base.limit - base.cursor; + r_Step_1c(); + base.cursor = base.limit - v_5; + /** @const */ var /** number */ v_6 = base.limit - base.cursor; + r_Step_2(); + base.cursor = base.limit - v_6; + /** @const */ var /** number */ v_7 = base.limit - base.cursor; + r_Step_3(); + base.cursor = base.limit - v_7; + /** @const */ var /** number */ v_8 = base.limit - base.cursor; + r_Step_4(); + base.cursor = base.limit - v_8; + /** @const */ var /** number */ v_9 = base.limit - base.cursor; + r_Step_5(); + base.cursor = base.limit - v_9; + base.cursor = base.limit_backward; + /** @const */ var /** number */ v_10 = base.cursor; + r_postlude(); + base.cursor = v_10; + } + return true; + }; + + /**@return{string}*/ + this['stemWord'] = function(/**string*/word) { + base.setCurrent(word); + this.stem(); + return base.getCurrent(); + }; +}; diff --git a/docs/build/html/_static/file.png b/docs/build/html/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/docs/build/html/_static/file.png differ diff --git a/docs/build/html/_static/github-banner.svg b/docs/build/html/_static/github-banner.svg new file mode 100644 index 0000000..c47d9dc --- /dev/null +++ b/docs/build/html/_static/github-banner.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/docs/build/html/_static/language_data.js b/docs/build/html/_static/language_data.js new file mode 100644 index 0000000..5776786 --- /dev/null +++ b/docs/build/html/_static/language_data.js @@ -0,0 +1,13 @@ +/* + * This script contains the language-specific data used by searchtools.js, + * namely the set of stopwords, stemmer, scorer and splitter. + */ + +const stopwords = new Set(["a", "about", "above", "after", "again", "against", "all", "am", "an", "and", "any", "are", "aren't", "as", "at", "be", "because", "been", "before", "being", "below", "between", "both", "but", "by", "can't", "cannot", "could", "couldn't", "did", "didn't", "do", "does", "doesn't", "doing", "don't", "down", "during", "each", "few", "for", "from", "further", "had", "hadn't", "has", "hasn't", "have", "haven't", "having", "he", "he'd", "he'll", "he's", "her", "here", "here's", "hers", "herself", "him", "himself", "his", "how", "how's", "i", "i'd", "i'll", "i'm", "i've", "if", "in", "into", "is", "isn't", "it", "it's", "its", "itself", "let's", "me", "more", "most", "mustn't", "my", "myself", "no", "nor", "not", "of", "off", "on", "once", "only", "or", "other", "ought", "our", "ours", "ourselves", "out", "over", "own", "same", "shan't", "she", "she'd", "she'll", "she's", "should", "shouldn't", "so", "some", "such", "than", "that", "that's", "the", "their", "theirs", "them", "themselves", "then", "there", "there's", "these", "they", "they'd", "they'll", "they're", "they've", "this", "those", "through", "to", "too", "under", "until", "up", "very", "was", "wasn't", "we", "we'd", "we'll", "we're", "we've", "were", "weren't", "what", "what's", "when", "when's", "where", "where's", "which", "while", "who", "who's", "whom", "why", "why's", "with", "won't", "would", "wouldn't", "you", "you'd", "you'll", "you're", "you've", "your", "yours", "yourself", "yourselves"]); +window.stopwords = stopwords; // Export to global scope + + +/* Non-minified versions are copied as separate JavaScript files, if available */ +BaseStemmer=function(){this.current="",this.cursor=0,this.limit=0,this.limit_backward=0,this.bra=0,this.ket=0,this.setCurrent=function(t){this.current=t,this.cursor=0,this.limit=this.current.length,this.limit_backward=0,this.bra=this.cursor,this.ket=this.limit},this.getCurrent=function(){return this.current},this.copy_from=function(t){this.current=t.current,this.cursor=t.cursor,this.limit=t.limit,this.limit_backward=t.limit_backward,this.bra=t.bra,this.ket=t.ket},this.in_grouping=function(t,r,i){return!(this.cursor>=this.limit||i<(i=this.current.charCodeAt(this.cursor))||i>>3]&1<<(7&i))||(this.cursor++,0))},this.go_in_grouping=function(t,r,i){for(;this.cursor>>3]&1<<(7&s)))return!0;this.cursor++}return!1},this.in_grouping_b=function(t,r,i){return!(this.cursor<=this.limit_backward||i<(i=this.current.charCodeAt(this.cursor-1))||i>>3]&1<<(7&i))||(this.cursor--,0))},this.go_in_grouping_b=function(t,r,i){for(;this.cursor>this.limit_backward;){var s=this.current.charCodeAt(this.cursor-1);if(i>>3]&1<<(7&s)))return!0;this.cursor--}return!1},this.out_grouping=function(t,r,i){return!(this.cursor>=this.limit)&&(i<(i=this.current.charCodeAt(this.cursor))||i>>3]&1<<(7&i)))&&(this.cursor++,!0)},this.go_out_grouping=function(t,r,i){for(;this.cursor>>3]&1<<(7&s)))return!0;this.cursor++}return!1},this.out_grouping_b=function(t,r,i){return!(this.cursor<=this.limit_backward)&&(i<(i=this.current.charCodeAt(this.cursor-1))||i>>3]&1<<(7&i)))&&(this.cursor--,!0)},this.go_out_grouping_b=function(t,r,i){for(;this.cursor>this.limit_backward;){var s=this.current.charCodeAt(this.cursor-1);if(s<=i&&r<=s&&0!=(t[(s-=r)>>>3]&1<<(7&s)))return!0;this.cursor--}return!1},this.eq_s=function(t){return!(this.limit-this.cursor>>1),o=0,a=e=(l=t[r])[0].length){if(this.cursor=s+l[0].length,l.length<4)return l[2];var g=l[3](this);if(this.cursor=s+l[0].length,g)return l[2]}}while(0<=(r=l[1]));return 0},this.find_among_b=function(t){for(var r=0,i=t.length,s=this.cursor,h=this.limit_backward,e=0,n=0,c=!1;;){for(var u,o=r+(i-r>>1),a=0,l=e=(u=t[r])[0].length){if(this.cursor=s-u[0].length,u.length<4)return u[2];var g=u[3](this);if(this.cursor=s-u[0].length,g)return u[2]}}while(0<=(r=u[1]));return 0},this.replace_s=function(t,r,i){var s=i.length-(r-t);return this.current=this.current.slice(0,t)+i+this.current.slice(r),this.limit+=s,this.cursor>=r?this.cursor+=s:this.cursor>t&&(this.cursor=t),s},this.slice_check=function(){return!(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>this.current.length)},this.slice_from=function(t){var r=!1;return this.slice_check()&&(this.replace_s(this.bra,this.ket,t),r=!0),r},this.slice_del=function(){return this.slice_from("")},this.insert=function(t,r,i){r=this.replace_s(t,r,i);t<=this.bra&&(this.bra+=r),t<=this.ket&&(this.ket+=r)},this.slice_to=function(){var t="";return t=this.slice_check()?this.current.slice(this.bra,this.ket):t},this.assign_to=function(){return this.current.slice(0,this.limit)}}; +var EnglishStemmer=function(){var a=new BaseStemmer,c=[["arsen",-1,-1],["commun",-1,-1],["emerg",-1,-1],["gener",-1,-1],["later",-1,-1],["organ",-1,-1],["past",-1,-1],["univers",-1,-1]],o=[["'",-1,1],["'s'",0,1],["'s",-1,1]],u=[["ied",-1,2],["s",-1,3],["ies",1,2],["sses",1,1],["ss",1,-1],["us",1,-1]],t=[["succ",-1,1],["proc",-1,1],["exc",-1,1]],l=[["even",-1,2],["cann",-1,2],["inn",-1,2],["earr",-1,2],["herr",-1,2],["out",-1,2],["y",-1,1]],n=[["",-1,-1],["ed",0,2],["eed",1,1],["ing",0,3],["edly",0,2],["eedly",4,1],["ingly",0,2]],f=[["",-1,3],["bb",0,2],["dd",0,2],["ff",0,2],["gg",0,2],["bl",0,1],["mm",0,2],["nn",0,2],["pp",0,2],["rr",0,2],["at",0,1],["tt",0,2],["iz",0,1]],_=[["anci",-1,3],["enci",-1,2],["ogi",-1,14],["li",-1,16],["bli",3,12],["abli",4,4],["alli",3,8],["fulli",3,9],["lessli",3,15],["ousli",3,10],["entli",3,5],["aliti",-1,8],["biliti",-1,12],["iviti",-1,11],["tional",-1,1],["ational",14,7],["alism",-1,8],["ation",-1,7],["ization",17,6],["izer",-1,6],["ator",-1,7],["iveness",-1,11],["fulness",-1,9],["ousness",-1,10],["ogist",-1,13]],m=[["icate",-1,4],["ative",-1,6],["alize",-1,3],["iciti",-1,4],["ical",-1,4],["tional",-1,1],["ational",5,2],["ful",-1,5],["ness",-1,5]],b=[["ic",-1,1],["ance",-1,1],["ence",-1,1],["able",-1,1],["ible",-1,1],["ate",-1,1],["ive",-1,1],["ize",-1,1],["iti",-1,1],["al",-1,1],["ism",-1,1],["ion",-1,2],["er",-1,1],["ous",-1,1],["ant",-1,1],["ent",-1,1],["ment",15,1],["ement",16,1]],k=[["e",-1,1],["l",-1,2]],g=[["andes",-1,-1],["atlas",-1,-1],["bias",-1,-1],["cosmos",-1,-1],["early",-1,5],["gently",-1,3],["howe",-1,-1],["idly",-1,2],["news",-1,-1],["only",-1,6],["singly",-1,7],["skies",-1,1],["sky",-1,-1],["ugly",-1,4]],d=[17,64],v=[17,65,16,1],i=[1,17,65,208,1],w=[55,141,2],p=!1,y=0,h=0;function q(){var r=a.limit-a.cursor;return!!(a.out_grouping_b(i,89,121)&&a.in_grouping_b(v,97,121)&&a.out_grouping_b(v,97,121)||(a.cursor=a.limit-r,a.out_grouping_b(v,97,121)&&a.in_grouping_b(v,97,121)&&!(a.cursor>a.limit_backward))||(a.cursor=a.limit-r,a.eq_s_b("past")))}function z(){return h<=a.cursor}function Y(){return y<=a.cursor}this.stem=function(){var r=a.cursor;if(!(()=>{var r;if(a.bra=a.cursor,0!=(r=a.find_among(g))&&(a.ket=a.cursor,!(a.cursora.limit)a.cursor=i;else{a.cursor=e,a.cursor=r,(()=>{p=!1;var r=a.cursor;if(a.bra=a.cursor,!a.eq_s("'")||(a.ket=a.cursor,a.slice_del())){a.cursor=r;r=a.cursor;if(a.bra=a.cursor,a.eq_s("y")){if(a.ket=a.cursor,!a.slice_from("Y"))return;p=!0}a.cursor=r;for(r=a.cursor;;){var i=a.cursor;r:{for(;;){var e=a.cursor;if(a.in_grouping(v,97,121)&&(a.bra=a.cursor,a.eq_s("y"))){a.ket=a.cursor,a.cursor=e;break}if(a.cursor=e,a.cursor>=a.limit)break r;a.cursor++}if(!a.slice_from("Y"))return;p=!0;continue}a.cursor=i;break}a.cursor=r}})(),h=a.limit,y=a.limit;i=a.cursor;r:{var s=a.cursor;if(0==a.find_among(c)){if(a.cursor=s,!a.go_out_grouping(v,97,121))break r;if(a.cursor++,!a.go_in_grouping(v,97,121))break r;a.cursor++}h=a.cursor,a.go_out_grouping(v,97,121)&&(a.cursor++,a.go_in_grouping(v,97,121))&&(a.cursor++,y=a.cursor)}a.cursor=i,a.limit_backward=a.cursor,a.cursor=a.limit;var e=a.limit-a.cursor,r=((()=>{var r=a.limit-a.cursor;if(a.ket=a.cursor,0==a.find_among_b(o))a.cursor=a.limit-r;else if(a.bra=a.cursor,!a.slice_del())return;if(a.ket=a.cursor,0!=(r=a.find_among_b(u)))switch(a.bra=a.cursor,r){case 1:if(a.slice_from("ss"))break;return;case 2:r:{var i=a.limit-a.cursor,e=a.cursor-2;if(!(e{a.ket=a.cursor,o=a.find_among_b(n),a.bra=a.cursor;r:{var r=a.limit-a.cursor;i:{switch(o){case 1:var i=a.limit-a.cursor;e:{var e=a.limit-a.cursor;if(0==a.find_among_b(t)||a.cursor>a.limit_backward){if(a.cursor=a.limit-e,!z())break e;if(!a.slice_from("ee"))return}}a.cursor=a.limit-i;break;case 2:break i;case 3:if(0==(o=a.find_among_b(l)))break i;switch(o){case 1:var s=a.limit-a.cursor;if(!a.out_grouping_b(v,97,121))break i;if(a.cursor>a.limit_backward)break i;if(a.cursor=a.limit-s,a.bra=a.cursor,a.slice_from("ie"))break;return;case 2:if(a.cursor>a.limit_backward)break i}}break r}a.cursor=a.limit-r;var c=a.limit-a.cursor;if(!a.go_out_grouping_b(v,97,121))return;if(a.cursor--,a.cursor=a.limit-c,!a.slice_del())return;a.ket=a.cursor,a.bra=a.cursor;var o,c=a.limit-a.cursor;switch(o=a.find_among_b(f)){case 1:return a.slice_from("e");case 2:var u=a.limit-a.cursor;if(a.in_grouping_b(d,97,111)&&!(a.cursor>a.limit_backward))return;a.cursor=a.limit-u;break;case 3:return a.cursor!=h||(u=a.limit-a.cursor,q()&&(a.cursor=a.limit-u,a.slice_from("e")))}if(a.cursor=a.limit-c,a.ket=a.cursor,a.cursor<=a.limit_backward)return;if(a.cursor--,a.bra=a.cursor,!a.slice_del())return}})(),a.cursor=a.limit-r,a.limit-a.cursor),r=(a.ket=a.cursor,e=a.limit-a.cursor,(a.eq_s_b("y")||(a.cursor=a.limit-e,a.eq_s_b("Y")))&&(a.bra=a.cursor,a.out_grouping_b(v,97,121))&&a.cursor>a.limit_backward&&a.slice_from("i"),a.cursor=a.limit-i,a.limit-a.cursor),e=((()=>{var r;if(a.ket=a.cursor,0!=(r=a.find_among_b(_))&&(a.bra=a.cursor,z()))switch(r){case 1:if(a.slice_from("tion"))break;return;case 2:if(a.slice_from("ence"))break;return;case 3:if(a.slice_from("ance"))break;return;case 4:if(a.slice_from("able"))break;return;case 5:if(a.slice_from("ent"))break;return;case 6:if(a.slice_from("ize"))break;return;case 7:if(a.slice_from("ate"))break;return;case 8:if(a.slice_from("al"))break;return;case 9:if(a.slice_from("ful"))break;return;case 10:if(a.slice_from("ous"))break;return;case 11:if(a.slice_from("ive"))break;return;case 12:if(a.slice_from("ble"))break;return;case 13:if(a.slice_from("og"))break;return;case 14:if(!a.eq_s_b("l"))return;if(a.slice_from("og"))break;return;case 15:if(a.slice_from("less"))break;return;case 16:if(!a.in_grouping_b(w,99,116))return;if(a.slice_del())break}})(),a.cursor=a.limit-r,a.limit-a.cursor),i=((()=>{var r;if(a.ket=a.cursor,0!=(r=a.find_among_b(m))&&(a.bra=a.cursor,z()))switch(r){case 1:if(a.slice_from("tion"))break;return;case 2:if(a.slice_from("ate"))break;return;case 3:if(a.slice_from("al"))break;return;case 4:if(a.slice_from("ic"))break;return;case 5:if(a.slice_del())break;return;case 6:if(!Y())return;if(a.slice_del())break}})(),a.cursor=a.limit-e,a.limit-a.cursor),r=((()=>{var r;if(a.ket=a.cursor,0!=(r=a.find_among_b(b))&&(a.bra=a.cursor,Y()))switch(r){case 1:if(a.slice_del())break;return;case 2:var i=a.limit-a.cursor;if(!a.eq_s_b("s")&&(a.cursor=a.limit-i,!a.eq_s_b("t")))return;if(a.slice_del())break}})(),a.cursor=a.limit-i,a.limit-a.cursor),e=((()=>{var r;if(a.ket=a.cursor,0!=(r=a.find_among_b(k)))switch(a.bra=a.cursor,r){case 1:if(!Y()){if(!z())return;var i=a.limit-a.cursor;if(q())return;a.cursor=a.limit-i}if(a.slice_del())break;return;case 2:if(!Y())return;if(!a.eq_s_b("l"))return;if(a.slice_del())break}})(),a.cursor=a.limit-r,a.cursor=a.limit_backward,a.cursor);(()=>{if(p)for(;;){var r=a.cursor;r:{for(;;){var i=a.cursor;if(a.bra=a.cursor,a.eq_s("Y")){a.ket=a.cursor,a.cursor=i;break}if(a.cursor=i,a.cursor>=a.limit)break r;a.cursor++}if(a.slice_from("y"))continue;return}a.cursor=r;break}})(),a.cursor=e}}return!0},this.stemWord=function(r){return a.setCurrent(r),this.stem(),a.getCurrent()}}; +window.Stemmer = EnglishStemmer; diff --git a/docs/build/html/_static/minus.png b/docs/build/html/_static/minus.png new file mode 100644 index 0000000..d96755f Binary files /dev/null and b/docs/build/html/_static/minus.png differ diff --git a/docs/build/html/_static/plus.png b/docs/build/html/_static/plus.png new file mode 100644 index 0000000..7107cec Binary files /dev/null and b/docs/build/html/_static/plus.png differ diff --git a/docs/build/html/_static/pygments.css b/docs/build/html/_static/pygments.css new file mode 100644 index 0000000..9392ddc --- /dev/null +++ b/docs/build/html/_static/pygments.css @@ -0,0 +1,84 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #8F5902; font-style: italic } /* Comment */ +.highlight .err { color: #A40000; border: 1px solid #EF2929 } /* Error */ +.highlight .g { color: #000 } /* Generic */ +.highlight .k { color: #004461; font-weight: bold } /* Keyword */ +.highlight .l { color: #000 } /* Literal */ +.highlight .n { color: #000 } /* Name */ +.highlight .o { color: #582800 } /* Operator */ +.highlight .x { color: #000 } /* Other */ +.highlight .p { color: #000; font-weight: bold } /* Punctuation */ +.highlight .ch { color: #8F5902; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #8F5902; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #8F5902 } /* Comment.Preproc */ +.highlight .cpf { color: #8F5902; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #8F5902; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #8F5902; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #A40000 } /* Generic.Deleted */ +.highlight .ge { color: #000; font-style: italic } /* Generic.Emph */ +.highlight .ges { color: #000 } /* Generic.EmphStrong */ +.highlight .gr { color: #EF2929 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #888 } /* Generic.Output */ +.highlight .gp { color: #745334 } /* Generic.Prompt */ +.highlight .gs { color: #000; font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #A40000; font-weight: bold } /* Generic.Traceback */ +.highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */ +.highlight .ld { color: #000 } /* Literal.Date */ +.highlight .m { color: #900 } /* Literal.Number */ +.highlight .s { color: #4E9A06 } /* Literal.String */ +.highlight .na { color: #C4A000 } /* Name.Attribute */ +.highlight .nb { color: #004461 } /* Name.Builtin */ +.highlight .nc { color: #000 } /* Name.Class */ +.highlight .no { color: #000 } /* Name.Constant */ +.highlight .nd { color: #888 } /* Name.Decorator */ +.highlight .ni { color: #CE5C00 } /* Name.Entity */ +.highlight .ne { color: #C00; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #000 } /* Name.Function */ +.highlight .nl { color: #F57900 } /* Name.Label */ +.highlight .nn { color: #000 } /* Name.Namespace */ +.highlight .nx { color: #000 } /* Name.Other */ +.highlight .py { color: #000 } /* Name.Property */ +.highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #000 } /* Name.Variable */ +.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */ +.highlight .pm { color: #000; font-weight: bold } /* Punctuation.Marker */ +.highlight .w { color: #F8F8F8 } /* Text.Whitespace */ +.highlight .mb { color: #900 } /* Literal.Number.Bin */ +.highlight .mf { color: #900 } /* Literal.Number.Float */ +.highlight .mh { color: #900 } /* Literal.Number.Hex */ +.highlight .mi { color: #900 } /* Literal.Number.Integer */ +.highlight .mo { color: #900 } /* Literal.Number.Oct */ +.highlight .sa { color: #4E9A06 } /* Literal.String.Affix */ +.highlight .sb { color: #4E9A06 } /* Literal.String.Backtick */ +.highlight .sc { color: #4E9A06 } /* Literal.String.Char */ +.highlight .dl { color: #4E9A06 } /* Literal.String.Delimiter */ +.highlight .sd { color: #8F5902; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #4E9A06 } /* Literal.String.Double */ +.highlight .se { color: #4E9A06 } /* Literal.String.Escape */ +.highlight .sh { color: #4E9A06 } /* Literal.String.Heredoc */ +.highlight .si { color: #4E9A06 } /* Literal.String.Interpol */ +.highlight .sx { color: #4E9A06 } /* Literal.String.Other */ +.highlight .sr { color: #4E9A06 } /* Literal.String.Regex */ +.highlight .s1 { color: #4E9A06 } /* Literal.String.Single */ +.highlight .ss { color: #4E9A06 } /* Literal.String.Symbol */ +.highlight .bp { color: #3465A4 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #000 } /* Name.Function.Magic */ +.highlight .vc { color: #000 } /* Name.Variable.Class */ +.highlight .vg { color: #000 } /* Name.Variable.Global */ +.highlight .vi { color: #000 } /* Name.Variable.Instance */ +.highlight .vm { color: #000 } /* Name.Variable.Magic */ +.highlight .il { color: #900 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/build/html/_static/searchtools.js b/docs/build/html/_static/searchtools.js new file mode 100644 index 0000000..e29b1c7 --- /dev/null +++ b/docs/build/html/_static/searchtools.js @@ -0,0 +1,693 @@ +/* + * Sphinx JavaScript utilities for the full-text search. + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename, kind] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +// Global search result kind enum, used by themes to style search results. +// prettier-ignore +class SearchResultKind { + static get index() { return "index"; } + static get object() { return "object"; } + static get text() { return "text"; } + static get title() { return "title"; } +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _escapeHTML = (text) => { + return text + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">") + .replaceAll('"', """) + .replaceAll("'", "'"); +}; + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename, kind] = item; + + let listItem = document.createElement("li"); + // Add a class representing the item's type: + // can be used by a theme's CSS selector for styling + // See SearchResultKind for the class names. + listItem.classList.add(`kind-${kind}`); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = _escapeHTML(title); + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + ` (${_escapeHTML(descr)})`; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) + // SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js + highlightTerms.forEach((term) => + _highlightText(listItem, term, "highlighted"), + ); + } else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms, anchor), + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) + // SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js + highlightTerms.forEach((term) => + _highlightText(listItem, term, "highlighted"), + ); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.", + ); + else + Search.status.innerText = Documentation.ngettext( + "Search finished, found one page matching the search query.", + "Search finished, found ${resultCount} pages matching the search query.", + resultCount, + ).replace("${resultCount}", resultCount); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5, + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; +// Helper function used by query() to order search results. +// Each input is an array of [docname, title, anchor, descr, score, filename, kind]. +// Order the results by score (in opposite order of appearance, since the +// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. +const _orderResultsByScoreThenName = (a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => + query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter((term) => term); // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString, anchor) => { + const htmlElement = new DOMParser().parseFromString( + htmlString, + "text/html", + ); + for (const removalQuery of [".headerlink", "script", "style"]) { + htmlElement.querySelectorAll(removalQuery).forEach((el) => { + el.remove(); + }); + } + if (anchor) { + const anchorContent = htmlElement.querySelector( + `[role="main"] ${anchor}`, + ); + if (anchorContent) return anchorContent.textContent; + + console.warn( + `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.`, + ); + } + + // if anchor not specified or not found, fall back to main content + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent) return docContent.textContent; + + console.warn( + "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template.", + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.setAttribute("role", "list"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + _parseQuery: (query) => { + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords set is from language_data.js + if (stopwords.has(queryTermLower) || queryTerm.match(/^\d+$/)) return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { + // SPHINX_HIGHLIGHT_ENABLED is set in sphinx_highlight.js + localStorage.setItem( + "sphinx_highlight_terms", + [...highlightTerms].join(" "), + ); + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; + }, + + /** + * execute search (requires search index to be loaded) + */ + _performSearch: ( + query, + searchTerms, + excludedTerms, + highlightTerms, + objectTerms, + ) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // Collect multiple result groups to be sorted separately and then ordered. + // Each is an array of [docname, title, anchor, descr, score, filename, kind]. + const normalResults = []; + const nonMainIndexResults = []; + + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase().trim(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if ( + title.toLowerCase().trim().includes(queryLower) + && queryLower.length >= title.length / 2 + ) { + for (const [file, id] of foundTitles) { + const score = Math.round( + (Scorer.title * queryLower.length) / title.length, + ); + const boost = titles[file] === title ? 1 : 0; // add a boost for document titles + normalResults.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score + boost, + filenames[file], + SearchResultKind.title, + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && queryLower.length >= entry.length / 2) { + for (const [file, id, isMain] of foundEntries) { + const score = Math.round((100 * queryLower.length) / entry.length); + const result = [ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + SearchResultKind.index, + ]; + if (isMain) { + normalResults.push(result); + } else { + nonMainIndexResults.push(result); + } + } + } + } + + // lookup as object + objectTerms.forEach((term) => + normalResults.push(...Search.performObjectSearch(term, objectTerms)), + ); + + // lookup as search terms in fulltext + normalResults.push( + ...Search.performTermsSearch(searchTerms, excludedTerms), + ); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) { + normalResults.forEach((item) => (item[4] = Scorer.score(item))); + nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); + } + + // Sort each group of results by score and then alphabetically by name. + normalResults.sort(_orderResultsByScoreThenName); + nonMainIndexResults.sort(_orderResultsByScoreThenName); + + // Combine the result groups in (reverse) order. + // Non-main index entries are typically arbitrary cross-references, + // so display them after other results. + let results = [...nonMainIndexResults, ...normalResults]; + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result + .slice(0, 4) + .concat([result[5]]) + .map((v) => String(v)) + .join(","); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + return results.reverse(); + }, + + query: (query) => { + const [ + searchQuery, + searchTerms, + excludedTerms, + highlightTerms, + objectTerms, + ] = Search._parseQuery(query); + const results = Search._performSearch( + searchQuery, + searchTerms, + excludedTerms, + highlightTerms, + objectTerms, + ); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4]; + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + SearchResultKind.object, + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => objectSearchCallback(prefix, array)), + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + // find documents, if any, containing the query word in their text/title term indices + // use Object.hasOwnProperty to avoid mismatching against prototype properties + const arr = [ + { + files: terms.hasOwnProperty(word) ? terms[word] : undefined, + score: Scorer.term, + }, + { + files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : undefined, + score: Scorer.title, + }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + if (!terms.hasOwnProperty(word)) { + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + } + if (!titleTerms.hasOwnProperty(word)) { + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); + }); + } + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, new Map()); + const fileScores = scoreMap.get(file); + fileScores.set(word, record.score); + }); + }); + + // create the mapping + files.forEach((file) => { + if (!fileMap.has(file)) fileMap.set(file, [word]); + else if (fileMap.get(file).indexOf(word) === -1) + fileMap.get(file).push(word); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2, + ).length; + if ( + wordList.length !== searchTerms.size + && wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file + || titleTerms[term] === file + || (terms[term] || []).includes(file) + || (titleTerms[term] || []).includes(file), + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file).get(w))); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + SearchResultKind.text, + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords, anchor) => { + const text = Search.htmlToText(htmlText, anchor); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = + top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/docs/build/html/_static/sphinx_highlight.js b/docs/build/html/_static/sphinx_highlight.js new file mode 100644 index 0000000..a74e103 --- /dev/null +++ b/docs/build/html/_static/sphinx_highlight.js @@ -0,0 +1,159 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true; + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 + && !parent.classList.contains(className) + && !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore(span, parent.insertBefore(rest, node.nextSibling)); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect", + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target), + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms"); + // Update history only if '?highlight' is present; otherwise it + // clears text fragments (not set in window.location by the browser) + if (url.searchParams.has("highlight")) { + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + } + + // get individual terms from highlight string + const terms = highlight + .toLowerCase() + .split(/\s+/) + .filter((x) => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '", + ), + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms"); + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) + return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) + return; + if ( + DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + && event.key === "Escape" + ) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/docs/build/html/genindex.html b/docs/build/html/genindex.html new file mode 100644 index 0000000..6f9a523 --- /dev/null +++ b/docs/build/html/genindex.html @@ -0,0 +1,99 @@ + + + + + + + Index — concordantmodes 0.0.0 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + +

Index

+ +
+ +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/build/html/index.html b/docs/build/html/index.html new file mode 100644 index 0000000..8f9288b --- /dev/null +++ b/docs/build/html/index.html @@ -0,0 +1,106 @@ + + + + + + + + concordantmodes documentation — concordantmodes 0.0.0 documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

concordantmodes documentation¶

+

This program utilizes the Concordant Modes theoretical framework to compute +highly accurate Hessians with linear scaling. The CMA protocol is detailed in +the literature (J. Am. Chem. Soc., 2022, 144, 51, 23271–23274; +J. Chem. Theory Comput., 2024, 20, 24, 10886–10898; J Phys Chem A. 2026 Apr 13;**130**(16):3249–3260).

+
+
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/build/html/objects.inv b/docs/build/html/objects.inv new file mode 100644 index 0000000..d636a08 --- /dev/null +++ b/docs/build/html/objects.inv @@ -0,0 +1,7 @@ +# Sphinx inventory version 2 +# Project: concordantmodes +# Version: +# The remainder of this file is compressed using zlib. +xÚ…�A +ƒ@ E÷sŠ\À‚ÛÞ ‹‚à â$Ì3‰8êí«Œ¥ +…îÂÿïå–Qˆ_PŒî NдŽô-'xì§ûb¤~‡N„Wñ:Še%.°Kf1´QÅmÙ��im>E}òTZk§ö�Ö­U®vaœ}¼Š5«N_û»7Ht\� \ No newline at end of file diff --git a/docs/build/html/search.html b/docs/build/html/search.html new file mode 100644 index 0000000..f8ae75a --- /dev/null +++ b/docs/build/html/search.html @@ -0,0 +1,117 @@ + + + + + + + Search — concordantmodes 0.0.0 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Search

+ + + + +

+ Searching for multiple words only shows matches that contain + all words. +

+ + +
+ + + +
+ + +
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/build/html/searchindex.js b/docs/build/html/searchindex.js new file mode 100644 index 0000000..20375f6 --- /dev/null +++ b/docs/build/html/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"alltitles":{"concordantmodes documentation":[[0,null]]},"docnames":["index"],"envversion":{"sphinx":66,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["index.rst"],"indexentries":{},"objects":{},"objnames":{},"objtypes":{},"terms":{"A":0,"Am":0,"The":0,"This":0,"accur":0,"add":[],"apr":0,"chem":0,"cma":0,"comput":0,"concord":0,"content":[],"detail":0,"framework":0,"hessian":0,"high":0,"j":0,"linear":0,"literatur":0,"mode":0,"phys":0,"program":0,"protocol":0,"restructuredtext":[],"scale":0,"see":[],"soc":0,"syntax":[],"theoret":0,"theori":0,"use":[],"util":0},"titles":["concordantmodes documentation"],"titleterms":{"concordantmod":0,"document":0}}) \ No newline at end of file diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..747ffb7 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..9a8cff8 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,30 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'concordantmodes' +copyright = '2026, Mitchell Lahm' +author = 'M.E. Lahm and N. L. Kitzmiller' +release = '0.0.0' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'sphinx.ext.autodoc', +] + +templates_path = ['_templates'] +exclude_patterns = [] + +language = 'English' + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'alabaster' +html_static_path = ['_static'] diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..4634b0b --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,17 @@ +.. concordantmodes documentation master file, created by + sphinx-quickstart on Mon Sep 7 12:59:32 2026. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +concordantmodes documentation +============================= + +This program utilizes the Concordant Modes theoretical framework to compute +highly accurate Hessians with linear scaling. The CMA protocol is detailed in +the literature (*J. Am. Chem. Soc.*, 2022, **144**, 51, 23271–23274; +*J. Chem. Theory Comput.*, 2024, **20**, 24, 10886–10898; *J Phys Chem A.* 2026 Apr 13;**130**(16):3249–3260). + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + diff --git a/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/Nate_Test/main.py b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/Nate_Test/main.py new file mode 100644 index 0000000..7d19c41 --- /dev/null +++ b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/Nate_Test/main.py @@ -0,0 +1,24 @@ +from concordantmodes.options import Options + +options_kwargs = { + "cluster": "slurm", + "program_b": "molpro", + "program_a": "molpro", + "energy_regex_a": r"\(T\) total energy\s+(\-\d+\.\d+)", + "energy_regex_b": r"\(T\) total energy\s+(\-\d+\.\d+)", + "cart_insert_b": 9, + "cart_insert_a": 9, + "coords": "Delocalized", + "disp" : 0.005, + "covalent_radii": True, + "second_order" : True, + "success_regex_b": r"Molpro calculation terminated", + "success_regex_a": r"Molpro calculation terminated", +} +options_obj = Options(**options_kwargs) + +# 3. call Concordant Modes Program +from concordantmodes.cma import ConcordantModes + +CMA_obj = ConcordantModes(options_obj) +CMA_obj.run() diff --git a/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/Nate_Test/submit_CMA.sh b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/Nate_Test/submit_CMA.sh new file mode 100755 index 0000000..5356991 --- /dev/null +++ b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/Nate_Test/submit_CMA.sh @@ -0,0 +1,13 @@ +#!/bin/bash +#SBATCH --partition=batch +#SBATCH --job-name=benchmarker +#SBATCH --ntasks=1 +#SBATCH --time=2:00:00 +#SBATCH --mem=10G + +source /home/mel64643/.bashrc +source activate CMA +python -u main.py > CMA.out + + + diff --git a/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/Nate_Test/templateA.dat b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/Nate_Test/templateA.dat new file mode 100644 index 0000000..241bde2 --- /dev/null +++ b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/Nate_Test/templateA.dat @@ -0,0 +1,14 @@ +*** Optimization of min3 +memory,1000,m +gthresh,energy=1.0d-12,orbital=1.0d-10,oneint=1.0d-16,twoint=1.0d-16,optgrad=1.0d-6,compress=1.0d-13 +geomtyp=xyz + + bohr + geometry = { + 3 + arbitrary comment +} + +basis=cc-pvtz +{rhf;maxit,200} +{ccsd(t);maxit,200} diff --git a/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/Nate_Test/templateB.dat b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/Nate_Test/templateB.dat new file mode 100644 index 0000000..241bde2 --- /dev/null +++ b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/Nate_Test/templateB.dat @@ -0,0 +1,14 @@ +*** Optimization of min3 +memory,1000,m +gthresh,energy=1.0d-12,orbital=1.0d-10,oneint=1.0d-16,twoint=1.0d-16,optgrad=1.0d-6,compress=1.0d-13 +geomtyp=xyz + + bohr + geometry = { + 3 + arbitrary comment +} + +basis=cc-pvtz +{rhf;maxit,200} +{ccsd(t);maxit,200} diff --git a/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/Nate_Test/zmat b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/Nate_Test/zmat new file mode 100644 index 0000000..ab51ace --- /dev/null +++ b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/Nate_Test/zmat @@ -0,0 +1,6 @@ +cart begin +O -0.0000000148 0.1221791249 0.0000000000 +H 1.4195039554 -0.9695357769 0.0000000000 +H -1.4195037200 -0.9695355979 0.0000000000 +cart end + diff --git a/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/sameBA/main.py b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/sameBA/main.py new file mode 100644 index 0000000..82cdcd6 --- /dev/null +++ b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/sameBA/main.py @@ -0,0 +1,23 @@ +from concordantmodes.options import Options + +options_kwargs = { + "cluster": "slurm", + "program_b": "molpro", + "program_a": "molpro", + "energy_regex_a": r"\(T\) total energy\s+(\-\d+\.\d+)", + "energy_regex_b": r"\(T\) total energy\s+(\-\d+\.\d+)", + "cart_insert_b": 9, + "cart_insert_a": 9, + "coords": "Delocalized", + "disp" : 0.005, + "covalent_radii": True, + "success_regex_b": r"Molpro calculation terminated", + "success_regex_a": r"Molpro calculation terminated", +} +options_obj = Options(**options_kwargs) + +# 3. call Concordant Modes Program +from concordantmodes.cma import ConcordantModes + +CMA_obj = ConcordantModes(options_obj) +CMA_obj.run() diff --git a/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/sameBA/submit_CMA.sh b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/sameBA/submit_CMA.sh new file mode 100755 index 0000000..5356991 --- /dev/null +++ b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/sameBA/submit_CMA.sh @@ -0,0 +1,13 @@ +#!/bin/bash +#SBATCH --partition=batch +#SBATCH --job-name=benchmarker +#SBATCH --ntasks=1 +#SBATCH --time=2:00:00 +#SBATCH --mem=10G + +source /home/mel64643/.bashrc +source activate CMA +python -u main.py > CMA.out + + + diff --git a/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/sameBA/templateA.dat b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/sameBA/templateA.dat new file mode 100644 index 0000000..241bde2 --- /dev/null +++ b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/sameBA/templateA.dat @@ -0,0 +1,14 @@ +*** Optimization of min3 +memory,1000,m +gthresh,energy=1.0d-12,orbital=1.0d-10,oneint=1.0d-16,twoint=1.0d-16,optgrad=1.0d-6,compress=1.0d-13 +geomtyp=xyz + + bohr + geometry = { + 3 + arbitrary comment +} + +basis=cc-pvtz +{rhf;maxit,200} +{ccsd(t);maxit,200} diff --git a/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/sameBA/templateB.dat b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/sameBA/templateB.dat new file mode 100644 index 0000000..241bde2 --- /dev/null +++ b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/sameBA/templateB.dat @@ -0,0 +1,14 @@ +*** Optimization of min3 +memory,1000,m +gthresh,energy=1.0d-12,orbital=1.0d-10,oneint=1.0d-16,twoint=1.0d-16,optgrad=1.0d-6,compress=1.0d-13 +geomtyp=xyz + + bohr + geometry = { + 3 + arbitrary comment +} + +basis=cc-pvtz +{rhf;maxit,200} +{ccsd(t);maxit,200} diff --git a/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/sameBA/zmat b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/sameBA/zmat new file mode 100644 index 0000000..62f62a7 --- /dev/null +++ b/examples/1_H2O/molpro/CMA0A/No_Spec_Deloc/sameBA/zmat @@ -0,0 +1,6 @@ +cart begin + H -0.00000000 -1.42462540 0.99592408 + O 0.00000000 0.00000000 -0.12550454 + H -0.00000000 1.42462540 0.99592408 +cart end + diff --git a/examples/2_Methanol/molpro/CMA0A/No_Spec_Deloc/main.py b/examples/2_Methanol/molpro/CMA0A/No_Spec_Deloc/main.py index ac33884..690c5fa 100644 --- a/examples/2_Methanol/molpro/CMA0A/No_Spec_Deloc/main.py +++ b/examples/2_Methanol/molpro/CMA0A/No_Spec_Deloc/main.py @@ -11,9 +11,9 @@ "covalent_radii": True, "coords": "Delocalized", "topo_analysis": True, - # "calc_b" : False, + "calc_b" : False, # "calc_a" : False, - # "gen_disps_b" : False, + "gen_disps_b" : False, # "gen_disps_a" : False, "success_regex_b": r"Molpro calculation terminated", "success_regex_a": r"Molpro calculation terminated", diff --git a/examples/2_Methanol/molpro/CMA0A/gradients/main.py b/examples/2_Methanol/molpro/CMA0A/gradients/main.py new file mode 100644 index 0000000..e794135 --- /dev/null +++ b/examples/2_Methanol/molpro/CMA0A/gradients/main.py @@ -0,0 +1,29 @@ +from concordantmodes.options import Options + +options_kwargs = { + "cluster": "slurm", + "program_b": "molpro", + "program_a": "molpro", + "energy_regex_a": r"\(T\) total energy\s+(\-\d+\.\d+)", + "energy_regex_b": r"\(T\) total energy\s+(\-\d+\.\d+)", + "gradient_regex_b": [r"virial=",r"gradient"], + "cart_insert_b": 9, + "cart_insert_a": 9, + "covalent_radii": True, + "coords": "Delocalized", + "topo_analysis": True, + "deriv_level_b": 1, + "calc_b" : False, + # "calc_a" : False, + "gen_disps_b" : False, + # "gen_disps_a" : False, + "success_regex_b": r"virial", + "success_regex_a": r"Molpro calculation terminated", +} +options_obj = Options(**options_kwargs) + +# 3. call Concordant Modes Program +from concordantmodes.cma import ConcordantModes + +CMA_obj = ConcordantModes(options_obj) +CMA_obj.run() diff --git a/examples/2_Methanol/molpro/CMA0A/gradients/submit_CMA.sh b/examples/2_Methanol/molpro/CMA0A/gradients/submit_CMA.sh new file mode 100755 index 0000000..5356991 --- /dev/null +++ b/examples/2_Methanol/molpro/CMA0A/gradients/submit_CMA.sh @@ -0,0 +1,13 @@ +#!/bin/bash +#SBATCH --partition=batch +#SBATCH --job-name=benchmarker +#SBATCH --ntasks=1 +#SBATCH --time=2:00:00 +#SBATCH --mem=10G + +source /home/mel64643/.bashrc +source activate CMA +python -u main.py > CMA.out + + + diff --git a/examples/2_Methanol/molpro/CMA0A/gradients/templateA.dat b/examples/2_Methanol/molpro/CMA0A/gradients/templateA.dat new file mode 100644 index 0000000..94b0aa0 --- /dev/null +++ b/examples/2_Methanol/molpro/CMA0A/gradients/templateA.dat @@ -0,0 +1,14 @@ +*** Optimization of min3 +memory,1000,m +gthresh,energy=1.0d-12,orbital=1.0d-10,oneint=1.0d-16,twoint=1.0d-16,optgrad=1.0d-6,compress=1.0d-13 +geomtyp=xyz + + bohr + geometry = { + 6 + arbitrary comment +} + +basis=cc-pvtz +{rhf;maxit,200} +{ccsd(t);maxit,200} diff --git a/examples/2_Methanol/molpro/CMA0A/gradients/templateB.dat b/examples/2_Methanol/molpro/CMA0A/gradients/templateB.dat new file mode 100644 index 0000000..b330873 --- /dev/null +++ b/examples/2_Methanol/molpro/CMA0A/gradients/templateB.dat @@ -0,0 +1,15 @@ +*** Optimization of min3 +memory,1000,m +gthresh,energy=1.0d-12,orbital=1.0d-10,oneint=1.0d-16,twoint=1.0d-16,optgrad=1.0d-6,compress=1.0d-13 +geomtyp=xyz + + bohr + geometry = { + 6 + arbitrary comment +} + +basis=cc-pvdz +{rhf;maxit,200} +{ccsd(t);maxit,200} +forces diff --git a/examples/2_Methanol/molpro/CMA0A/gradients/zmat b/examples/2_Methanol/molpro/CMA0A/gradients/zmat new file mode 100644 index 0000000..c6e73cd --- /dev/null +++ b/examples/2_Methanol/molpro/CMA0A/gradients/zmat @@ -0,0 +1,9 @@ +cart begin +C 0.0000000000 -0.0242640817 1.3747756567 +O 0.0000000000 0.1206779016 -1.3063297814 +H 0.0000000000 1.9096437005 2.0775458546 +H 1.6819317189 -0.9793126991 2.1086885389 +H -1.6819317189 -0.9793126991 2.1086885389 +H 0.0000000000 -1.5775243580 -1.9414666861 +cart end + diff --git a/examples/2_Methanol/molpro/CMA0A/manual_sym/main.py b/examples/2_Methanol/molpro/CMA0A/manual_sym/main.py index daf02e6..fe60fe8 100644 --- a/examples/2_Methanol/molpro/CMA0A/manual_sym/main.py +++ b/examples/2_Methanol/molpro/CMA0A/manual_sym/main.py @@ -11,6 +11,11 @@ "energy_regex_b": r"\(T\) total energy\s+(\-\d+\.\d+)", "cart_insert_b": 9, "cart_insert_a": 9, + 'disp_a' : 0.02, + 'disp_b' : 0.02, + # "scaled_disp" : True, + # "calc_a" : False, + # "gen_disps_a" : False, # "calc_b" : False, # "gen_disps_b" : False, "man_proj": True, diff --git a/examples/2_Methanol/molpro/CMA0A/manual_sym/submit_CMA.sh b/examples/2_Methanol/molpro/CMA0A/manual_sym/submit_CMA.sh new file mode 100755 index 0000000..5356991 --- /dev/null +++ b/examples/2_Methanol/molpro/CMA0A/manual_sym/submit_CMA.sh @@ -0,0 +1,13 @@ +#!/bin/bash +#SBATCH --partition=batch +#SBATCH --job-name=benchmarker +#SBATCH --ntasks=1 +#SBATCH --time=2:00:00 +#SBATCH --mem=10G + +source /home/mel64643/.bashrc +source activate CMA +python -u main.py > CMA.out + + + diff --git a/examples/5_benzene/molpro/CMA0A/nospec_deloc/main.py b/examples/5_benzene/molpro/CMA0A/nospec_deloc/main.py new file mode 100644 index 0000000..c0da392 --- /dev/null +++ b/examples/5_benzene/molpro/CMA0A/nospec_deloc/main.py @@ -0,0 +1,29 @@ +from concordantmodes.options import Options + +options_kwargs = { + "cluster": "slurm", + "program_b": "molpro", + "program_a": "molpro", + "energy_regex_a": r"\(T\) total energy\s+(\-\d+\.\d+)", + "energy_regex_b": r"!RHF STATE 1.1 Energy\s*\s+(\-\d+\.\d+)", + "cart_insert_b": 7, + "cart_insert_a": 7, + "covalent_radii": True, + "coords": "Delocalized", + "topo_analysis": True, + "calc_b" : False, + # "calc_a" : False, + "gen_disps_b" : False, + # "gen_disps_a" : False, + "success_regex_a": r"Molpro calculation terminated", + "success_regex_b": r"Molpro calculation terminated", + # "symmetry": True, + # "autosalcs": True, +} +options_obj = Options(**options_kwargs) + +# 3. call Concordant Modes Program +from concordantmodes.cma import ConcordantModes + +CMA_obj = ConcordantModes(options_obj) +CMA_obj.run() diff --git a/examples/5_benzene/molpro/CMA0A/nospec_deloc/templateA.dat b/examples/5_benzene/molpro/CMA0A/nospec_deloc/templateA.dat new file mode 100644 index 0000000..84f082f --- /dev/null +++ b/examples/5_benzene/molpro/CMA0A/nospec_deloc/templateA.dat @@ -0,0 +1,12 @@ +*** Optimization of min3 +memory,1000,m +gthresh,energy=1.0d-12,orbital=1.0d-10,oneint=1.0d-16,twoint=1.0d-16,optgrad=1.0d-6,compress=1.0d-13 +geomtyp=xyz + + bohr + geometry = { +} + +basis=cc-pvtz +{rhf;maxit,200} +{ccsd(t);maxit,200} diff --git a/examples/5_benzene/molpro/CMA0A/nospec_deloc/templateB.dat b/examples/5_benzene/molpro/CMA0A/nospec_deloc/templateB.dat new file mode 100644 index 0000000..60d0b1c --- /dev/null +++ b/examples/5_benzene/molpro/CMA0A/nospec_deloc/templateB.dat @@ -0,0 +1,11 @@ +*** Optimization of min3 +memory,1000,m +gthresh,energy=1.0d-12,orbital=1.0d-10,oneint=1.0d-16,twoint=1.0d-16,optgrad=1.0d-6,compress=1.0d-13 +geomtyp=xyz + + bohr + geometry = { +} + +basis=cc-pvdz +{rhf;maxit,200} diff --git a/examples/5_benzene/molpro/CMA0A/nospec_deloc/zmat b/examples/5_benzene/molpro/CMA0A/nospec_deloc/zmat new file mode 100644 index 0000000..00ed2f0 --- /dev/null +++ b/examples/5_benzene/molpro/CMA0A/nospec_deloc/zmat @@ -0,0 +1,59 @@ +ZMAT begin +1 2 +2 3 +3 4 +4 5 +5 6 +6 1 + +1 7 +2 8 +3 9 +4 10 +5 11 +6 12 + +6 1 2 +1 2 3 +2 3 4 +3 4 5 +4 5 6 +5 6 1 + +7 1 2 +8 2 3 +9 3 4 + +10 4 5 +11 5 6 +12 6 1 + +1 2 3 4 T +2 3 4 5 T +3 4 5 6 T +4 5 6 1 T +5 6 1 2 T +6 1 2 3 T + +7 1 2 6 O +8 2 3 1 O +9 3 4 2 O +10 4 5 3 O +11 5 6 4 O +12 6 1 5 O +ZMAT end + +cart begin + C 0.00000000 2.64096559 -0.00000000 + C -2.28714329 1.32048279 -0.00000000 + C -2.28714329 -1.32048279 0.00000000 + C -0.00000000 -2.64096559 0.00000000 + C 2.28714329 -1.32048279 0.00000000 + C 2.28714329 1.32048279 -0.00000000 + H 0.00000000 4.68777554 -0.00000000 + H -4.05973271 2.34388777 -0.00000000 + H -4.05973271 -2.34388777 0.00000000 + H -0.00000000 -4.68777554 0.00000000 + H 4.05973271 -2.34388777 0.00000000 + H 4.05973271 2.34388777 -0.00000000 +cart end diff --git a/pyproject.toml b/pyproject.toml index e316b12..45bba00 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,11 +27,24 @@ tests = [ "pytest-pep8", ] +docs = [ + "numpydoc", + "pydata-sphinx-theme", + "sphinx<8", + "sphinx-automodapi", + "sphinx-design", + "sphinxcontrib-napoleon", + "sphinx-autobuild", + "autodoc_pydantic>=1.9.1", + "pint", +] + [project.urls] Homepage = "https://github.com/CCQC/ConcordantModes" [tool.hatch.build.hooks.vcs] version-file = "_version.py" +Documentation = "https://optking.readthedocs.io/en/latest/" [tool.hatch.build.targets.sdist] include = [ @@ -39,4 +52,18 @@ include = [ ] [tool.hatch.version] source = "vcs" +docs = [ + "numpydoc", + "pydata-sphinx-theme", + "sphinx<8", + "sphinx-automodapi", + "sphinx-design", + "sphinxcontrib-napoleon", + "sphinx-autobuild", + "autodoc_pydantic>=1.9.1", + "pint", +] +[tool.hatch.envs.docs.scripts] +build = "sphinx-build -M html docs/source docs/build" +serve = "sphinx-autobuild --port=0 --open-browser docs/source docs/build/html/ &"