Ejemplo n.º 1
0
    def get_vortex_influence(self, points):
        """Determines the velocity vector induced by this edge at arbitrary
        points, assuming a horseshoe vortex is shed from this edge.

        Parameters
        ----------
        points : ndarray
            An array of points where the first index is the point index and 
            the second index is the coordinate.

        Returns
        -------
        ndarray
            The velocity vector induced at each point.
        """

        # Determine displacement vectors
        r0 = points - self.vertices[0, :]
        r1 = points - self.vertices[1, :]

        # Determine displacement vector magnitudes
        r0_mag = vec_norm(r0)
        r1_mag = vec_norm(r1)

        # Calculate influence of bound segment
        with np.errstate(divide='ignore'):
            d = (np.pi * r0_mag * r1_mag *
                 (r0_mag * r1_mag + vec_inner(r0, r1)))
            n = 0.25 * ((r0_mag + r1_mag) / d)
            n = np.nan_to_num(n, copy=False)
            return n[:, np.newaxis] * vec_cross(r0, r1)
Ejemplo n.º 2
0
    def get_ring_influence(self, points):
        """Determines the velocity vector induced by this panel at arbitrary
        points, assuming a vortex ring (0th order) model and a unit positive
        vortex strength.

        Parameters
        ----------
        points : ndarray
            An array of points where the first index is the point index and 
            the second index is the coordinate.

        Returns
        -------
        ndarray
            The velocity vector induced at each point.
        """

        # Determine displacement vectors
        r = points[np.newaxis, :, :] - self.vertices[:, np.newaxis, :]
        r_mag = vec_norm(r)

        # Calculate influence
        v = np.zeros_like(points)
        with np.errstate(divide='ignore'):
            for i in range(self.N):
                d = (r_mag[i - 1] * r_mag[i] *
                     (r_mag[i - 1] * r_mag[i] + vec_inner(r[i - 1], r[i])))
                n = (r_mag[i - 1] + r_mag[i]) / d
                n = np.nan_to_num(n, copy=False)
                v += vec_cross(r[i - 1], r[i]) * n[:, np.newaxis]

        return 0.25 / np.pi * v
Ejemplo n.º 3
0
    def _get_filament_influences(self, points):
        # Determines the unit vortex influence from the wake filaments on the given points

        # Determine displacement vectors: first index is point, second is filament, third is segment, fourth is vector component
        if self._end_infinite:
            r0 = points[:, np.newaxis, np.newaxis, :] - self._vertices[
                np.newaxis, :, :
                -2, :]  # Don't add the last segment at this point
            r1 = points[:, np.newaxis,
                        np.newaxis, :] - self._vertices[np.newaxis, :, 1:-1, :]
        else:
            r0 = points[:, np.newaxis,
                        np.newaxis, :] - self._vertices[np.newaxis, :, :-1, :]
            r1 = points[:, np.newaxis,
                        np.newaxis, :] - self._vertices[np.newaxis, :, 1:, :]

        # Determine displacement vector magnitudes
        r0_mag = vec_norm(r0)
        r1_mag = vec_norm(r1)

        # Calculate influence of each segment
        inf = np.sum(
            ((r0_mag + r1_mag) /
             (r0_mag * r1_mag *
              (r0_mag * r1_mag + vec_inner(r0, r1))))[:, :, :, np.newaxis] *
            vec_cross(r0, r1),
            axis=2)

        # Add influence of last segment, if needed
        if self._end_infinite:

            # Determine displacement vector magnitudes
            r = r1[:, :, -1, :]
            r_mag = vec_norm(r)
            u = self._vertices[:, -1, :] - self._vertices[:, -2, :]
            u /= vec_norm(u)[:, np.newaxis]

            # Calculate influence
            inf += vec_cross(u[np.newaxis, :, :], r) / (
                r_mag *
                (r_mag - vec_inner(u[np.newaxis, :, :], r)))[:, :, np.newaxis]

        return 0.25 / np.pi * np.nan_to_num(inf)
Ejemplo n.º 4
0
    def update(self, velocity_from_body, mu, v_inf, omega, verbose):
        """Updates the shape of the wake based on solved flow results.

        Parameters
        ----------
        velocity_from_body : callable
            Function which will return the velocity induced by the body at a given set of points.

        mu : ndarray
            Vector of doublet strengths.

        v_inf : ndarray
            Freestream velocity vector.

        omega : ndarray
            Angular rate vector.

        verbose : bool
        """

        if verbose:
            print()
            prog = OneLineProgress(4, msg="    Updating wake shape")

        # Reorder vertices for computation
        points = self._vertices[:, 1:, :].reshape(
            (self.N * (self.N_segments), 3))

        # Get velocity from body and rotation
        v_ind = velocity_from_body(points) - vec_cross(omega, points)
        if verbose: prog.display()

        # Get velocity from wake elements
        v_ind += self._get_velocity_from_filaments_and_edges(points, mu)
        if verbose: prog.display()

        # Calculate time-stepping parameter
        U = norm(v_inf)
        u = v_inf / U
        dl = self._vertices[:, 1:, :] - self._vertices[:, 0, :][:,
                                                                np.newaxis, :]
        d = vec_inner(dl, u[np.newaxis, :])
        dt = self._K * d / U
        if verbose: prog.display()

        # Shift vertices
        self._vertices[:, 1:, :] += dt[:, :, np.newaxis] * v_ind.reshape(
            (self.N, self.N_segments, 3))
        if verbose: prog.display()
Ejemplo n.º 5
0
    def _get_filament_influences(self, points):
        # Determines the unit vortex influence from the wake filaments on the given points

        # Determine displacement vectors: first index is point, second is filament, third is segment, fourth is vector component
        r0 = points[:, np.newaxis, np.newaxis, :] - self._vertices[
            np.newaxis, :, :self.N_segments, :]
        r1 = points[:, np.newaxis,
                    np.newaxis, :] - self._vertices[np.newaxis, :,
                                                    1:self.N_segments + 1, :]

        # Determine displacement vector magnitudes
        r0_mag = vec_norm(r0)
        r1_mag = vec_norm(r1)

        # Calculate influence of each segment
        inf = np.sum(
            ((r0_mag + r1_mag) /
             (r0_mag * r1_mag *
              (r0_mag * r1_mag + vec_inner(r0, r1))))[:, :, :, np.newaxis] *
            vec_cross(r0, r1),
            axis=2)

        return 0.25 / np.pi * np.nan_to_num(inf)
Ejemplo n.º 6
0
    def set_condition(self, **kwargs):
        """Sets the atmospheric conditions for the computation.

        V_inf : list
            Freestream velocity vector.

        rho : float
            Freestream density.
        
        angular_rate : list, optional
            Body-fixed angular rate vector (given in rad/s). Defaults to [0.0, 0.0, 0.0].
        """

        # Set solved flag
        self._solved = False

        # Get freestream
        self._v_inf = np.array(kwargs["V_inf"])
        self._V_inf = norm(self._v_inf)
        self._u_inf = self._v_inf / self._V_inf
        self._rho = kwargs["rho"]
        self._omega = np.array(kwargs.get("angular_rate", [0.0, 0.0, 0.0]))

        # Create part of b vector dependent upon v_inf and rotation
        v_rot = vec_cross(self._omega, self._mesh.cp)
        self._v_inf_and_rot = self._v_inf - v_rot
        self._b = -vec_inner(self._v_inf - v_rot, self._mesh.n)

        # Get solid body rotation
        self._omega = np.array(kwargs.get("angular_rate", [0.0, 0.0, 0.0]))

        # Finish Kutta edge search on mesh
        self._mesh.finalize_kutta_edge_search(self._u_inf)

        # Update wake
        self._mesh.wake.set_filament_direction(self._v_inf, self._omega)
Ejemplo n.º 7
0
    def export_vtk(self, filename):
        """Exports the solver results on the mesh to a VTK file. If a wake exists, the only meaningful scalar result which will be specified on the wake filaments is the filament strength. All other results are arbitrarily set to zero on the wake.

        Parameters
        ----------
        filename : str
            Name of the file to write the results to. Must have '.vtk' extension.
        """

        # Check extension
        if '.vtk' not in filename:
            raise IOError(
                "Filename for VTK export must contain .vtk extension.")

        # Open file
        with open(filename, 'w') as export_handle:

            # Write header
            print("# vtk DataFile Version 3.0", file=export_handle)
            print(
                "PyPan results file. Generated by PyPan, USU AeroLab (c) 2020.",
                file=export_handle)
            print("ASCII", file=export_handle)

            # Write dataset
            print("DATASET POLYDATA", file=export_handle)

            # Write vertices
            vertices, panel_indices = self._mesh.get_vtk_data()
            wake_vertices, wake_filament_indices, N_segments = self._mesh.wake.get_vtk_data(
            )
            print(
                "POINTS {0} float".format(len(vertices) + len(wake_vertices)),
                file=export_handle)
            for vertex in vertices:
                print("{0:<20.12}{1:<20.12}{2:<20.12}".format(*vertex),
                      file=export_handle)
            for vertex in wake_vertices:
                print("{0:<20.12}{1:<20.12}{2:<20.12}".format(*vertex),
                      file=export_handle)

            # Determine wake filament list size
            size = 0
            for li in wake_filament_indices:
                size += len(li)

            # Write wake filaments
            print("LINES {0} {1}".format(N_segments, size), file=export_handle)
            for filament in wake_filament_indices:
                print(" ".join([
                    str(index) if i == 0 else str(index + len(vertices))
                    for i, index in enumerate(filament)
                ]),
                      file=export_handle)

            # Determine polygon list size
            size = 0
            for pi in panel_indices:
                size += len(pi)

            # Write panel polygons
            print("POLYGONS {0} {1}".format(self._N_panels, size),
                  file=export_handle)
            for panel in panel_indices:
                print(" ".join([str(i) for i in panel]), file=export_handle)

            # Write flow results
            print("CELL_DATA {0}".format(self._N_panels + N_segments),
                  file=export_handle)

            # Normals
            print("NORMALS panel_normals float", file=export_handle)
            for i in range(N_segments):
                print("0.00 0.00 0.00", file=export_handle)
            for n in self._mesh.n:
                print("{0:<20.12} {1:<20.12} {2:<20.12}".format(
                    n[0], n[1], n[2]),
                      file=export_handle)

            # Pressure coefficient
            print("SCALARS pressure_coefficient float 1", file=export_handle)
            print("LOOKUP_TABLE default", file=export_handle)
            for i in range(N_segments):
                print("0.0", file=export_handle)
            for C_P in self._C_P:
                print("{0:<20.12}".format(C_P), file=export_handle)

            # Singularity strength
            if hasattr(self, "_mu"):
                print("SCALARS doublet_strength float 1", file=export_handle)
                print("LOOKUP_TABLE default", file=export_handle)
                for i in range(self._mesh.wake.N):

                    # Determine strength of filament
                    mu = 0

                    # Add for outbound panels
                    outbound_panels = self._mesh.wake.outbound_panels[i]
                    if len(outbound_panels) > 0:
                        mu -= self._mu[outbound_panels[0]]
                        mu += self._mu[outbound_panels[1]]

                    # Add for inbound panels
                    inbound_panels = self._mesh.wake.inbound_panels[i]
                    if len(inbound_panels) > 0:
                        mu += self._mu[inbound_panels[0]]
                        mu -= self._mu[inbound_panels[1]]

                    # Print out
                    for i in range(self._mesh.wake.N_segments):
                        print("{0:<20.12}".format(mu), file=export_handle)

                for mu in self._mu:
                    print("{0:<20.12}".format(mu), file=export_handle)

            # Velocity
            if hasattr(self, "_v"):
                print("VECTORS velocity float", file=export_handle)
                for i in range(N_segments):
                    print("0.0 0.0 0.0", file=export_handle)
                for v in self._v:
                    print("{0:<20.12} {1:<20.12} {2:<20.12}".format(
                        v[0], v[1], v[2]),
                          file=export_handle)

                # Normal velocity
                print("SCALARS normal_velocity float", file=export_handle)
                print("LOOKUP_TABLE default", file=export_handle)
                for i in range(N_segments):
                    print("0.0", file=export_handle)
                for v_n in vec_inner(self._v, self._mesh.n):
                    print("{0:<20.12}".format(v_n), file=export_handle)

        if self._verbose:
            print()
            print(
                "Case results successfully written to '{0}'.".format(filename))
Ejemplo n.º 8
0
    def get_influence_matrix(self, **kwargs):
        """Create wake influence matrix; first index is the influenced panels, second is the influencing panel, third is the velocity component.

        Parameters
        ----------
        points : ndarray
            Array of points at which to calculate the influence.

        N_panels : int
            Number of panels in the mesh to which this wake belongs.

        Returns
        -------
        ndarray
            Trailing vortex influences.
        """

        # Get kwargs
        points = kwargs.get("points")

        # Initialize storage
        N = len(points)
        vortex_influence_matrix = np.zeros((N, kwargs["N_panels"], 3))

        # Get influence of edges
        for edge in self._kutta_edges:

            # Get indices of panels defining the edge
            p_ind = edge.panel_indices

            # Get infulence
            V = edge.get_vortex_influence(points)

            # Store
            vortex_influence_matrix[:, p_ind[0]] = -V
            vortex_influence_matrix[:, p_ind[1]] = V

        # Determine displacement vector magnitudes
        r = points[:, np.newaxis, :] - self._vertices[np.newaxis, :, :]
        r_mag = vec_norm(r)

        # Calculate influences
        V = 0.25 / np.pi * vec_cross(
            self.filament_dirs[np.newaxis, :, :],
            r) / (r_mag *
                  (r_mag - vec_inner(self.filament_dirs[np.newaxis, :, :], r))
                  )[:, :, np.newaxis]
        for i in range(self.N):

            # Add for outbound panels
            outbound_panels = self.outbound_panels[i]
            if len(outbound_panels) > 0:
                vortex_influence_matrix[:, outbound_panels[0], :] -= V[:, i, :]
                vortex_influence_matrix[:, outbound_panels[1], :] += V[:, i, :]

            # Add for inbound panels
            inbound_panels = self.inbound_panels[i]
            if len(inbound_panels) > 0:
                vortex_influence_matrix[:, inbound_panels[0], :] += V[:, i, :]
                vortex_influence_matrix[:, inbound_panels[1], :] -= V[:, i, :]

        return vortex_influence_matrix