Пример #1
0
    def test_hover(self):
        # num_nodes = 40
        nstart = 0
        nend = 39
        num_nodes = nend - nstart + 1
        chord = 0.060
        theta = 0.0
        Rtip = 0.656
        Rhub = 0.19 * Rtip
        rho = 1.225
        omega = 800.0 * np.pi / 30
        Vinf = 0.0
        turbine = False
        B = 3

        r = np.linspace(Rhub + 0.01 * Rtip, Rtip - 0.01 * Rtip, 30)
        num_radial = r.shape[-1]
        r = np.tile(r, (num_nodes, 1))

        chord = np.tile(chord, (num_nodes, num_radial))
        theta = np.tile(theta, (num_nodes, num_radial))

        # Airfoil interpolator.
        af = af_from_files(["airfoils/naca0012v2.txt"])

        pitch = np.linspace(1e-4, 20 * np.pi / 180, 40)[nstart:nend + 1]
        prob = om.Problem()

        comp = om.IndepVarComp()
        comp.add_output('v', val=Vinf, shape=num_nodes, units='m/s')
        comp.add_output('omega', val=np.tile(omega, num_nodes), units='rad/s')
        comp.add_output('radii',
                        val=r,
                        shape=(num_nodes, num_radial),
                        units='m')
        comp.add_output('chord',
                        val=chord,
                        shape=(num_nodes, num_radial),
                        units='m')
        comp.add_output('theta',
                        val=theta,
                        shape=(num_nodes, num_radial),
                        units='rad')
        comp.add_output('precone', val=0., shape=num_nodes, units='rad')
        comp.add_output('rho', val=rho, shape=(num_nodes, 1), units='kg/m**3')
        comp.add_output('mu', val=1.0, shape=(num_nodes, 1), units='N/m**2*s')
        comp.add_output('asound', val=1.0, shape=(num_nodes, 1), units='m/s')
        comp.add_output('hub_radius', val=Rhub, shape=num_nodes, units='m')
        comp.add_output('prop_radius', val=Rtip, shape=num_nodes, units='m')
        comp.add_output('pitch', val=pitch, shape=num_nodes, units='rad')
        prob.model.add_subsystem('ivc', comp, promotes_outputs=['*'])

        comp = SimpleInflow(num_nodes=num_nodes, num_radial=num_radial)
        prob.model.add_subsystem(
            "simple_inflow",
            comp,
            promotes_inputs=["v", "omega", "radii", "precone"],
            promotes_outputs=["Vx", "Vy"])

        comp = make_component(
            CCBladeResidualComp(num_nodes=num_nodes,
                                num_radial=num_radial,
                                B=B,
                                af=af,
                                turbine=turbine))
        comp.linear_solver = om.DirectSolver(assemble_jac=True)
        prob.model.add_subsystem('ccblade',
                                 comp,
                                 promotes_inputs=[('r', 'radii'), 'chord',
                                                  'theta', 'Vx', 'Vy', 'rho',
                                                  'mu', 'asound',
                                                  ('Rhub', 'hub_radius'),
                                                  ('Rtip', 'prop_radius'),
                                                  'precone', 'pitch'],
                                 promotes_outputs=['Np', 'Tp'])

        comp = FunctionalsComp(num_nodes=num_nodes,
                               num_radial=num_radial,
                               num_blades=B)
        prob.model.add_subsystem(
            'ccblade_torquethrust_comp',
            comp,
            promotes_inputs=[
                'hub_radius', 'prop_radius', 'radii', 'Np', 'Tp', 'v', 'omega'
            ],
            promotes_outputs=['thrust', 'torque', 'efficiency'])

        prob.setup()
        prob.final_setup()
        prob.run_model()

        # these are not directly from the experimental data, but have been compared to the experimental data and compare favorably.
        # this is more of a regression test on the Vx=0 case
        CTcomp = np.array([
            9.452864991304056e-9, 6.569947366946672e-5, 0.00022338783939012262,
            0.0004420355541809959, 0.0007048495858030926,
            0.0010022162314665929, 0.0013268531109981317,
            0.0016736995380106938, 0.0020399354072946035,
            0.0024223576277264307, 0.0028189858460418893,
            0.0032281290309981213, 0.003649357660426685, 0.004081628946214875,
            0.004526034348853718, 0.004982651929181267, 0.0054553705714941,
            0.005942700094508395, 0.006447634897014323, 0.006963626871239654,
            0.007492654931894796, 0.00803866268066438, 0.008597914974368199,
            0.009163315934297088, 0.00973817187875574, 0.010309276997090536,
            0.010827599471613264, 0.011322361524464346, 0.01180210507896255,
            0.012276543435307877, 0.012749323136224754, 0.013223371028562213,
            0.013697833731701945, 0.01417556699620018, 0.014646124777465859,
            0.015112116772851365, 0.015576452747370885, 0.01602507607909594,
            0.016461827164870473, 0.016880126012974343
        ])
        CQcomp = np.array([
            0.000226663607327854, 0.0002270862930229147, 0.0002292742856722754,
            0.00023412703235791698, 0.00024192624628054639,
            0.0002525855612031453, 0.00026638347417704255,
            0.00028314784456601373, 0.00030299181501156373,
            0.0003259970210015136, 0.00035194661281707764,
            0.00038102864688744595, 0.0004132249034847219,
            0.00044859355432807347, 0.0004873204055790553,
            0.0005293656187218555, 0.0005753409000182888,
            0.0006250099998058788, 0.0006788861946930185,
            0.0007361096750412038, 0.0007970800153713466,
            0.0008624036743669367, 0.0009315051772818803,
            0.0010035766105979213, 0.0010791941808362153,
            0.0011566643573792704, 0.001229236439467123, 0.0013007334425769355,
            0.001372124993921022, 0.0014449961686871802, 0.0015197156782734364,
            0.0015967388663224156, 0.0016761210460920718,
            0.0017578748614666766, 0.0018409716992061841,
            0.0019248522013432586, 0.0020103360819251357, 0.002096387027559033,
            0.002182833604491109, 0.0022686470790128036
        ])
        T = prob.get_val('thrust', units='N')
        Q = prob.get_val('torque', units='N*m')
        A = np.pi * Rtip**2
        CT = T / (rho * A * (omega * Rtip)**2)
        CQ = Q / (rho * A * (omega * Rtip)**2 * Rtip)

        assert_allclose(CT, CTcomp[nstart:nend + 1], atol=1e-4, rtol=1)
        assert_allclose(CQ, CQcomp[nstart:nend + 1], atol=1e-4, rtol=1)
Пример #2
0
    def test_multiple_adv_ratios(self):
        # num_nodes = 20
        nstart = 0
        nend = 19
        num_nodes = nend - nstart + 1

        r = 0.0254 * np.array([
            0.7526, 0.7928, 0.8329, 0.8731, 0.9132, 0.9586, 1.0332, 1.1128,
            1.1925, 1.2722, 1.3519, 1.4316, 1.5114, 1.5911, 1.6708, 1.7505,
            1.8302, 1.9099, 1.9896, 2.0693, 2.1490, 2.2287, 2.3084, 2.3881,
            2.4678, 2.5475, 2.6273, 2.7070, 2.7867, 2.8661, 2.9410
        ]).reshape(1, -1)
        num_radial = r.shape[-1]
        r = np.tile(r, (num_nodes, 1))

        chord = 0.0254 * np.array([
            0.6270, 0.6255, 0.6231, 0.6199, 0.6165, 0.6125, 0.6054, 0.5973,
            0.5887, 0.5794, 0.5695, 0.5590, 0.5479, 0.5362, 0.5240, 0.5111,
            0.4977, 0.4836, 0.4689, 0.4537, 0.4379, 0.4214, 0.4044, 0.3867,
            0.3685, 0.3497, 0.3303, 0.3103, 0.2897, 0.2618, 0.1920
        ]).reshape((1, num_radial))
        chord = np.tile(chord, (num_nodes, 1))

        theta = np.pi / 180.0 * np.array([
            40.2273, 38.7657, 37.3913, 36.0981, 34.8803, 33.5899, 31.6400,
            29.7730, 28.0952, 26.5833, 25.2155, 23.9736, 22.8421, 21.8075,
            20.8586, 19.9855, 19.1800, 18.4347, 17.7434, 17.1005, 16.5013,
            15.9417, 15.4179, 14.9266, 14.4650, 14.0306, 13.6210, 13.2343,
            12.8685, 12.5233, 12.2138
        ]).reshape((1, num_radial))
        theta = np.tile(theta, (num_nodes, 1))

        rho = 1.225
        Rhub = 0.0254 * .5
        Rtip = 0.0254 * 3.0
        B = 2  # number of blades
        turbine = False

        J = np.linspace(0.1, 0.9, 20)[nstart:nend + 1]  # advance ratio
        # J = np.linspace(0.1, 0.9, 20)

        omega = 8000.0 * np.pi / 30

        n = omega / (2 * np.pi)
        D = 2 * Rtip

        Vinf = J * D * n
        dr = r[0, 1] - r[0, 0]

        # Airfoil interpolator.
        af = af_from_files(["airfoils/NACA64_A17.dat"])

        prob = om.Problem()

        comp = om.IndepVarComp()
        comp.add_output('v', val=Vinf, units='m/s')
        comp.add_output('omega', val=np.tile(omega, num_nodes), units='rad/s')
        comp.add_output('radii',
                        val=r,
                        shape=(num_nodes, num_radial),
                        units='m')
        comp.add_output('dradii',
                        val=dr,
                        shape=(num_nodes, num_radial),
                        units='m')
        comp.add_output('chord',
                        val=chord,
                        shape=(num_nodes, num_radial),
                        units='m')
        comp.add_output('theta',
                        val=theta,
                        shape=(num_nodes, num_radial),
                        units='rad')
        comp.add_output('precone', val=0., shape=num_nodes, units='rad')
        comp.add_output('rho', val=rho, shape=(num_nodes, 1), units='kg/m**3')
        comp.add_output('mu', val=1.0, shape=(num_nodes, 1), units='N/m**2*s')
        comp.add_output('asound', val=1.0, shape=(num_nodes, 1), units='m/s')
        comp.add_output('hub_radius', val=Rhub, shape=num_nodes, units='m')
        comp.add_output('prop_radius', val=Rtip, shape=num_nodes, units='m')
        comp.add_output('pitch', val=0.0, shape=num_nodes, units='rad')
        prob.model.add_subsystem('ivc', comp, promotes_outputs=['*'])

        comp = SimpleInflow(num_nodes=num_nodes, num_radial=num_radial)
        prob.model.add_subsystem(
            "simple_inflow",
            comp,
            promotes_inputs=["v", "omega", "radii", "precone"],
            promotes_outputs=["Vx", "Vy"])

        comp = make_component(
            CCBladeResidualComp(num_nodes=num_nodes,
                                num_radial=num_radial,
                                B=B,
                                af=af,
                                turbine=turbine))
        comp.linear_solver = om.DirectSolver(assemble_jac=True)
        prob.model.add_subsystem('ccblade',
                                 comp,
                                 promotes_inputs=[('r', 'radii'), 'chord',
                                                  'theta', 'Vx', 'Vy', 'rho',
                                                  'mu', 'asound',
                                                  ('Rhub', 'hub_radius'),
                                                  ('Rtip', 'prop_radius'),
                                                  'precone', 'pitch'],
                                 promotes_outputs=['Np', 'Tp'])

        comp = FunctionalsComp(num_nodes=num_nodes,
                               num_radial=num_radial,
                               num_blades=B)
        prob.model.add_subsystem(
            'ccblade_torquethrust_comp',
            comp,
            promotes_inputs=[
                'hub_radius', 'prop_radius', 'radii', 'Np', 'Tp', 'v', 'omega'
            ],
            promotes_outputs=['thrust', 'torque', 'efficiency'])

        prob.setup()
        prob.final_setup()
        prob.run_model()

        CTtest = np.array([
            0.12007272369269596, 0.11685168049381163, 0.113039580103141,
            0.1085198115279716, 0.10319003920478352, 0.09725943970861005,
            0.09086388629270399, 0.0839976424307642, 0.07671734636145504,
            0.06920018426158829, 0.06146448769092457, 0.05351803580676398,
            0.04536760940952122, 0.037021384017275026, 0.028444490366496933,
            0.019460037067085302, 0.0101482712198453, 0.0009431056296630114,
            -0.008450506440868786, -0.018336716057292365
        ])[nstart:nend + 1]
        CPtest = np.array([
            0.04881364095025621, 0.0495814889974431, 0.05010784517185014,
            0.0503081363797726, 0.05016808708430908, 0.04959928236064606,
            0.04857913603557237, 0.0470530147170992, 0.045034787733797786,
            0.042554367414565766, 0.03958444136790737, 0.036104210007643946,
            0.032085430933805684, 0.02750011065614528, 0.022307951002430132,
            0.016419276860939747, 0.009846368564156775, 0.002833135813003493,
            -0.004876593891333574, -0.013591625085158215
        ])[nstart:nend + 1]
        etatest = np.array([
            0.24598190455626265, 0.3349080300487075, 0.4155652767326253,
            0.48818637673414306, 0.5521115225679999, 0.6089123481436948,
            0.6595727776885079, 0.7046724703349897, 0.7441662053086512,
            0.7788447616541276, 0.8090611349633181, 0.8347808848055981,
            0.8558196582739432, 0.8715046719672315, 0.8791362131978436,
            0.8670633642311274, 0.7974063895510229, 0.2715632768892098, 0.0,
            0.0
        ])[nstart:nend + 1]

        T = prob.get_val('thrust', units='N')
        Q = prob.get_val('torque', units='N*m')
        eff = prob.get_val('efficiency')

        CT = T / (rho * n**2 * D**4)
        CQ = Q / (rho * n**2 * D**5) * 2 * np.pi

        assert_allclose(CT, CTtest, atol=1e-3, rtol=1)
        assert_allclose(CQ, CPtest, atol=1e-3, rtol=1)
        assert_allclose(eff[T > 0.0], etatest[T > 0.0], atol=1e-3, rtol=1)
Пример #3
0
    def test_camber(self):
        # Copied from CCBlade.jl/test/runtests.jl

        # inputs
        chord = 0.10
        D = 1.6
        RPM = 2100
        rho = 1.225
        pitch = 1.0  # pitch distance in meters.

        # --- rotor definition ---
        turbine = False
        Rhub = 0.0
        Rtip = D / 2
        Rhub_eff = 1e-6  # something small to eliminate hub effects
        Rtip_eff = 100.0  # something large to eliminate tip effects
        B = 2  # number of blades

        # --- section definitions ---

        num_nodes = 1
        R = D / 2.0
        r = np.linspace(R / 10, R, 11)
        dr = r[1] - r[0]
        r = np.tile(r, (num_nodes, 1))
        dr = np.tile(dr, r.shape)
        theta = np.arctan(pitch / (2 * np.pi * r))

        jlmain.eval("""
                function affunc(alpha, Re, M)
                    alpha0 = -3*pi/180
                    cl = 6.2*(alpha - alpha0)
                    cd = 0.008 - 0.003*cl + 0.01*cl*cl
                    return cl, cd
                end
            """)
        affunc = jlmain.affunc

        prob = om.Problem()
        num_radial = r.shape[-1]

        comp = om.IndepVarComp()
        comp.add_output('v', val=[5.0], units='m/s')
        comp.add_output('omega', val=np.tile(RPM, num_nodes), units='rpm')
        comp.add_output('radii', val=r, units='m')
        comp.add_output('dradii', val=dr, units='m')
        comp.add_output('chord',
                        val=chord,
                        shape=(num_nodes, num_radial),
                        units='m')
        comp.add_output('theta',
                        val=theta,
                        shape=(num_nodes, num_radial),
                        units='rad')
        comp.add_output('precone', val=0., shape=num_nodes, units='rad')
        comp.add_output('rho', val=rho, shape=(num_nodes, 1), units='kg/m**3')
        comp.add_output('mu', val=1.0, shape=(num_nodes, 1), units='N/m**2*s')
        comp.add_output('asound', val=1.0, shape=(num_nodes, 1), units='m/s')
        comp.add_output('hub_radius_eff',
                        val=Rhub_eff,
                        shape=num_nodes,
                        units='m')
        comp.add_output('prop_radius_eff',
                        val=Rtip_eff,
                        shape=num_nodes,
                        units='m')
        comp.add_output('hub_radius', val=Rhub, shape=num_nodes, units='m')
        comp.add_output('prop_radius', val=Rtip, shape=num_nodes, units='m')
        comp.add_output('pitch', val=0.0, shape=num_nodes, units='rad')
        prob.model.add_subsystem('ivc', comp, promotes_outputs=['*'])

        comp = SimpleInflow(num_nodes=num_nodes, num_radial=num_radial)
        prob.model.add_subsystem(
            "simple_inflow",
            comp,
            promotes_inputs=["v", "omega", "radii", "precone"],
            promotes_outputs=["Vx", "Vy"])

        comp = make_component(
            CCBladeResidualComp(num_nodes=num_nodes,
                                num_radial=num_radial,
                                B=B,
                                af=affunc,
                                turbine=turbine))
        comp.linear_solver = om.DirectSolver(assemble_jac=True)
        prob.model.add_subsystem('ccblade',
                                 comp,
                                 promotes_inputs=[('r', 'radii'), 'chord',
                                                  'theta', 'Vx', 'Vy', 'rho',
                                                  'mu', 'asound',
                                                  ('Rhub', 'hub_radius_eff'),
                                                  ('Rtip', 'prop_radius_eff'),
                                                  'precone', 'pitch'],
                                 promotes_outputs=['Np', 'Tp'])

        prob.setup()
        prob.final_setup()
        prob.run_model()

        Np = prob.get_val('Np', units='N/m')
        Tp = prob.get_val('Tp', units='N/m')
        T = np.sum(Np * dr) * B
        Q = np.sum(r * Tp * dr) * B

        assert_allclose(1223.0506862888788, T, atol=1e-8)
        assert_allclose(113.79919472569034, Q, atol=1e-8)

        assert_allclose(prob.get_val('Np', units='N/m')[0, 3],
                        427.3902632382494,
                        atol=1e-8,
                        rtol=1)
        assert_allclose(prob.get_val('Tp', units='N/m')[0, 3],
                        122.38414345762305,
                        atol=1e-8,
                        rtol=1)
        assert_allclose(prob.get_val('ccblade.a')[0, 3],
                        2.2845512476210943,
                        atol=1e-8,
                        rtol=1)
        assert_allclose(prob.get_val('ccblade.ap')[0, 3],
                        0.05024950801920044,
                        atol=1e-8,
                        rtol=1)
        assert_allclose(prob.get_val('ccblade.u', units='m/s')[0, 3],
                        11.422756238105471,
                        atol=1e-8,
                        rtol=1)
        assert_allclose(prob.get_val('ccblade.v', units='m/s')[0, 3],
                        3.2709314141649575,
                        atol=1e-8,
                        rtol=1)
        assert_allclose(prob.get_val('ccblade.phi', units='rad')[0, 3],
                        0.2596455971546484,
                        atol=1e-8,
                        rtol=1)
        # assert_allclose(prob.get_val('ccblade.alpha', units='rad')[0, 3], 0.23369406105568025, atol=1e-8, rtol=1)
        assert_allclose(prob.get_val('ccblade.W', units='m/s')[0, 3],
                        63.96697566502531,
                        atol=1e-8,
                        rtol=1)
        assert_allclose(prob.get_val('ccblade.cl')[0, 3],
                        1.773534419416163,
                        atol=1e-8,
                        rtol=1)
        assert_allclose(prob.get_val('ccblade.cd')[0, 3],
                        0.03413364011028978,
                        atol=1e-8,
                        rtol=1)
        assert_allclose(prob.get_val('ccblade.cn')[0, 3],
                        1.7053239640124302,
                        atol=1e-8,
                        rtol=1)
        assert_allclose(prob.get_val('ccblade.ct')[0, 3],
                        0.48832327407767123,
                        atol=1e-8,
                        rtol=1)
        assert_allclose(prob.get_val('ccblade.F')[0, 3],
                        1.0,
                        atol=1e-8,
                        rtol=1)
        assert_allclose(prob.get_val('ccblade.G')[0, 3],
                        1.0,
                        atol=1e-8,
                        rtol=1)

        theta = np.arctan(pitch / (2 * np.pi * r)) - 3 * np.pi / 180
        prob.set_val('theta', theta, units='rad')
        prob.run_model()

        Np = prob.get_val('Np', units='N/m')
        Tp = prob.get_val('Tp', units='N/m')
        T = np.sum(Np * dr) * B
        Q = np.sum(r * Tp * dr) * B

        assert_allclose(T, 1e3 * 0.962407923825140, atol=1e-3)
        assert_allclose(Q, 1e2 * 0.813015017103876, atol=1e-4)
Пример #4
0
    def test_propeller_aero4students(self):
        # Copied from CCBlade.jl/test/runtests.jl

        # -------- verification: propellers.  using script at http://www.aerodynamics4students.com/propulsion/blade-element-propeller-theory.php ------
        # I increased their tolerance to 1e-6

        # inputs
        chord = 0.10
        D = 1.6
        RPM = 2100
        rho = 1.225
        pitch = 1.0  # pitch distance in meters.

        # --- rotor definition ---
        turbine = False
        # Rhub = 0.0
        # Rtip = D/2
        Rhub_eff = 1e-6  # something small to eliminate hub effects
        Rtip_eff = 100.0  # something large to eliminate tip effects
        B = 2  # number of blades

        # --- section definitions ---

        num_nodes = 60
        R = D / 2.0
        r = np.linspace(R / 10, R, 11)
        dr = r[1] - r[0]
        r = np.tile(r, (num_nodes, 1))
        theta = np.arctan(pitch / (2 * np.pi * r))

        jlmain.eval("""
                function affunc(alpha, Re, M)
                    cl = 6.2*alpha
                    cd = 0.008 - 0.003*cl + 0.01*cl*cl
                    return cl, cd
                end
            """)
        affunc = jlmain.affunc

        prob = om.Problem()
        num_radial = r.shape[-1]

        comp = om.IndepVarComp()
        comp.add_output('v', val=np.arange(1, 60 + 1)[:num_nodes], units='m/s')
        comp.add_output('omega', val=np.tile(RPM, num_nodes), units='rpm')
        comp.add_output('radii', val=r, units='m')
        comp.add_output('chord',
                        val=chord,
                        shape=(num_nodes, num_radial),
                        units='m')
        comp.add_output('theta',
                        val=theta,
                        shape=(num_nodes, num_radial),
                        units='rad')
        comp.add_output('precone', val=0., shape=num_nodes, units='rad')
        comp.add_output('rho', val=rho, shape=(num_nodes, 1), units='kg/m**3')
        comp.add_output('mu', val=1.0, shape=(num_nodes, 1), units='N/m**2*s')
        comp.add_output('asound', val=1.0, shape=(num_nodes, 1), units='m/s')
        comp.add_output('hub_radius', val=Rhub_eff, shape=num_nodes, units='m')
        comp.add_output('prop_radius',
                        val=Rtip_eff,
                        shape=num_nodes,
                        units='m')
        comp.add_output('pitch', val=0.0, shape=num_nodes, units='rad')
        prob.model.add_subsystem('ivc', comp, promotes_outputs=['*'])

        comp = SimpleInflow(num_nodes=num_nodes, num_radial=num_radial)
        prob.model.add_subsystem(
            "simple_inflow",
            comp,
            promotes_inputs=["v", "omega", "radii", "precone"],
            promotes_outputs=["Vx", "Vy"])

        comp = make_component(
            CCBladeResidualComp(num_nodes=num_nodes,
                                num_radial=num_radial,
                                B=B,
                                af=affunc,
                                turbine=turbine))
        comp.linear_solver = om.DirectSolver(assemble_jac=True)
        prob.model.add_subsystem('ccblade',
                                 comp,
                                 promotes_inputs=[('r', 'radii'), 'chord',
                                                  'theta', 'Vx', 'Vy', 'rho',
                                                  'mu', 'asound',
                                                  ('Rhub', 'hub_radius'),
                                                  ('Rtip', 'prop_radius'),
                                                  'precone', 'pitch'],
                                 promotes_outputs=['Np', 'Tp'])

        prob.setup()
        prob.final_setup()
        prob.run_model()

        Np = prob.get_val('Np', units='N/m')
        Tp = prob.get_val('Tp', units='N/m')
        T = np.sum(Np * dr, axis=1) * B
        Q = np.sum(r * Tp * dr, axis=1) * B

        tsim = 1e3 * np.array([
            1.045361193032356, 1.025630300048415, 1.005234466788998,
            0.984163367036026, 0.962407923825140, 0.939960208707079,
            0.916813564966455, 0.892962691000145, 0.868403981825492,
            0.843134981103815, 0.817154838249790, 0.790463442573673,
            0.763063053839278, 0.734956576558370, 0.706148261507327,
            0.676643975451150, 0.646450304160057, 0.615575090105131,
            0.584027074365864, 0.551815917391907, 0.518952127358381,
            0.485446691671386, 0.451311288662196, 0.416557935286392,
            0.381199277009438, 0.345247916141561, 0.308716772800348,
            0.271618894441869, 0.233967425339051, 0.195775319296371,
            0.157055230270717, 0.117820154495231, 0.078082266879117,
            0.037854059197644, -0.002852754149850, -0.044026182837742,
            -0.085655305814570, -0.127728999394140, -0.170237722799272,
            -0.213169213043848, -0.256515079286031, -0.300266519551194,
            -0.344414094748869, -0.388949215983616, -0.433863576642539,
            -0.479150401337354, -0.524801553114807, -0.570810405128802,
            -0.617169893200684, -0.663873474163182, -0.710915862524620,
            -0.758291877949762, -0.805995685105502, -0.854022273120508,
            -0.902366919041604, -0.951025170820984, -0.999992624287163,
            -1.049265666456123, -1.098840222937414, -1.148712509929845
        ])
        qsim = 1e2 * np.array([
            0.803638686218187, 0.806984572453978, 0.809709290183008,
            0.811743686838315, 0.813015017103876, 0.813446921530685,
            0.812959654049620, 0.811470393912576, 0.808893852696513,
            0.805141916379142, 0.800124489784850, 0.793748780791057,
            0.785921727832179, 0.776548246109426, 0.765532528164390,
            0.752778882688809, 0.738190986274448, 0.721673076180745,
            0.703129918771009, 0.682467282681955, 0.659592296506578,
            0.634413303042323, 0.606840565246423, 0.576786093366321,
            0.544164450503912, 0.508891967461804, 0.470887571011192,
            0.430072787279711, 0.386371788290446, 0.339711042057184,
            0.290019539402947, 0.237229503458026, 0.181274942660876,
            0.122093307308376, 0.059623821454727, -0.006190834182631,
            -0.075406684829235, -0.148076528546541, -0.224253047813501,
            -0.303980950928302, -0.387309291734422, -0.474283793689904,
            -0.564946107631716, -0.659336973911858, -0.757495165410553,
            -0.859460291551374, -0.965266648683888, -1.074949504731187,
            -1.188540970723477, -1.306072104649531, -1.427575034895290,
            -1.553080300508925, -1.682614871422754, -1.816205997296014,
            -1.953879956474228, -2.095662107769925, -2.241576439746701,
            -2.391647474158875, -2.545897099743367, -2.704346566395035
        ])

        assert_allclose(T, tsim[:num_nodes], atol=1e-2, rtol=1)
        assert_allclose(Q, qsim[:num_nodes], atol=2e-3, rtol=1)

        # Run with v = 20.0 m/s.
        prob.set_val('v', 20.0, units='m/s', indices=0)
        prob.run_model()

        Vhub = prob.get_val('v', units='m/s', indices=0)
        omega = prob.get_val('omega', units='rad/s', indices=0)
        Np = prob.get_val('Np', units='N/m', indices=0)
        Tp = prob.get_val('Tp', units='N/m', indices=0)
        T = np.sum(Np * dr) * B
        Q = np.sum(r[0, :] * Tp * dr) * B

        P = Q * omega
        n = omega / (2 * np.pi)
        CT = T / (rho * n**2 * D**4)
        CQ = Q / (rho * n**2 * D**5)
        eff = T * Vhub / P

        assert_allclose(CT, 0.056110238632657, atol=1e-7, rtol=1)
        assert_allclose(CQ, 0.004337202960642, atol=1e-8, rtol=1)
        assert_allclose(eff, 0.735350632777002, atol=1e-6, rtol=1)
Пример #5
0
    def test_multiple_adv_ratios(self):
        # num_nodes = 20
        nstart = 0
        nend = 19
        num_nodes = nend - nstart + 1

        r = 0.0254 * np.array([
            0.7526, 0.7928, 0.8329, 0.8731, 0.9132, 0.9586, 1.0332, 1.1128,
            1.1925, 1.2722, 1.3519, 1.4316, 1.5114, 1.5911, 1.6708, 1.7505,
            1.8302, 1.9099, 1.9896, 2.0693, 2.1490, 2.2287, 2.3084, 2.3881,
            2.4678, 2.5475, 2.6273, 2.7070, 2.7867, 2.8661, 2.9410
        ]).reshape(1, -1)
        num_radial = r.shape[-1]
        r = np.tile(r, (num_nodes, 1))

        chord = 0.0254 * np.array([
            0.6270, 0.6255, 0.6231, 0.6199, 0.6165, 0.6125, 0.6054, 0.5973,
            0.5887, 0.5794, 0.5695, 0.5590, 0.5479, 0.5362, 0.5240, 0.5111,
            0.4977, 0.4836, 0.4689, 0.4537, 0.4379, 0.4214, 0.4044, 0.3867,
            0.3685, 0.3497, 0.3303, 0.3103, 0.2897, 0.2618, 0.1920
        ]).reshape((1, num_radial))
        chord = np.tile(chord, (num_nodes, 1))

        theta = np.pi / 180.0 * np.array([
            40.2273, 38.7657, 37.3913, 36.0981, 34.8803, 33.5899, 31.6400,
            29.7730, 28.0952, 26.5833, 25.2155, 23.9736, 22.8421, 21.8075,
            20.8586, 19.9855, 19.1800, 18.4347, 17.7434, 17.1005, 16.5013,
            15.9417, 15.4179, 14.9266, 14.4650, 14.0306, 13.6210, 13.2343,
            12.8685, 12.5233, 12.2138
        ]).reshape((1, num_radial))
        theta = np.tile(theta, (num_nodes, 1))

        rho = 1.225
        Rhub = 0.0254 * .5
        Rtip = 0.0254 * 3.0
        B = 2  # number of blades
        turbine = False

        J = np.linspace(0.1, 0.9, 20)[nstart:nend + 1]  # advance ratio
        # J = np.linspace(0.1, 0.9, 20)

        omega = 8000.0 * np.pi / 30

        n = omega / (2 * np.pi)
        D = 2 * Rtip

        Vinf = J * D * n
        dr = r[0, 1] - r[0, 0]

        # Airfoil interpolator.
        af = af_from_files(["airfoils/NACA64_A17.dat"])[0]

        prob = om.Problem()

        comp = om.IndepVarComp()
        comp.add_output('v', val=Vinf, units='m/s')
        comp.add_output('omega', val=np.tile(omega, num_nodes), units='rad/s')
        comp.add_output('radii',
                        val=r,
                        shape=(num_nodes, num_radial),
                        units='m')
        comp.add_output('dradii',
                        val=dr,
                        shape=(num_nodes, num_radial),
                        units='m')
        comp.add_output('chord',
                        val=chord,
                        shape=(num_nodes, num_radial),
                        units='m')
        comp.add_output('theta',
                        val=theta,
                        shape=(num_nodes, num_radial),
                        units='rad')
        comp.add_output('precone', val=0., shape=num_nodes, units='rad')
        comp.add_output('rho', val=rho, shape=(num_nodes, 1), units='kg/m**3')
        comp.add_output('mu', val=1.0, shape=(num_nodes, 1), units='N/m**2*s')
        comp.add_output('asound', val=1.0, shape=(num_nodes, 1), units='m/s')
        comp.add_output('hub_radius', val=Rhub, shape=num_nodes, units='m')
        comp.add_output('prop_radius', val=Rtip, shape=num_nodes, units='m')
        comp.add_output('pitch', val=0.0, shape=num_nodes, units='rad')
        prob.model.add_subsystem('ivc', comp, promotes_outputs=['*'])

        comp = SimpleInflow(num_nodes=num_nodes, num_radial=num_radial)
        prob.model.add_subsystem(
            "simple_inflow",
            comp,
            promotes_inputs=["v", "omega", "radii", "precone"],
            promotes_outputs=["Vx", "Vy"])

        comp = ccb.LocalInflowAngleComp(num_nodes=num_nodes,
                                        num_radial=num_radial,
                                        num_blades=B,
                                        airfoil_interp=af,
                                        turbine=turbine,
                                        debug_print=False)
        comp.linear_solver = om.DirectSolver(assemble_jac=True)
        prob.model.add_subsystem('ccblade',
                                 comp,
                                 promotes_inputs=[
                                     'radii', 'chord', 'theta', 'Vx', 'Vy',
                                     'rho', 'mu', 'asound', 'hub_radius',
                                     'prop_radius', 'precone', 'pitch'
                                 ],
                                 promotes_outputs=['Np', 'Tp'])

        comp = ccb.FunctionalsComp(num_nodes=num_nodes,
                                   num_radial=num_radial,
                                   num_blades=B)
        prob.model.add_subsystem(
            'ccblade_torquethrust_comp',
            comp,
            promotes_inputs=[
                'hub_radius', 'prop_radius', 'radii', 'Np', 'Tp', 'v', 'omega'
            ],
            promotes_outputs=['thrust', 'torque', 'efficiency'])

        prob.setup()
        prob.final_setup()
        prob.run_model()

        etatest = np.array([
            0.24598190455626265, 0.3349080300487075, 0.4155652767326253,
            0.48818637673414306, 0.5521115225679999, 0.6089123481436948,
            0.6595727776885079, 0.7046724703349897, 0.7441662053086512,
            0.7788447616541276, 0.8090611349633181, 0.8347808848055981,
            0.8558196582739432, 0.8715046719672315, 0.8791362131978436,
            0.8670633642311274, 0.7974063895510229, 0.2715632768892098, 0.0,
            0.0
        ])[nstart:nend + 1]

        thrust = prob.get_val('thrust', units='N')
        eff = prob.get_val('efficiency')
        assert_array_almost_equal(eff[thrust > 0.0],
                                  etatest[thrust > 0.0],
                                  decimal=2)
Пример #6
0
    def test_propeller_aero4students(self):
        # From test/runtests.jl

        # inputs
        chord = 0.10
        D = 1.6
        RPM = 2100
        rho = 1.225
        pitch = 1.0  # pitch distance in meters.

        # --- rotor definition ---
        turbine = False
        # Rhub = 0.0
        # Rtip = D/2
        Rhub_eff = 1e-6  # something small to eliminate hub effects
        Rtip_eff = 100.0  # something large to eliminate tip effects
        B = 2  # number of blades

        # --- section definitions ---

        num_nodes = 60
        R = D / 2.0
        r = np.linspace(R / 10, R, 11)
        dr = r[1] - r[0]
        r = np.tile(r, (num_nodes, 1))
        # print(f"r =\n{r}")
        dr = np.tile(dr, r.shape)
        theta = np.arctan(pitch / (2 * np.pi * r))

        def affunc(alpha, Re, M):
            cl = 6.2 * alpha
            cd = 0.008 - 0.003 * cl + 0.01 * cl * cl
            return cl, cd

        prob = om.Problem()
        num_radial = r.shape[-1]

        comp = om.IndepVarComp()
        comp.add_output('v', val=np.arange(1, num_nodes + 1), units='m/s')
        comp.add_output('omega', val=np.tile(RPM, num_nodes), units='rpm')
        comp.add_output('radii', val=r, units='m')
        comp.add_output('dradii', val=dr, units='m')
        comp.add_output('chord',
                        val=chord,
                        shape=(num_nodes, num_radial),
                        units='m')
        comp.add_output('theta',
                        val=theta,
                        shape=(num_nodes, num_radial),
                        units='rad')
        comp.add_output('precone', val=0., units='rad')
        comp.add_output('rho', val=rho, shape=(num_nodes, 1), units='kg/m**3')
        comp.add_output('mu', val=1.0, shape=(num_nodes, 1), units='N/m**2*s')
        comp.add_output('asound', val=1.0, shape=(num_nodes, 1), units='m/s')
        comp.add_output('hub_radius', val=Rhub_eff, units='m')
        comp.add_output('prop_radius', val=Rtip_eff, units='m')
        comp.add_discrete_output('B', val=B)
        prob.model.add_subsystem('ivc', comp, promotes_outputs=['*'])

        comp = SimpleInflow(num_nodes=num_nodes, num_radial=num_radial)
        prob.model.add_subsystem(
            "simple_inflow",
            comp,
            promotes_inputs=["v", "omega", "radii", "precone"],
            promotes_outputs=["Vx", "Vy"])

        comp = ccb.LocalInflowAngleComp(num_nodes=num_nodes,
                                        num_radial=num_radial,
                                        airfoil_interp=affunc,
                                        turbine=turbine,
                                        debug_print=False)
        comp.linear_solver = om.DirectSolver(assemble_jac=True)
        prob.model.add_subsystem('ccblade',
                                 comp,
                                 promotes_inputs=[
                                     'radii', 'chord', 'theta', 'Vx', 'Vy',
                                     'rho', 'mu', 'asound', 'hub_radius',
                                     'prop_radius', 'precone', 'B'
                                 ],
                                 promotes_outputs=['Np', 'Tp'])

        comp = ccb.FunctionalsComp(num_nodes=num_nodes, num_radial=num_radial)
        prob.model.add_subsystem(
            'ccblade_torquethrust_comp',
            comp,
            promotes_inputs=['radii', 'dradii', 'Np', 'Tp', 'v', 'omega', 'B'],
            promotes_outputs=['thrust', 'torque'])

        prob.setup()
        prob.final_setup()
        prob.run_model()

        tsim = 1e3 * np.array([
            1.045361193032356, 1.025630300048415, 1.005234466788998,
            0.984163367036026, 0.962407923825140, 0.939960208707079,
            0.916813564966455, 0.892962691000145, 0.868403981825492,
            0.843134981103815, 0.817154838249790, 0.790463442573673,
            0.763063053839278, 0.734956576558370, 0.706148261507327,
            0.676643975451150, 0.646450304160057, 0.615575090105131,
            0.584027074365864, 0.551815917391907, 0.518952127358381,
            0.485446691671386, 0.451311288662196, 0.416557935286392,
            0.381199277009438, 0.345247916141561, 0.308716772800348,
            0.271618894441869, 0.233967425339051, 0.195775319296371,
            0.157055230270717, 0.117820154495231, 0.078082266879117,
            0.037854059197644, -0.002852754149850, -0.044026182837742,
            -0.085655305814570, -0.127728999394140, -0.170237722799272,
            -0.213169213043848, -0.256515079286031, -0.300266519551194,
            -0.344414094748869, -0.388949215983616, -0.433863576642539,
            -0.479150401337354, -0.524801553114807, -0.570810405128802,
            -0.617169893200684, -0.663873474163182, -0.710915862524620,
            -0.758291877949762, -0.805995685105502, -0.854022273120508,
            -0.902366919041604, -0.951025170820984, -0.999992624287163,
            -1.049265666456123, -1.098840222937414, -1.148712509929845
        ])
        qsim = 1e2 * np.array([
            0.803638686218187, 0.806984572453978, 0.809709290183008,
            0.811743686838315, 0.813015017103876, 0.813446921530685,
            0.812959654049620, 0.811470393912576, 0.808893852696513,
            0.805141916379142, 0.800124489784850, 0.793748780791057,
            0.785921727832179, 0.776548246109426, 0.765532528164390,
            0.752778882688809, 0.738190986274448, 0.721673076180745,
            0.703129918771009, 0.682467282681955, 0.659592296506578,
            0.634413303042323, 0.606840565246423, 0.576786093366321,
            0.544164450503912, 0.508891967461804, 0.470887571011192,
            0.430072787279711, 0.386371788290446, 0.339711042057184,
            0.290019539402947, 0.237229503458026, 0.181274942660876,
            0.122093307308376, 0.059623821454727, -0.006190834182631,
            -0.075406684829235, -0.148076528546541, -0.224253047813501,
            -0.303980950928302, -0.387309291734422, -0.474283793689904,
            -0.564946107631716, -0.659336973911858, -0.757495165410553,
            -0.859460291551374, -0.965266648683888, -1.074949504731187,
            -1.188540970723477, -1.306072104649531, -1.427575034895290,
            -1.553080300508925, -1.682614871422754, -1.816205997296014,
            -1.953879956474228, -2.095662107769925, -2.241576439746701,
            -2.391647474158875, -2.545897099743367, -2.704346566395035
        ])

        assert_rel_error(self, tsim, prob['thrust'], 1e-5)
        assert_rel_error(self, qsim, prob['torque'], 1e-5)