Пример #1
0
    def draw_streamlines(self, res=200, show=True):
        fig, ax = plt.subplots(1, 1, figsize=(6.4, 4.8), dpi=200)

        plt.xlim(-0.5, 1.5)
        plt.ylim(-0.5, 0.5)
        xrng = np.diff(np.array(ax.get_xlim()))
        yrng = np.diff(np.array(ax.get_ylim()))

        x = np.linspace(*ax.get_xlim(), int(np.round(res * xrng / yrng)))
        y = np.linspace(*ax.get_ylim(), res)

        X, Y = np.meshgrid(x, y)
        shape = X.shape
        X = X.flatten()
        Y = Y.flatten()

        U, V = self.calculate_velocity(X, Y)
        X = X.reshape(shape)
        Y = Y.reshape(shape)
        U = U.reshape(shape)
        V = V.reshape(shape)

        # NaN out any points inside the airfoil
        for airfoil in self.airfoils:
            contains = airfoil.contains_points(X, Y)
            U[contains] = np.NaN
            V[contains] = np.NaN

        speed = (U**2 + V**2)**0.5
        Cp = 1 - speed**2

        ### Draw the airfoils
        for airfoil in self.airfoils:
            plt.fill(airfoil.x(), airfoil.y(), "k", linewidth=0, zorder=4)

        plt.streamplot(
            x,
            y,
            U,
            V,
            color=speed,
            density=2.5,
            arrowsize=0,
            cmap=plt.get_cmap('coolwarm_r'),
        )
        CB = plt.colorbar(
            orientation="horizontal",
            shrink=0.8,
            aspect=40,
        )
        CB.set_label(r"Relative Airspeed ($U/U_\infty$)")
        plt.clim(0.6, 1.4)

        plt.gca().set_aspect('equal', adjustable='box')
        plt.xlabel(r"$x/c$")
        plt.ylabel(r"$y/c$")
        plt.title(rf"Inviscid Airfoil: Flow Field")
        plt.tight_layout()
        if show:
            plt.show()
Пример #2
0
    def _enforce_governing_equations(self):

        for airfoil in self.airfoils:
            ### Compute normal velocities at the middle of each panel
            x_midpoints = np.trapz(airfoil.x())
            y_midpoints = np.trapz(airfoil.y())

            u_midpoints, v_midpoints = self.calculate_velocity(
                x_field=x_midpoints,
                y_field=y_midpoints,
            )

            panel_dx = np.diff(airfoil.x())
            panel_dy = np.diff(airfoil.y())
            panel_length = (panel_dx**2 + panel_dy**2)**0.5

            xp_hat_x = panel_dx / panel_length  # x-coordinate of the xp_hat vector
            xp_hat_y = panel_dy / panel_length  # y-coordinate of the yp_hat vector

            yp_hat_x = -xp_hat_y
            yp_hat_y = xp_hat_x

            normal_velocities = u_midpoints * yp_hat_x + v_midpoints * yp_hat_y

            ### Add in flow tangency constraint
            self.opti.subject_to(normal_velocities == 0)

            ### Add in Kutta condition
            self.opti.subject_to(airfoil.gamma[0] + airfoil.gamma[-1] == 0)
Пример #3
0
def test_opti_hanging_chain_with_callback(plot=False):
    N = 40
    m = 40 / N
    D = 70 * N
    g = 9.81
    L = 1

    opti = asb.Opti()

    x = opti.variable(init_guess=np.linspace(-2, 2, N))
    y = opti.variable(
        init_guess=1,
        n_vars=N,
    )

    distance = np.sqrt(  # Distance from one node to the next
        np.diff(x)**2 + np.diff(y)**2)

    potential_energy_spring = 0.5 * D * np.sum((distance - L / N)**2)
    potential_energy_gravity = g * m * np.sum(y)
    potential_energy = potential_energy_spring + potential_energy_gravity

    opti.minimize(potential_energy)

    # Add end point constraints
    opti.subject_to([x[0] == -2, y[0] == 1, x[-1] == 2, y[-1] == 1])

    # Add a ground constraint
    opti.subject_to(y >= np.cos(0.1 * x) - 0.5)

    # Add a callback

    if plot:

        def my_callback(iter: int):
            plt.plot(opti.debug.value(x),
                     opti.debug.value(y),
                     ".-",
                     label=f"Iter {iter}",
                     zorder=3 + iter)

        fig, ax = plt.subplots(1, 1, figsize=(6.4, 4.8), dpi=200)
        x_ground = np.linspace(-2, 2, N)
        y_ground = np.cos(0.1 * x_ground) - 0.5
        plt.plot(x_ground, y_ground, "--k", zorder=2)

    else:

        def my_callback(iter: int):
            print(f"Iter {iter}")
            print(f"\tx = {opti.debug.value(x)}")
            print(f"\ty = {opti.debug.value(y)}")

    sol = opti.solve(callback=my_callback)

    assert sol.value(potential_energy) == pytest.approx(626.462, abs=1e-3)

    if plot:
        plt.show()
Пример #4
0
    def _calculate_forces(self):

        for airfoil in self.airfoils:
            panel_dx = np.diff(airfoil.x())
            panel_dy = np.diff(airfoil.y())
            panel_length = (panel_dx**2 + panel_dy**2)**0.5

            ### Sum up the vorticity on this airfoil by integrating
            airfoil.vorticity = np.sum(
                (airfoil.gamma[1:] + airfoil.gamma[:-1]) / 2 * panel_length)

            airfoil.Cl = 2 * airfoil.vorticity  # TODO normalize by chord and freestream velocity etc.

        self.total_vorticity = sum(
            [airfoil.vorticity for airfoil in self.airfoils])
        self.Cl = 2 * self.total_vorticity
Пример #5
0
def test_quadcopter_navigation():
    opti = asb.Opti()

    N = 300
    time_final = 1
    time = np.linspace(0, time_final, N)

    left_thrust = opti.variable(init_guess=0.5, scale=1, n_vars=N, lower_bound=0, upper_bound=1)
    right_thrust = opti.variable(init_guess=0.5, scale=1, n_vars=N, lower_bound=0, upper_bound=1)

    mass = 0.1

    dyn = asb.FreeBodyDynamics(
        opti_to_add_constraints_to=opti,
        time=time,
        xe=opti.variable(init_guess=np.linspace(0, 1, N)),
        ze=opti.variable(init_guess=np.linspace(0, -1, N)),
        u=opti.variable(init_guess=0, n_vars=N),
        w=opti.variable(init_guess=0, n_vars=N),
        theta=opti.variable(init_guess=np.linspace(np.pi / 2, np.pi / 2, N)),
        q=opti.variable(init_guess=0, n_vars=N),
        X=left_thrust + right_thrust,
        M=(right_thrust - left_thrust) * 0.1 / 2,
        mass=mass,
        Iyy=0.5 * mass * 0.1 ** 2,
        g=9.81,
    )

    opti.subject_to([  # Starting state
        dyn.xe[0] == 0,
        dyn.ze[0] == 0,
        dyn.u[0] == 0,
        dyn.w[0] == 0,
        dyn.theta[0] == np.radians(90),
        dyn.q[0] == 0,
    ])

    opti.subject_to([  # Final state
        dyn.xe[-1] == 1,
        dyn.ze[-1] == -1,
        dyn.u[-1] == 0,
        dyn.w[-1] == 0,
        dyn.theta[-1] == np.radians(90),
        dyn.q[-1] == 0,
    ])

    effort = np.sum(  # The average "effort per second", where effort is integrated as follows:
        np.trapz(left_thrust ** 2 + right_thrust ** 2) * np.diff(time)
    ) / time_final

    opti.minimize(effort)

    sol = opti.solve()
    dyn.substitute_solution(sol)

    assert sol.value(effort) == pytest.approx(0.714563, rel=0.01)

    print(sol.value(effort))
Пример #6
0
def test_block_move_fixed_time():
    opti = asb.Opti()

    n_timesteps = 300

    time = np.linspace(0, 1, n_timesteps)

    dyn = asb.DynamicsPointMass1DHorizontal(
        mass_props=asb.MassProperties(mass=1),
        x_e=opti.variable(init_guess=np.linspace(0, 1, n_timesteps)),
        u_e=opti.variable(init_guess=1, n_vars=n_timesteps),
    )

    u = opti.variable(init_guess=np.linspace(1, -1, n_timesteps))

    dyn.add_force(
        Fx=u
    )

    dyn.constrain_derivatives(
        opti=opti,
        time=time
    )

    opti.subject_to([
        dyn.x_e[0] == 0,
        dyn.x_e[-1] == 1,
        dyn.u_e[0] == 0,
        dyn.u_e[-1] == 0,
    ])

    # effort = np.sum(
    #     np.trapz(dyn.X ** 2) * np.diff(time)
    # )

    effort = np.sum(  # More sophisticated integral-of-squares integration (closed form correct)
        np.diff(time) / 3 *
        (u[:-1] ** 2 + u[:-1] * u[1:] + u[1:] ** 2)
    )

    opti.minimize(effort)

    sol = opti.solve()

    dyn.substitute_solution(sol)

    assert dyn.x_e[0] == pytest.approx(0)
    assert dyn.x_e[-1] == pytest.approx(1)
    assert dyn.u_e[0] == pytest.approx(0)
    assert dyn.u_e[-1] == pytest.approx(0)
    assert np.max(dyn.u_e) == pytest.approx(1.5, abs=0.01)
    assert sol.value(u)[0] == pytest.approx(6, abs=0.05)
    assert sol.value(u)[-1] == pytest.approx(-6, abs=0.05)
Пример #7
0
def peak_sun_hours_per_day_on_horizontal(latitude, day_of_year, scattering=True):
    """
    How many hours of equivalent peak sun do you get per day?
    :param latitude: Latitude [degrees]
    :param day_of_year: Julian day (1 == Jan. 1, 365 == Dec. 31)
    :param time: Time since (local) solar noon [seconds]
    :param scattering: Boolean: include scattering effects at very low angles?
    :return:
    """
    times = np.linspace(0, 86400, 1000)
    dt = np.diff(times)
    normalized_fluxes = (
        # solar_flux_outside_atmosphere_normal(day_of_year) *
        incidence_angle_function(latitude, day_of_year, times, scattering)
    )
    sun_hours = np.sum(
        (normalized_fluxes[1:] + normalized_fluxes[:-1]) / 2 * dt
    ) / 3600

    return sun_hours
data = io.loadmat(str(root / "data" / "wind_data_99.mat"))
lats_v = data["lats"].flatten()
alts_v = data["alts"].flatten()
speeds = data["speeds"].reshape(len(alts_v), len(lats_v)).T.flatten()

lats, alts = np.meshgrid(lats_v, alts_v, indexing="ij")
lats = lats.flatten()
alts = alts.flatten()

# %%

lats_scaled = (lats - 37.5) / 11.5
alts_scaled = (alts - 24200) / 24200
speeds_scaled = (speeds - 7) / 56

alt_diff = np.diff(alts_v)
alt_diff_aug = np.hstack((alt_diff[0], alt_diff, alt_diff[-1]))
weights_1d = (alt_diff_aug[:-1] + alt_diff_aug[1:]) / 2
weights_1d = weights_1d / np.mean(weights_1d)
# region_of_interest = np.logical_and(
#     alts_v > 10000,
#     alts_v < 40000
# )
# true_weights = np.where(
#     region_of_interest,
#     2,
#     1
# )
weights = np.tile(weights_1d, (93, 1)).flatten()

# %%
Пример #9
0
def test_trapz():
    a = np.arange(100)

    assert np.diff(np.trapz(a)) == pytest.approx(1)
Пример #10
0
    def constrain_derivative(self,
                             derivative: cas.MX,
                             variable: cas.MX,
                             with_respect_to: Union[np.ndarray, cas.MX],
                             method: str = "midpoint",
                             ) -> None:
        """
        Adds a constraint to the optimization problem such that:

            d(variable) / d(with_respect_to) == derivative

        Can be used directly; also called indirectly by opti.derivative_of() for implicit derivative creation.

        Args:
            derivative: The derivative that is to be constrained here.

            variable: The variable or quantity that you are taking the derivative of. The "numerator" of the
            derivative, in colloquial parlance.

            with_respect_to: The variable or quantity that you are taking the derivative with respect to. The
            "denominator" of the derivative, in colloquial parlance.

                In a typical example case, this `with_respect_to` parameter would be time. Please make sure that the
                value of this parameter is monotonically increasing, otherwise you may get nonsensical answers.

            method: The type of integrator to use to define this derivative. Options are:

                * "forward euler" - a first-order-accurate forward Euler method

                    Citation: https://en.wikipedia.org/wiki/Euler_method

                * "backwards euler" - a first-order-accurate backwards Euler method

                    Citation: https://en.wikipedia.org/wiki/Backward_Euler_method

                * "midpoint" or "trapezoid" - a second-order-accurate midpoint method

                    Citation: https://en.wikipedia.org/wiki/Midpoint_method

                * "simpson" - Simpson's rule for integration

                    Citation: https://en.wikipedia.org/wiki/Simpson%27s_rule

                * "runge-kutta" or "rk4" - a fourth-order-accurate Runge-Kutta method. I suppose that technically,
                "forward euler", "backward euler", and "midpoint" are all (lower-order) Runge-Kutta methods...

                    Citation: https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods#The_Runge%E2%80%93Kutta_method

                * "runge-kutta-3/8" - A modified version of the Runge-Kutta 4 proposed by Kutta in 1901. Also
                fourth-order-accurate, but all of the error coefficients are smaller than they are in the standard
                Runge-Kutta 4 method. The downside is that more floating point operations are required per timestep,
                as the Butcher tableau is more dense (i.e. not banded).

                    Citation: Kutta, Martin (1901), "Beitrag zur näherungsweisen Integration totaler
                    Differentialgleichungen", Zeitschrift für Mathematik und Physik, 46: 435–453

            Note that all methods are expressed as integrators rather than differentiators; this prevents
            singularities from forming in the limit of timestep approaching zero. (For those coming from the PDE
            world, this is analogous to using finite volume methods rather than finite difference methods to allow
            shock capturing.)

        Returns: None (adds constraint in-place).

        """
        d_var = np.diff(variable)
        d_time = np.diff(with_respect_to)  # Calculate the timestep

        # TODO scale constraints by variable scale?
        # TODO make

        if method == "forward euler" or method == "forward" or method == "forwards":
            # raise NotImplementedError
            self.subject_to(
                d_var == derivative[:-1] * d_time
            )


        elif method == "backward euler" or method == "backward" or method == "backwards":
            # raise NotImplementedError
            self.subject_to(
                d_var == derivative[1:] * d_time
            )

        elif method == "midpoint" or method == "trapezoid" or method == "trapezoidal":
            self.subject_to(
                d_var == np.trapz(derivative) * d_time,
            )


        elif method == "simpson":
            raise NotImplementedError

        elif method == "runge-kutta" or method == "rk4":
            raise NotImplementedError

        elif method == "runge-kutta-3/8":
            raise NotImplementedError

        else:
            raise ValueError("Bad value of `method`!")
Пример #11
0
    def repanel(
        self,
        n_points_per_side: int = 100,
    ) -> 'Airfoil':
        """
        Returns a repaneled version of the airfoil with cosine-spaced coordinates on the upper and lower surfaces.
        :param n_points_per_side: Number of points per side (upper and lower) of the airfoil [int]
            Notes: The number of points defining the final airfoil will be n_points_per_side*2-1,
            since one point (the leading edge point) is shared by both the upper and lower surfaces.
        :return: Returns the new airfoil.
        """

        upper_original_coors = self.upper_coordinates(
        )  # Note: includes leading edge point, be careful about duplicates
        lower_original_coors = self.lower_coordinates(
        )  # Note: includes leading edge point, be careful about duplicates

        # Find distances between coordinates, assuming linear interpolation
        upper_distances_between_points = (
            (upper_original_coors[:-1, 0] - upper_original_coors[1:, 0])**2 +
            (upper_original_coors[:-1, 1] - upper_original_coors[1:, 1])**
            2)**0.5
        lower_distances_between_points = (
            (lower_original_coors[:-1, 0] - lower_original_coors[1:, 0])**2 +
            (lower_original_coors[:-1, 1] - lower_original_coors[1:, 1])**
            2)**0.5
        upper_distances_from_TE = np.hstack(
            (0, np.cumsum(upper_distances_between_points)))
        lower_distances_from_LE = np.hstack(
            (0, np.cumsum(lower_distances_between_points)))
        upper_distances_from_TE_normalized = upper_distances_from_TE / upper_distances_from_TE[
            -1]
        lower_distances_from_LE_normalized = lower_distances_from_LE / lower_distances_from_LE[
            -1]

        distances_from_TE_normalized = np.hstack(
            (upper_distances_from_TE_normalized,
             1 + lower_distances_from_LE_normalized[1:]))

        # Generate a cosine-spaced list of points from 0 to 1
        cosspaced_points = np.cosspace(0, 1, n_points_per_side)
        s = np.hstack((
            cosspaced_points,
            1 + cosspaced_points[1:],
        ))

        # Check that there are no duplicate points in the airfoil.
        if np.any(np.diff(distances_from_TE_normalized) == 0):
            raise ValueError(
                "This airfoil has a duplicated point (i.e. two adjacent points with the same (x, y) coordinates), so you can't repanel it!"
            )

        x = interp1d(
            distances_from_TE_normalized,
            self.x(),
            kind="cubic",
        )(s)
        y = interp1d(
            distances_from_TE_normalized,
            self.y(),
            kind="cubic",
        )(s)

        return Airfoil(name=self.name, coordinates=stack_coordinates(x, y))
Пример #12
0
if __name__ == '__main__':
    mc = 0.7
    drag = lambda mach: approximate_CD_wave(
        mach,
        mach_crit=mc,
        CD_wave_at_fully_supersonic=1,
    )

    import matplotlib.pyplot as plt
    import aerosandbox.tools.pretty_plots as p

    fig, ax = plt.subplots(1, 3, figsize=(10, 5))

    mach = np.linspace(0., 2, 10000)
    drag = drag(mach)
    ddragdm = np.gradient(drag, np.diff(mach)[0])
    dddragdm = np.gradient(ddragdm, np.diff(mach)[0])

    plt.sca(ax[0])
    plt.title("$C_D$")
    plt.ylabel("$C_{D, wave} / C_{D, wave, M=1.2}$")
    plt.plot(mach, drag)
    plt.ylim(-0.05, 1.5)
    # plt.ylim(-0.01, 0.05)

    plt.sca(ax[1])
    plt.title("$d(C_D)/d(M)$")
    plt.ylabel(r"$\frac{d(C_{D, wave})}{dM}$")
    plt.plot(mach, ddragdm)
    plt.ylim(-5, 15)
Пример #13
0
    def draw(
        self,
        vehicle_model: Airplane = None,
        backend: str = "pyvista",
        draw_axes: bool = True,
        scale_vehicle_model: Union[float, None] = None,
        n_vehicles_to_draw: int = 10,
        cg_axes: str = "geometry",
        show: bool = True,
    ):
        if backend == "pyvista":
            import pyvista as pv
            import aerosandbox.tools.pretty_plots as p

            if vehicle_model is None:
                default_vehicle_stl = _asb_root / "dynamics/visualization/default_assets/yf23.stl"
                vehicle_model = pv.read(str(default_vehicle_stl))
            elif isinstance(vehicle_model, pv.PolyData):
                pass
            elif isinstance(vehicle_model, Airplane):
                vehicle_model = vehicle_model.draw(backend="pyvista",
                                                   show=False)
                vehicle_model.rotate_y(
                    180)  # Rotate from geometry axes to body axes.
            elif isinstance(
                    vehicle_model, str
            ):  # Interpret the string as a filepath to a .stl or similar
                try:
                    pv.read(filename=vehicle_model)
                except:
                    raise ValueError("Could not parse `vehicle_model`!")
            else:
                raise TypeError(
                    "`vehicle_model` should be an Airplane or PolyData object."
                )

            x_e = np.array(self.x_e)
            y_e = np.array(self.y_e)
            z_e = np.array(self.z_e)
            if np.length(x_e) == 1:
                x_e = x_e * np.ones(len(self))
            if np.length(y_e) == 1:
                y_e = y_e * np.ones(len(self))
            if np.length(z_e) == 1:
                z_e = z_e * np.ones(len(self))

            if scale_vehicle_model is None:
                trajectory_bounds = np.array([
                    [x_e.min(), x_e.max()],
                    [y_e.min(), y_e.max()],
                    [z_e.min(), z_e.max()],
                ])
                trajectory_size = np.max(np.diff(trajectory_bounds, axis=1))

                vehicle_bounds = np.array(vehicle_model.bounds).reshape((3, 2))
                vehicle_size = np.max(np.diff(vehicle_bounds, axis=1))

                scale_vehicle_model = 0.1 * trajectory_size / vehicle_size

            ### Initialize the plotter
            plotter = pv.Plotter()

            # Set the window title
            title = "ASB Dynamics"
            addenda = []
            if scale_vehicle_model != 1:
                addenda.append(
                    f"Vehicle drawn at {scale_vehicle_model:.2g}x scale")
            addenda.append(f"{self.__class__.__name__} Engine")
            if len(addenda) != 0:
                title = title + f" ({'; '.join(addenda)})"
            plotter.title = title

            # Draw axes and grid
            plotter.add_axes()
            plotter.show_grid(color='gray')

            ### Draw the vehicle
            for i in np.unique(
                    np.round(np.linspace(0,
                                         len(self) - 1,
                                         n_vehicles_to_draw))).astype(int):
                dyn = self[i]
                try:
                    phi = dyn.phi
                except AttributeError:
                    phi = dyn.bank
                try:
                    theta = dyn.theta
                except AttributeError:
                    theta = dyn.gamma
                try:
                    psi = dyn.psi
                except AttributeError:
                    psi = dyn.track

                x_cg_b, y_cg_b, z_cg_b = dyn.convert_axes(dyn.mass_props.x_cg,
                                                          dyn.mass_props.y_cg,
                                                          dyn.mass_props.z_cg,
                                                          from_axes=cg_axes,
                                                          to_axes="body")

                this_vehicle = copy.deepcopy(vehicle_model)
                this_vehicle.translate([
                    -x_cg_b,
                    -y_cg_b,
                    -z_cg_b,
                ],
                                       inplace=True)
                this_vehicle.points *= scale_vehicle_model
                this_vehicle.rotate_x(np.degrees(phi), inplace=True)
                this_vehicle.rotate_y(np.degrees(theta), inplace=True)
                this_vehicle.rotate_z(np.degrees(psi), inplace=True)
                this_vehicle.translate([
                    dyn.x_e,
                    dyn.y_e,
                    dyn.z_e,
                ],
                                       inplace=True)
                plotter.add_mesh(this_vehicle, )
                if draw_axes:
                    rot = np.rotation_matrix_from_euler_angles(phi, theta, psi)
                    axes_scale = 0.5 * np.max(
                        np.diff(np.array(this_vehicle.bounds).reshape((3, -1)),
                                axis=1))
                    origin = np.array([
                        dyn.x_e,
                        dyn.y_e,
                        dyn.z_e,
                    ])
                    for i, c in enumerate(["r", "g", "b"]):
                        plotter.add_mesh(
                            pv.Spline(
                                np.array(
                                    [origin,
                                     origin + rot[:, i] * axes_scale])),
                            color=c,
                            line_width=2.5,
                        )

            for i in range(len(self)):
                ### Draw the trajectory line

                polyline = pv.Spline(np.array([x_e, y_e, z_e]).T)
                plotter.add_mesh(
                    polyline,
                    color=p.adjust_lightness(p.palettes["categorical"][0],
                                             1.2),
                    line_width=3,
                )

            ### Finalize the plotter
            plotter.camera.up = (0, 0, -1)
            plotter.camera.Azimuth(90)
            plotter.camera.Elevation(60)
            if show:
                plotter.show()
            return plotter
Пример #14
0
velocity = opti.derivative_of(
    position,
    with_respect_to=time,
    derivative_init_guess=1,
)

force = opti.variable(init_guess=np.linspace(1, -1, n_timesteps),
                      n_vars=n_timesteps)

opti.constrain_derivative(
    variable=velocity,
    with_respect_to=time,
    derivative=force / mass_block,
)

effort_expended = np.sum(np.trapz(force**2) * np.diff(time))

opti.minimize(effort_expended)

### Boundary conditions
opti.subject_to([
    position[0] == 0,
    position[-1] == 1,
    velocity[0] == 0,
    velocity[-1] == 0,
])

sol = opti.solve()

import matplotlib.pyplot as plt
import seaborn as sns
Пример #15
0
def test_invertability_of_diff_trapz():
    a = np.sin(np.arange(10))

    assert np.all(np.trapz(np.diff(a)) == pytest.approx(np.diff(np.trapz(a))))
Пример #16
0
)  # From AVF Eq. 4.54
c_D = H_star / 2 / Re_theta * np.where(
    H < 4,
    0.207 + 0.00205 * np.abs(4 - H) ** 5.5,
    0.207 - 0.100 * (H - 4) ** 2 / H ** 2
)  # From AVF Eq. 4.55
Re_theta_o = 10 ** (
        2.492 / (H - 1) ** 0.43 +
        0.7 * (
                np.tanh(
                    14 / (H - 1) - 9.24
                ) + 1
        )
)  # From AVF Eq. 6.38

d_theta_dx = np.diff(theta) / np.diff(x)
d_ue_dx = np.diff(ue) / np.diff(x)
d_H_star_dx = np.diff(H_star) / np.diff(x)


def int(x):
    return (x[1:] + x[:-1]) / 2


def logint(x):
    # return int(x)
    logx = np.log(x)
    return np.exp(
        (logx[1:] + logx[:-1]) / 2
    )
Пример #17
0
def test_diff():
    a = np.arange(100)

    assert np.all(np.diff(a) == pytest.approx(1))