コード例 #1
0
ファイル: sterimol.py プロジェクト: sailfish009/morfeus
    def __init__(
        self,
        elements: Union[Iterable[int], Iterable[str]],
        coordinates: ArrayLike2D,
        dummy_index: int,
        attached_index: Union[int, Iterable[int]],
        radii: Optional[ArrayLike1D] = None,
        radii_type: str = "crc",
        n_rot_vectors: int = 3600,
        excluded_atoms: Optional[Sequence[int]] = None,
        calculate: bool = True,
    ) -> None:
        # Convert elements to atomic numbers if the are symbols
        elements = convert_elements(elements, output="numbers")
        coordinates = np.array(coordinates)

        if excluded_atoms is None:
            excluded_atoms = []
        excluded_atoms = np.array(excluded_atoms)

        # Get radii if they are not supplied
        if radii is None:
            radii = get_radii(elements, radii_type=radii_type)
        radii = np.array(radii)

        # Add dummy atom if multiple attached indices are given
        if isinstance(attached_index, Iterable):
            attached_dummy_coordinates = np.mean(
                [coordinates[i - 1] for i in attached_index], axis=0)
            attached_dummy_coordinates = cast(np.ndarray,
                                              attached_dummy_coordinates)
            coordinates = np.vstack([coordinates, attached_dummy_coordinates])
            elements.append(0)
            radii = np.concatenate([radii, [0.0]])
            attached_index = len(elements)

        # Set up coordinate array
        all_coordinates = coordinates
        all_radii = radii

        # Translate coordinates so origin is at atom 2
        origin = all_coordinates[attached_index - 1]
        all_coordinates -= origin

        # Get vector pointing from atom 2 to atom 1
        vector_2_to_1 = (all_coordinates[attached_index - 1] -
                         all_coordinates[dummy_index - 1])
        bond_length = np.linalg.norm(vector_2_to_1)
        vector_2_to_1 = vector_2_to_1 / np.linalg.norm(vector_2_to_1)

        # Get rotation quaternion that overlays vector with x-axis
        x_axis = np.array([[1.0, 0.0, 0.0]])
        R = kabsch_rotation_matrix(vector_2_to_1.reshape(1, -1),
                                   x_axis,
                                   center=False)
        all_coordinates = (R @ all_coordinates.T).T
        self._rotation_matrix = R

        # Get list of atoms as Atom objects
        atoms = []
        for i, (element, radius,
                coord) in enumerate(zip(elements, all_radii, all_coordinates),
                                    start=1):
            atom = Atom(element, coord, radius, i)
            atoms.append(atom)
            if i == dummy_index:
                dummy_atom = atom
            if i == attached_index:
                attached_atom = atom

        # Set up attributes
        self._atoms = atoms
        self._excluded_atoms = set(excluded_atoms)
        self._origin = origin

        self._dummy_atom = dummy_atom
        self._attached_atom = attached_atom

        self.bond_length = bond_length

        self._n_rot_vectors = n_rot_vectors

        if calculate:
            self.calculate()
コード例 #2
0
    def __init__(
        self,
        elements: Iterable[int] | Iterable[str],
        coordinates: ArrayLike2D,
        metal_index: int,
        excluded_atoms: Sequence[int] | None = None,
        radii: ArrayLike1D | None = None,
        include_hs: bool = False,
        radius: float = 3.5,
        radii_type: str = "bondi",
        radii_scale: float = 1.17,
        density: float = 0.001,
        z_axis_atoms: Sequence[int] | None = None,
        xz_plane_atoms: Sequence[int] | None = None,
    ) -> None:
        # Get center and and reortient coordinate system
        coordinates: Array2DFloat = np.array(coordinates)
        center = coordinates[metal_index - 1]
        coordinates -= center

        if excluded_atoms is None:
            excluded_atoms = []
        excluded_atoms = set(excluded_atoms)

        if metal_index not in excluded_atoms:
            excluded_atoms.add(metal_index)

        if z_axis_atoms is not None and xz_plane_atoms is not None:
            z_axis_coordinates = coordinates[np.array(z_axis_atoms) - 1]
            z_point = np.mean(z_axis_coordinates, axis=0)

            xz_plane_coordinates = coordinates[np.array(xz_plane_atoms) - 1]
            xz_point = np.mean(xz_plane_coordinates, axis=0)

            v_1 = z_point - center
            v_2 = xz_point - center
            # TODO: Remove type ignores when https://github.com/numpy/numpy/pull/21216 is released
            v_3: Array1DFloat = np.cross(v_2, v_1)
            real: Array2DFloat = np.vstack([v_1, v_3])  # type: ignore
            real /= np.linalg.norm(real, axis=1).reshape(-1, 1)
            ref_1 = np.array([0.0, 0.0, -1.0])
            ref_2 = np.array([0.0, 1.0, 0.0])
            ref = np.vstack([ref_1, ref_2])
            R = kabsch_rotation_matrix(real, ref, center=False)
            coordinates = (R @ coordinates.T).T
        elif z_axis_atoms is not None:
            z_axis_coordinates = coordinates[np.array(z_axis_atoms) - 1]
            z_point = np.mean(z_axis_coordinates, axis=0)
            v_1 = z_point - center
            v_1 = v_1 / np.linalg.norm(v_1)
            coordinates = rotate_coordinates(coordinates, v_1,
                                             np.array([0, 0, -1]))

        self._z_axis_atoms = z_axis_atoms
        self._xz_plane_atoms = xz_plane_atoms

        # Save density and coordinates for steric map plotting.
        self._density = density
        self._all_coordinates = coordinates

        # Converting element ids to atomic numbers if the are symbols
        elements = convert_elements(elements, output="numbers")

        # Getting radii if they are not supplied
        if radii is None:
            radii = get_radii(elements,
                              radii_type=radii_type,
                              scale=radii_scale)
        radii: Array1DFloat = np.array(radii)

        # Get list of atoms as Atom objects
        atoms = []
        for i, (element,
                radius_, coord) in enumerate(zip(elements, radii, coordinates),
                                             start=1):
            if i in excluded_atoms:
                continue
            elif (not include_hs) and element == 1:
                continue
            else:
                atom = Atom(element, coord, radius_, i)
                atoms.append(atom)

        # Set variables for outside access and function access.
        self._atoms = atoms
        self._excluded_atoms = set(excluded_atoms)

        # Compute buried volume
        self._compute_buried_volume(center=center,
                                    radius=radius,
                                    density=density)
コード例 #3
0
ファイル: local_force.py プロジェクト: sailfish009/morfeus
    def _parse_gaussian_log(self, file: Union[str,
                                              PathLike]) -> None:  # noqa: C901
        # Read the log file
        with open(file) as f:
            lines = f.readlines()

        # Set up read flags
        read_b_atoms = False
        read_b_vectors = False
        read_fc_matrix = False
        read_ifc_matrix = False
        read_input_orientation = False
        read_standard_orientation = False
        read_internal = False
        read_internal_modes = False
        read_hp_modes = False
        read_masses = False

        # Set up containers for reading data
        B_atom_map: Dict[int, List[int]] = {}
        B_vectors: Dict[int, List[float]] = {}
        normal_modes: List[List[List[float]]] = []
        internal_modes: List[List[float]] = []
        force_constants: List[float] = []
        masses: List[float] = []
        fc_matrix = np.array([])
        ifc_matrix = np.array([])
        input_coordinates: List[float] = []
        standard_coordinates: List[float] = []
        n_imag: int = 0
        n_atoms: int = 0
        internal_indices: Dict[FrozenSet[int], int] = {}
        atomic_numbers: List[int] = []
        coordinates: List[List[float]] = []

        # Parse through log file content
        counter = 0
        internal_names: List[str] = []
        internal_vector: List[float] = []

        values: Any
        value: Any
        for line in lines:
            # Read internal coordinate definitions
            if read_internal:
                if counter > 1:
                    if (" --------------------------------------------------------------------------------"  # noqa: B950
                            in line):
                        read_internal = False
                        n_internals = len(internal_indices.items())
                    else:
                        split_line = line.strip().split()
                        name = split_line[1]
                        internal_names.append(name)
                        atoms = split_line[2][2:].replace(")", "").split(",")
                        atoms = [int(atom) for atom in atoms]
                        internal_indices[frozenset(atoms)] = counter - 2
                counter += 1
            # Read Cartesian force constant matrix
            elif read_fc_matrix:
                if " FormGI is forming" in line:
                    read_fc_matrix = False
                    fc_matrix = np.triu(fc_matrix.T, 1) + fc_matrix
                elif "          " in line:
                    column_indices = [
                        int(value) for value in line.strip().split()
                    ]
                elif "D" in line:
                    split_line = line.strip().split()
                    row_index = int(split_line[0]) - 1
                    values = [
                        float(value.replace("D", "E"))
                        for value in split_line[1:]
                    ]
                    for i, value in enumerate(values):
                        column_index = column_indices[i] - 1
                        fc_matrix[row_index, column_index] = value
            # Read internal force constant matrix
            elif read_ifc_matrix:
                if "Leave Link  716" in line:
                    read_ifc_matrix = False
                    ifc_matrix = np.triu(ifc_matrix.T, 1) + ifc_matrix
                elif "          " in line:
                    column_indices = [
                        int(value) for value in line.strip().split()
                    ]
                elif "D" in line:
                    split_line = line.strip().split()
                    row_index = int(split_line[0]) - 1
                    values = [
                        float(value.replace("D", "E"))
                        for value in split_line[1:]
                    ]
                    for i, value in enumerate(values):
                        column_index = column_indices[i] - 1
                        ifc_matrix[row_index, column_index] = value
            # Read atoms for creation of B matrix
            elif read_b_atoms:
                if " B Matrix in FormBX:" in line:
                    read_b_atoms = False
                else:
                    if counter == 0:
                        atoms = [int(value) for value in line.strip().split()]
                        for atom in atoms:
                            B_atom_map[atom] = []
                    if counter > 0 and counter < 5:
                        values = [
                            int(value) for value in line.strip().split()[1:]
                        ]
                        for atom, value in zip(atoms, values):
                            B_atom_map[atom].append(value)
                    counter += 1
                    if counter == 5:
                        counter = 0
            # Read values of B matrix
            elif read_b_vectors:
                if (" IB Matrix in Red2BG:" in line or "Iteration" in line
                        or " G Matrix:" in line):
                    read_b_vectors = False
                else:
                    if counter == 0:
                        atoms = [int(value) for value in line.strip().split()]
                        for atom in atoms:
                            B_vectors[atom] = []
                    if counter > 0 and counter < 13:
                        values = [
                            float(value.replace("D", "E"))
                            for value in line.strip().split()[1:]
                        ]
                        for atom, value in zip(atoms, values):
                            B_vectors[atom].append(value)
                    counter += 1
                    if counter == 13:
                        counter = 0
            # Read atomic coordinates in input orientation
            elif read_input_orientation:
                if counter > 3:
                    if ("---------------------------------------------------------------------"  # noqa: B950
                            in line):
                        read_input_orientation = False
                    else:
                        strip_line = line.strip().split()
                        values = [float(value) for value in strip_line[3:]]
                        input_coordinates.append(values)
                        atomic_numbers.append(int(strip_line[1]))
                counter += 1
            # Read atomic coordinates in standard orientation
            elif read_standard_orientation:
                if counter > 3:
                    if ("---------------------------------------------------------------------"  # noqa: B950
                            in line):
                        read_standard_orientation = False
                    else:
                        strip_line = line.strip().split()
                        values = [float(value) for value in strip_line[3:]]
                        standard_coordinates.append(values)
                counter += 1
            # Read decomposition of normal modes in internal coordinates
            elif read_internal_modes:
                if counter > 3:
                    if ("--------------------------------------------------------------------------------"  # noqa: B950
                            in line):
                        read_internal_modes = False
                        internal_modes.append(internal_vector)
                    else:
                        value = float(line.strip().split()[3])
                        internal_vector.append(value)
                counter += 1
            # Read high-precision normal modes
            elif read_hp_modes:
                if counter < n_atoms * 3:
                    strip_line = line.strip().split()
                    values = [float(value) for value in strip_line[3:]]
                    coordinates.append(values)
                if counter == n_atoms * 3:
                    normal_modes.append(coordinates)
                    read_hp_modes = False
                counter += 1
            # Read atomic masses
            elif read_masses:
                if "Molecular mass: " in line:
                    read_masses = False
                elif "and mass" in line:
                    masses.append(float(line.strip().split()[8]))
            # Read number of atoms
            if " NAtoms=" in line and not n_atoms:
                n_atoms = int(line.strip().split()[1])
            # Read normal mode force constants
            elif "Frc consts" in line:
                split_line = line.strip().split()
                values = [float(value) for value in split_line[3:]]
                force_constants.extend(values)
            # Read number of imaginary frequencies
            elif "imaginary frequencies (negative Signs)" in line:
                n_imag = int(line.strip().split()[1])
            # Detect when to read data
            elif ("Name  Definition              Value          Derivative Info."
                  in line):
                read_internal = True
                counter = 1
                internal_names = []
            elif "- Thermochemistry -" in line:
                read_masses = True
            elif " IB Matrix in FormBX:" in line:
                read_b_atoms = True
                counter = 0
            elif " B Matrix in FormBX:" in line:
                read_b_vectors = True
                counter = 0
            elif " Force constants in Cartesian coordinates: " in line:
                read_fc_matrix = True
                fc_matrix = np.zeros((3 * n_atoms, 3 * n_atoms))
                counter = 0
            elif " Force constants in internal coordinates: " in line:
                read_ifc_matrix = True
                ifc_matrix = np.zeros((n_internals, n_internals))
                counter = 0
            elif "Input orientation: " in line:
                read_input_orientation = True
                counter = 0
            elif "Standard orientation: " in line:
                read_standard_orientation = True
                counter = 0
            elif "Normal Mode" in line:
                read_internal_modes = True
                counter = 1
                internal_vector = []
            elif " Coord Atom Element:" in line:
                read_hp_modes = True
                coordinates = []
                counter = 0

        # Process internal coordinates
        if len(internal_indices) > 0:
            for name, indices in zip(internal_names, internal_indices):
                if name[0] == "R" and len(indices) == 2:
                    self._internal_coordinates.add_internal_coordinate(
                        list(indices))
                if name[0] == "A" and len(indices) == 3:
                    self._internal_coordinates.add_internal_coordinate(
                        list(indices))
                if name[0] == "D" and len(indices) == 4:
                    self._internal_coordinates.add_internal_coordinate(
                        list(indices))

        # Construct the B matrix from atoms and vectors
        if B_vectors:
            n_cartesian = n_atoms * 3
            B = np.zeros((n_internals, n_cartesian))
            for i in range(n_internals):
                for j, atom in enumerate(B_atom_map[i + 1]):
                    if atom:
                        B[i][(atom - 1) * 3:(atom - 1) * 3 +
                             3] = B_vectors[i + 1][j * 3:j * 3 + 3]
            B_inv = np.linalg.pinv(B)
            # Detect whether the internal coordinate system is redundant
            if B.shape[0] == len(force_constants):
                self._redundant = False
            else:
                self._redundant = True
        else:
            B = np.array([])
            B_inv = np.array([])

        # Detect whether the input coordinates have been rotated. If so, rotate
        # B matrix and its inverse.
        input_coordinates = np.array(input_coordinates).reshape(-1, 3)
        standard_coordinates = np.array(standard_coordinates).reshape(-1, 3)

        if (not np.array_equal(input_coordinates, standard_coordinates)
                and standard_coordinates.size > 0):
            rotation_i_to_s = kabsch_rotation_matrix(input_coordinates,
                                                     standard_coordinates)
            if len(B) > 0:
                B = (rotation_i_to_s @ B.reshape(-1, 3).T).T.reshape(
                    n_internals, -1)
                B_inv = np.linalg.pinv(B)

        # Set up attributes
        self._B = B
        self._B_inv = B_inv
        self._fc_matrix = fc_matrix
        if fc_matrix.size == 0 and ifc_matrix.size > 0:
            self._fc_matrix = B.T @ ifc_matrix @ B
        self._ifc_matrix = ifc_matrix
        self._force_constants = np.array(force_constants)
        if len(normal_modes) > 0:
            self._normal_modes = np.hstack(normal_modes).T
        else:
            self._normal_modes = np.array([])
        if len(internal_modes) > 0:
            self._D = np.vstack(internal_modes).T
        self.n_imag = n_imag
        self._masses = np.array(masses)
        self._input_coordinates = input_coordinates
        self._standard_coordinates = standard_coordinates
        self._coordinates = input_coordinates
        self._elements = convert_elements(atomic_numbers, output="numbers")