Exemple #1
0
def testBladeOuterScalar():
    # scalar to blade/ scalar to scalar
    B1 = Blade(np.eye(2))
    B2 = Blade(1, s=2)

    result1 = bd.outer(B1, B2)
    print 'B1 outer B2 s: ', result1.s
    assert np.allclose(2, result1.s), 'value'
    assert result1.blade.shape == (2, 2), 'shape'

    result2 = bd.outer(B2, B1)
    print 'B2 outer B1 s: ', result2.s
    assert np.allclose(2, result2.s), 'value'
    assert result2.blade.shape == (2, 2), 'shape'

    result3 = bd.outer(B2, B2)
    print 'B2 outer B2 s: ', result3.s
    assert np.allclose(result3.s, 4)

    B1 = Blade(1, s=2)
    B2 = Blade(1, s=-.5)

    result1 = bd.outer(B1, B2)
    print 'B1 outer B2 s: ', result1.s
    assert np.allclose(-1, result1.s), 'value'
    assert result1.blade.shape == (1, 0), 'shape'
Exemple #2
0
def testBladeOuterScaleMultplicative():
    # multiplicative scaling
    B1 = Blade(np.array([2, 0]))
    B2 = Blade(np.array([0, 1]))
    result1 = bd.outer(B1, B2)
    print 'B1 outer B2 s: ', result1.s
    print 'B1 outer B2 blade: ', result1.blade
    assert la.det(result1.blade) > 0, la.det(result1.blade)
    assert result1.blade.shape == (2, 2), result1.blade.shape
    assert np.allclose(np.eye(2), np.dot(result1.blade.T, result1.blade))
    assert np.allclose(bd.inner(result1, result1).s,
                       -4), bd.inner(result1, result1).s

    result2 = bd.outer(B2, B1)
    print 'B2 outer B1 s: ', result2.s
    print 'B2 outer B1 blade: ', result2.blade
    assert la.det(result2.blade) < 0, la.det(result2.blade)
    assert result2.blade.shape == (2, 2), result2.blade.shape
    assert np.allclose(np.eye(2), np.dot(result2.blade.T, result2.blade))
    assert np.allclose(bd.inner(result2, result2).s,
                       -4), bd.inner(result2, result2).s

    # result1 == inverse(result2)
    assert np.allclose(bd.inner(result1, result2).s,
                       4), bd.inner(result1, result2).s
Exemple #3
0
def testBladeInitScalar():
    " Testing scalar blade "
    B = Blade(1)
    assert B.blade.shape == (1, 0)
    assert np.allclose(B.s, 1)
    assert B.k == 0

    B = Blade(1, s=2)
    assert B.blade.shape == (1, 0)
    assert np.allclose(B.s, 2)
    assert B.k == 0

    B = Blade(2)
    assert B.blade.shape == (1, 0)
    assert np.allclose(B.s, 2)
    assert B.k == 0

    B = Blade(2, s=2)
    assert B.blade.shape == (1, 0)
    assert np.allclose(B.s, 4)
    assert B.k == 0

    B = Blade(1, s=0)
    assert B.blade.shape == (1, 0)
    assert np.allclose(B.s, 0)
    assert B.k == 0
Exemple #4
0
def testBladeDualEuclidean():
    # s=0
    B = Blade(0)
    try:
        D = bd.dual(B)
    except AttributeError as err:
        assert err[0] == 'Not dualizable, s=0'
    except:
        assert False, err

    # scalar, expect a 2-blade
    A = Blade(2)
    D = bd.dual(A, n=2)
    revPseudo = bd.outer(bd.inverse(A), D)
    shouldBeOne = bd.inner(revPseudo, bd.pseudoScalar(2))
    assert np.allclose(shouldBeOne.s, 1.0), (shouldBeOne.blade, shouldBeOne.s)

    # 1-blade, expect 1-blade back
    A = Blade(np.array([1, 0]))
    D = bd.dual(A)
    revPseudo = bd.outer(bd.inverse(A), D)
    shouldBeOne = bd.inner(revPseudo, bd.pseudoScalar(2))
    assert np.allclose(shouldBeOne.s, 1.0), (shouldBeOne.blade, shouldBeOne.s)

    # 2-blade, expect 0-blade back
    A = Blade(np.eye(2))
    D = bd.dual(A)
    revPseudo = bd.outer(bd.inverse(A), D)
    shouldBeOne = bd.inner(revPseudo, bd.pseudoScalar(2))
    assert np.allclose(shouldBeOne.s, 1.0), (shouldBeOne.blade, shouldBeOne.s)
Exemple #5
0
def testBladeUnDualEuclidean():
    # s=0
    B = Blade(0)
    try:
        D = bd.undual(B)
    except AttributeError as err:
        assert err[0] == 'Not dualizable, s=0'
    except:
        assert False, err

    # scalar, expect a 2-blade
    A = Blade(2)
    Aprime = bd.dual(bd.undual(A, n=2), n=2)
    print 'A: ', A.blade, A.s
    print 'APrimeInv: ', bd.inverse(Aprime).blade, bd.inverse(Aprime).s
    shouldBeOne = bd.outer(A, bd.inverse(Aprime))
    print 'shouldBeOne: ', shouldBeOne.blade, shouldBeOne.s
    assert np.allclose(shouldBeOne.s, 1.0), (shouldBeOne.blade, shouldBeOne.s)

    # 1-blade, expect 1-blade back
    A = Blade(np.array([1, 0]))
    Aprime = bd.undual(bd.dual(A, n=2), n=2)
    shouldBeOne = bd.inner(A, bd.inverse(Aprime))
    assert np.allclose(shouldBeOne.s, 1.0), (shouldBeOne.blade, shouldBeOne.s)

    # 2-blade, expect 0-blade back
    A = Blade(np.eye(2))
    Aprime = bd.undual(bd.dual(A, n=2), n=2)
    print 'A: ', A.blade, A.s
    print 'APrimeInv: ', bd.inverse(Aprime).blade, bd.inverse(Aprime).s
    shouldBeOne = bd.inner(A, bd.inverse(Aprime))
    print 'shouldBeOne: ', shouldBeOne.blade, shouldBeOne.s
    assert np.allclose(shouldBeOne.s, 1.0), (shouldBeOne.blade, shouldBeOne.s)
Exemple #6
0
def testBladeCopy():
    B1 = Blade(np.eye(2))
    B2 = bd.copy(B1)
    assert np.allclose(B1.blade, B2.blade)
    assert np.allclose(B1.s, B2.s)

    B1.blade[1, 1] = 2
    assert not np.allclose(B1.blade, B2.blade)
Exemple #7
0
def testBladeEqualityNotSameSubspace():
    # 45-degree rotation in plane x-y
    A = Blade(np.array([[1, 0], [0, 1], [1, 0]]))
    B = Blade(np.array([[1, 1], [-1, 1], [0, 0]]))
    print 'join dim: ', bd.join(A, B).k
    print 'equal: ', bd.equality(A, B)
    assert not bd.subSpaceEquality(A, B)
    assert not bd.equality(A, B)
Exemple #8
0
def testBladeCopy():
    B1 = Blade(np.eye(2))
    B2 = bd.copy(B1)
    assert np.allclose(B1.blade, B2.blade)
    assert np.allclose(B1.s, B2.s)

    B1.blade[1, 1] = 2
    assert not np.allclose(B1.blade, B2.blade)
Exemple #9
0
def testBladeLeftContractOrthoProj():
    # orthogonal projection
    A = Blade(np.array([1, 1, 0]))
    B = Blade(np.array([[2, 0], [0, 0], [0, 2]]))
    Aproj = bd.leftContract(bd.leftContract(A, B), bd.inverse(B))
    assert (np.allclose(Aproj.blade, np.array([[1], [0], [0]])) and \
                   np.allclose(Aproj.s * la.norm(np.array([1, 1, 0])), A.s)) or \
           (np.allclose(-Aproj.blade, np.array([[-1], [0], [0]])) and \
                   np.allclose(-Aproj.s * la.norm(np.array([1, 1, 0])), A.s))
Exemple #10
0
def testBladeInnerCommutativity():
    # commutative
    for i in range(1, 5):
        B1 = Blade(np.eye(i, i))
        B2 = Blade(-np.eye(i, i))
        firstOrdering = bd.inner(B1, B2)
        secondOrdering = bd.inner(B2, B1)
        assert np.allclose(firstOrdering.s,
                           secondOrdering.s), (firstOrdering.s,
                                               secondOrdering.s)
Exemple #11
0
def testBladeLeftContractBasic():
    # basic usage
    A = Blade(np.array([1, 0]))
    B = Blade(np.array([[1, 0], [0, 1]]))
    lc = bd.leftContract(A, B)
    assert np.allclose(lc.s, 1.0), (lc.blade, lc.s)
    assert (np.allclose(lc.blade, np.array([[0], [1]])) and \
                   np.allclose(lc.s, 1)) or \
           (np.allclose(lc.blade, np.array([[0], [-1]])) and \
                   np.allclose(lc.s, -1)), (lc.blade, lc.s)
Exemple #12
0
def testBladeInitQrScaling():
    # testing qr scaling
    A = np.array([[2, 0], [0, 1], [0, 0]])
    B = Blade(A)
    assert np.allclose(np.dot(B.blade.T, B.blade), np.eye(2))
    assert np.allclose(B.s, 2.0)

    B = Blade(A, s=2.0)
    assert np.allclose(np.dot(B.blade.T, B.blade), np.eye(2))
    assert np.allclose(B.s, 4.0)
Exemple #13
0
def testBladeJoinBasic():
    # two bases
    A = Blade(np.array([1, 0, 0]))
    B = Blade(np.array([0, 1, 0]))
    J = bd.join(A, B)
    assert J.blade.shape == (3, 2)
    assert J.s == 1

    # add a third
    C = Blade(np.array([0, 0, 1]))
    J2 = bd.join(J, C)
    assert J2.blade.shape == (3, 3)
    assert J2.s == 1

    # linearly dependent
    A = Blade(np.array([1, 0, 0]))
    B = Blade(np.array([1, 0, 0]))
    J = bd.join(A, B)
    assert J.blade.shape == (3, 1)
    assert J.s == 1

    # linearly dependent
    A = Blade(np.array([1, 0, 0]))
    B = Blade(np.array([0, 1, 0]))
    J = bd.join(A, B)
    C = Blade(np.array([1, 1, 0]))
    D = Blade(np.array([1, -1, 2]))
    J2 = bd.join(C, D)
    J3 = bd.join(J, J2)
    assert J3.blade.shape == (3, 3)
    assert J3.s == 1
Exemple #14
0
def testBladeInitNull():
    " Testing null blade "
    B = Blade()
    assert B.blade.shape == (1, 0)
    assert np.allclose(B.s, 0)
    assert B.k == 0

    B = Blade(s=0)
    assert B.blade.shape == (1, 0)
    assert np.allclose(B.s, 0)
    assert B.k == 0
Exemple #15
0
def testBladeInitQr():
    " Testing qr "
    # testing qr
    A = np.array([[1, 0], [0, 1], [0, 0]])
    B = Blade(A)
    print 'B.blade: ', B.blade
    assert np.allclose(np.dot(B.blade.T, B.blade), np.eye(2))
    assert np.allclose(B.s, 1)

    B = Blade(A, s=2.0)
    assert np.allclose(np.dot(B.blade.T, B.blade), np.eye(2))
    assert np.allclose(B.s, 2.0)
Exemple #16
0
def testBladeInvolutio():
    # sign
    B = Blade(1)
    assert np.allclose(B.s, (-1)**B.k * bd.involution(B).s)
    assert np.allclose(B.s, (-1)**B.k * bd.involutionScaling(B))
    assert B.k == 0

    for i in range(1, 5):
        B = Blade(np.eye(i))
        assert np.allclose(B.s, (-1)**B.k * bd.involution(B).s)
        assert np.allclose(B.s, (-1)**B.k * bd.involutionScaling(B))
        assert B.k == i
Exemple #17
0
def testBladeEqualityAndSubEqualSelf():
    # self equal
    A = Blade(np.array([[1, 2], [2, 3], [0, 0]]))
    B = Blade(np.array([[1, 2], [2, 3], [0, 0]]))
    assert bd.subSpaceEquality(A, B)
    assert bd.equality(A, B)

    # self equal
    A = Blade(np.array([[1, 2], [2, 3], [0, 0]]))
    B = Blade(2 * np.array([[1, 2], [2, 3], [0, 0]]), s=.25)
    assert bd.subSpaceEquality(A, B)
    assert bd.equality(A, B)
Exemple #18
0
def testBladeEqualityRotation():
    # 90-degree rotation in plane x-y
    A = Blade(np.array([[1, 0], [0, 1], [0, 0]]))
    B = Blade(np.array([[0, 1], [-1, 0], [0, 0]]))
    assert bd.subSpaceEquality(A, B)
    assert bd.equality(A, B)

    # with scaling now
    A = Blade(np.array([[1, 0], [0, 1], [0, 0]]))
    B = Blade(np.array([[0, 1], [-1, 0], [0, 0]]), s=2)
    assert bd.subSpaceEquality(A, B)
    assert not bd.equality(A, B)
Exemple #19
0
def testBladeOuterSameSubspace():
    B1 = Blade(np.eye(2))
    B2 = Blade(np.eye(2)[:, 0])
    result = bd.outer(B1, B2)
    print 'B1 outer B2: ', result.s
    assert np.allclose(0, result.s)

    B1 = Blade(np.eye(2))
    B2 = Blade(np.eye(2))
    result = bd.outer(B1, B2)
    print 'B1 outer B2: ', result.s
    assert np.allclose(0, result.s)
Exemple #20
0
def testBladeReverse():
    # sign
    B = Blade(1)
    assert np.allclose(B.s, (-1)**(B.k * (B.k - 1) / 2) * bd.reverse(B).s)
    assert np.allclose(B.s, (-1)**(B.k * (B.k - 1) / 2) * bd.reverseScaling(B))
    assert B.k == 0

    for i in range(1, 5):
        B = Blade(np.eye(i))
        assert np.allclose(B.s, (-1)**(B.k * (B.k - 1) / 2) * bd.reverse(B).s)
        assert np.allclose(B.s,
                           (-1)**(B.k * (B.k - 1) / 2) * bd.reverseScaling(B))
        assert B.k == i
Exemple #21
0
def testBladeInnerScale():
    # scale
    A = np.array([[2, 0], [0, 1]])
    print 'A.T A : ', np.dot(A.T, A)

    B = Blade(A)
    print 'B inner B: ', bd.inner(B, B).s
    assert np.allclose(-4, bd.inner(B, B).s)

    A = np.array([[-2, 0], [0, 1]])
    print 'A.T A : ', np.dot(A.T, A)

    B = Blade(A)
    print 'B inner B: ', bd.inner(B, B).s
    assert np.allclose(-4, bd.inner(B, B).s)
Exemple #22
0
def testBladeInitOneIndexVector():
    # testing one-index (row-ish) vector
    c = np.array([2, 0, 0])
    B = Blade(c)
    assert np.allclose(np.dot(B.blade.T, B.blade), np.eye(1))
    assert np.allclose(B.s, 2.0)
    assert B.k == 1
Exemple #23
0
def testBladeInitOrthonormal():
    " Testing orthonormal "
    # testing orthonormal
    A = np.array([[1, 0], [0, 1], [0, 0]])
    B = Blade(A, orthonormal=True)
    assert np.allclose(B.blade, A)
    assert np.allclose(B.s, 1.0)

    B = Blade(A, orthonormal=True, s=2.0)
    assert np.allclose(B.blade, A)
    assert np.allclose(B.s, 2.0)

    B = Blade(A, orthonormal=True, s=0.0)
    assert B.blade.shape == (1, 0)
    assert np.allclose(B.s, 0)
    assert B.k == 0
Exemple #24
0
def testBladeInitColumnVector():
    # testing column vector
    c = np.array([[2], [0], [0]])
    B = Blade(c)
    print 's: ', B.s
    print 'blade: ', B.blade
    assert np.allclose(np.dot(B.blade.T, B.blade), np.eye(1))
    assert np.allclose(B.s, 2.0)
    assert B.k == 1

    B = Blade(c, s=2.0)
    print 's: ', B.s
    print 'blade: ', B.blade
    assert np.allclose(np.dot(B.blade.T, B.blade), np.eye(1))
    assert np.allclose(B.s, 4.0)
    assert B.k == 1
Exemple #25
0
    def start(self):
        logger.info("Starting Tryptofan...")
        self.opc_client = opc.Client('localhost:7890')

        apps = [
            (r'/ws', handlers.ControllerHandler, {
                'tryptofan': self
            }),
            (r'/(.*)', handlers.IndexStaticFileHandler, {
                'path': settings.WEBROOT
            }),
        ]
        application = Application(apps)
        self.http_server = HTTPServer(application)

        self.updater = PeriodicCallback(self.update, 1000 / settings.FRAMERATE)

        for i in range(settings.BLADES):
            blade = Blade(self.pixels, i * settings.BLADE_PIXELS,
                          self.state[i][0], self.state[i][1], self.state[i][2])
            self.blades.append(blade)

        self.http_server.listen(settings.HTTP_PORT)
        self.updater.start()
        IOLoop.instance().start()
Exemple #26
0
def testBladeLinearTransformScale():
    # scale
    B = Blade(np.eye(2), s=2)
    T = np.array([[2, 0], [0, .75]])
    result = bd.applyLinearTransform(T, B)
    assert result.k == 2, (result.blade, result.s)
    assert result.s == 3, (result.blade, result.s)
Exemple #27
0
def testBladeLinearTransformUnit():
    # unit transform
    B = Blade(np.eye(2), s=2)
    T = np.array([[1, 0], [0, 1]])
    result = bd.applyLinearTransform(T, B)
    assert result.k == 2, (result.blade, result.s)
    assert result.s == 2, (result.blade, result.s)
Exemple #28
0
def testBladeInnerSign():
    # sign
    for i in range(1, 5):
        B = Blade(np.eye(i, i))
        print 'B inner B: ', bd.inner(B, B).s
        assert np.allclose((-1)**((i * (i - 1)) / 2),
                           bd.inner(B, B).s), 'value'
        assert bd.inner(B, B).blade.shape == (1, 0), 'shape'
Exemple #29
0
def testBladeJoinScalar():
    # scalars
    A = Blade(1, s=2)
    B = Blade(1, s=4)
    J = bd.join(A, B)
    assert J.blade.shape == (1, 0)
    assert J.s == 1

    # scalar/blade
    B = Blade(np.array([1, 0, 1]))
    J = bd.join(A, B)
    assert J.blade.shape == (3, 1)
    assert J.s == 1

    B = Blade(np.array([1, 0, 1]))
    J = bd.join(B, A)
    assert J.blade.shape == (3, 1)
    assert J.s == 1
Exemple #30
0
    def __init__(self, bladed_file, root_length=0, rigid=False):
        # Modal element using data from Bladed model
        print "Loading modes from '%s'..." % bladed_file
        self.blade = Blade(bladed_file)
        self.tower = Tower(bladed_file)
        self.modes = self.blade.modal_rep()
        #self.tmodes = self.tower.modal_rep()

        Ry = rotmat_y(-pi/2)
        Rhb1 = rotmat_x(0 * 2*pi/3)
        Rhb2 = rotmat_x(1 * 2*pi/3)
        Rhb3 = rotmat_x(2 * 2*pi/3)

        self.base = FreeJoint('base')
        self.towerlink = RigidConnection('tower', [0,0,self.tower.hubheight])
        self.bearing = Hinge('bearing', [1,0,0])
        root1 = RigidConnection('root1', root_length*np.dot(Rhb1,[0,0,1]), dot(Rhb1,Ry))
        root2 = RigidConnection('root2', root_length*np.dot(Rhb2,[0,0,1]), dot(Rhb2,Ry))
        root3 = RigidConnection('root3', root_length*np.dot(Rhb3,[0,0,1]), dot(Rhb3,Ry))
        self.blade1 = ModalElement('blade1', self.modes)
        self.blade2 = ModalElement('blade2', self.modes)
        self.blade3 = ModalElement('blade3', self.modes)
        
        self.base.add_leaf(self.towerlink)
        self.towerlink.add_leaf(self.bearing)
        self.bearing.add_leaf(root1)
        self.bearing.add_leaf(root2)
        self.bearing.add_leaf(root3)
        root1.add_leaf(self.blade1)
        root2.add_leaf(self.blade2)
        root3.add_leaf(self.blade3)
        self.system = System(self.base)

        # Prescribed DOF accelerations - constant rotor speed
        self.base_motion = None
        self.base_motion_amp = 0
        self.system.prescribe(self.bearing, vel=0)
        self.system.prescribe(self.base, vel=0)
        if rigid:
            for b in (self.blade1, self.blade2, self.blade3):
                self.system.prescribe(b, vel=0)
        
        # setup integrator
        self.integ = Integrator(self.system, ('pos','vel','acc'))
        self.integ.add_output(dynamics.LoadOutput(self.base.iprox))
        self.integ.add_output(dynamics.LoadOutput(self.towerlink.iprox))
        self.integ.add_output(dynamics.LoadOutput(self.bearing.iprox))
        for b in (self.blade1, self.blade2, self.blade3):
            self.integ.add_output(dynamics.LoadOutput(b.iprox, local=True))
        for b in (self.blade1, self.blade2, self.blade3):
            self.integ.add_output(b.output_deflections())
        for b in (self.blade1, self.blade2, self.blade3):
            self.integ.add_output(b.output_positions())
        for b in (self.blade1, self.blade2, self.blade3):
            self.integ.add_output(dynamics.NodeOutput(b.iprox, local=True, deriv=2))
Exemple #31
0
def testBladeInitRankDeficient():
    # testing rank deficient
    A = np.array([[1, 0], [1, 0], [0, 0]])
    B = Blade(A)
    print B.blade.shape
    assert B.blade.shape == (1, 0)
    assert np.allclose(B.s, 0)
    assert B.k == 0

    B = Blade(A, s=2.0)
    assert B.blade.shape == (1, 0)
    assert np.allclose(B.s, 0)
    assert B.k == 0

    # too many columns
    C = np.array([[1, 0, 1], [0, 1, 0]])
    B2 = Blade(C)
    assert B2.blade.shape == (1, 0)
    assert np.allclose(B2.s, 0)
    assert B2.k == 0
Exemple #32
0
def testBladeDualEuclideanRegression():
    # regression test -- tests a bunch of functions and they all have to work
    # for many input dimensions for this to succeed fully
    for n in range(1, 10):
        for k in range(0, n + 1):
            if k == 0:
                blade = Blade(1, s=float(rn.rand(1)[0]))
            else:
                blade = Blade(rn.randn(n, k))
            print 'blade n, k: ', blade.n, blade.k
            if np.allclose(blade.s, 0):  # just skip this one for now...
                continue  # measure 0 event but could happen
            D = bd.dual(blade, n=n)
            print 'D n, k: ', D.n, D.k
            revPseudo = bd.outer(bd.inverse(blade), D)
            print 'n, k: ', n, k
            print 'revPseudo blade, s: ', revPseudo.blade, revPseudo.s
            shouldBeOne = bd.inner(revPseudo, bd.pseudoScalar(n))
            assert np.allclose(shouldBeOne.s,
                               1.0), (shouldBeOne.blade, shouldBeOne.s)
    def __init__(self, mode_source_file, root_length=0, wind_table=None):
        # Modal element using data from Bladed model
        print "Loading modes from '%s'..." % mode_source_file
        self.blade = Blade(mode_source_file)
        self.modes = self.blade.modal_rep()
        
        if wind_table is not None:
            loading = BladeLoading(self.blade, wind_table, None)
        else:
            loading = None

        Ry = rotmat_y(-pi/2)
        Rhb1 = rotmat_x(0 * 2*pi/3)
        Rhb2 = rotmat_x(1 * 2*pi/3)
        Rhb3 = rotmat_x(2 * 2*pi/3)

        self.bearing = Hinge('bearing', [1,0,0])
        root1 = RigidConnection('root1', root_length*np.dot(Rhb1,[0,0,1]), dot(Rhb1,Ry))
        root2 = RigidConnection('root2', root_length*np.dot(Rhb2,[0,0,1]), dot(Rhb2,Ry))
        root3 = RigidConnection('root3', root_length*np.dot(Rhb3,[0,0,1]), dot(Rhb3,Ry))
        self.blade1 = ModalElement('blade1', self.modes, loading)
        self.blade2 = ModalElement('blade2', self.modes)
        self.blade3 = ModalElement('blade3', self.modes)
        
        self.bearing.add_leaf(root1)
        self.bearing.add_leaf(root2)
        self.bearing.add_leaf(root3)
        root1.add_leaf(self.blade1)
        root2.add_leaf(self.blade2)
        root3.add_leaf(self.blade3)
        self.system = System(self.bearing)

        # Prescribed DOF accelerations - constant rotor speed
        self.system.prescribe(self.bearing, vel=0.0, acc=0.0)
        
        # setup integrator
        self.integ = Integrator(self.system, ('pos','vel'))
        self.integ.add_output(dynamics.LoadOutput(self.bearing.iprox))
        self.integ.add_output(dynamics.LoadOutput(self.blade1.iprox, local=True))
        self.integ.add_output(self.blade1.output_deflections())
        self.integ.add_output(self.blade1.output_positions())
class Rotor(object):
    def __init__(self, mode_source_file, root_length=0):
        # Modal element using data from Bladed model
        print "Loading modes from '%s'..." % mode_source_file
        self.blade = Blade(mode_source_file)
        self.modes = self.blade.modal_rep()

        Ry = rotmat_y(-pi/2)
        Rhb1 = rotmat_x(0 * 2*pi/3)
        Rhb2 = rotmat_x(1 * 2*pi/3)
        Rhb3 = rotmat_x(2 * 2*pi/3)

        self.bearing = Hinge('bearing', [1,0,0])
        root1 = RigidConnection('root1', root_length*np.dot(Rhb1,[0,0,1]), dot(Rhb1,Ry))
        root2 = RigidConnection('root2', root_length*np.dot(Rhb2,[0,0,1]), dot(Rhb2,Ry))
        root3 = RigidConnection('root3', root_length*np.dot(Rhb3,[0,0,1]), dot(Rhb3,Ry))
        self.blade1 = ModalElement('blade1', self.modes)
        self.blade2 = ModalElement('blade2', self.modes)
        self.blade3 = ModalElement('blade3', self.modes)
        
        self.bearing.add_leaf(root1)
        self.bearing.add_leaf(root2)
        self.bearing.add_leaf(root3)
        root1.add_leaf(self.blade1)
        root2.add_leaf(self.blade2)
        root3.add_leaf(self.blade3)
        self.system = System(self.bearing)

        # Prescribed DOF accelerations - constant rotor speed
        self.system.prescribe(self.bearing, acc=0.0)
        
        # setup integrator
        self.integ = Integrator(self.system, ('pos','vel'))
        self.integ.add_output(dynamics.LoadOutput(self.bearing.iprox))
        self.integ.add_output(dynamics.LoadOutput(self.blade1.iprox, local=True))
        self.integ.add_output(self.blade1.output_deflections())
        self.integ.add_output(self.blade1.output_positions())
        
    def simulate(self, qm0=None, spin=10.0, t1=1.5, dt=0.01):
        # reset
        self.system.q [:] = 0.0
        self.system.qd[:] = 0.0

        # initial conditions
        if qm0 is not None:  # initial modal amplitudes
            self.system.q[self.blade1.istrain] = qm0
            self.system.q[self.blade2.istrain] = qm0
            self.system.q[self.blade3.istrain] = qm0
        self.system.qd[self.bearing.istrain][0] = spin
        
        # simulate
        self.t,self.y = self.integ.integrate(t1, dt)
        for i,lab in enumerate(self.integ.labels()):
            print "%2d  %s" % (i,lab)
        return self.t, self.y
    
    def lin(self, qm0=None, spin=10.0):
        # reset
        self.system.q [:] = 0.0
        self.system.qd[:] = 0.0

        # initial conditions
        if qm0 is not None:  # initial modal amplitudes
            self.system.q[self.blade1.istrain] = qm0
            self.system.q[self.blade2.istrain] = qm0
            self.system.q[self.blade3.istrain] = qm0
        else:
            qm0 = np.zeros(self.blade1._nstrain * 3)
        self.system.prescribe(self.bearing, vel=spin, acc=0.0)
        self.system.qd[self.bearing.istrain][0] = spin
        
        linsys = linearisation.LinearisedSystem(self.system, qm0)
        return linsys

    def ani(self, t=None, y=None, planview=True):
        if t is None: t = self.t
        if y is None: y = self.y
        l = 40
        if planview:
            return dynvis.anim(self.system, t, y, (1,2), (-l,l), (-l,l))
        else:
            return dynvis.anim(self.system, t, y, (0,1), (-l,l), (-5,5))
Exemple #35
0
class Turbine(object):
    def __init__(self, bladed_file, root_length=0, rigid=False):
        # Modal element using data from Bladed model
        print "Loading modes from '%s'..." % bladed_file
        self.blade = Blade(bladed_file)
        self.tower = Tower(bladed_file)
        self.modes = self.blade.modal_rep()
        #self.tmodes = self.tower.modal_rep()

        Ry = rotmat_y(-pi/2)
        Rhb1 = rotmat_x(0 * 2*pi/3)
        Rhb2 = rotmat_x(1 * 2*pi/3)
        Rhb3 = rotmat_x(2 * 2*pi/3)

        self.base = FreeJoint('base')
        self.towerlink = RigidConnection('tower', [0,0,self.tower.hubheight])
        self.bearing = Hinge('bearing', [1,0,0])
        root1 = RigidConnection('root1', root_length*np.dot(Rhb1,[0,0,1]), dot(Rhb1,Ry))
        root2 = RigidConnection('root2', root_length*np.dot(Rhb2,[0,0,1]), dot(Rhb2,Ry))
        root3 = RigidConnection('root3', root_length*np.dot(Rhb3,[0,0,1]), dot(Rhb3,Ry))
        self.blade1 = ModalElement('blade1', self.modes)
        self.blade2 = ModalElement('blade2', self.modes)
        self.blade3 = ModalElement('blade3', self.modes)
        
        self.base.add_leaf(self.towerlink)
        self.towerlink.add_leaf(self.bearing)
        self.bearing.add_leaf(root1)
        self.bearing.add_leaf(root2)
        self.bearing.add_leaf(root3)
        root1.add_leaf(self.blade1)
        root2.add_leaf(self.blade2)
        root3.add_leaf(self.blade3)
        self.system = System(self.base)

        # Prescribed DOF accelerations - constant rotor speed
        self.base_motion = None
        self.base_motion_amp = 0
        self.system.prescribe(self.bearing, vel=0)
        self.system.prescribe(self.base, vel=0)
        if rigid:
            for b in (self.blade1, self.blade2, self.blade3):
                self.system.prescribe(b, vel=0)
        
        # setup integrator
        self.integ = Integrator(self.system, ('pos','vel','acc'))
        self.integ.add_output(dynamics.LoadOutput(self.base.iprox))
        self.integ.add_output(dynamics.LoadOutput(self.towerlink.iprox))
        self.integ.add_output(dynamics.LoadOutput(self.bearing.iprox))
        for b in (self.blade1, self.blade2, self.blade3):
            self.integ.add_output(dynamics.LoadOutput(b.iprox, local=True))
        for b in (self.blade1, self.blade2, self.blade3):
            self.integ.add_output(b.output_deflections())
        for b in (self.blade1, self.blade2, self.blade3):
            self.integ.add_output(b.output_positions())
        for b in (self.blade1, self.blade2, self.blade3):
            self.integ.add_output(dynamics.NodeOutput(b.iprox, local=True, deriv=2))

    @property
    def mass(self):
        """Total mass of turbine"""
        return self.tower.total_mass + self.modes.mass * 3
    
    @property
    def inertia(self):
        """Total rotational inertia of turbine about base"""
        inertia = self.tower.total_inertia
        inertia[(0,1),(0,1)] = self.modes.mass * 3 * self.tower.hubheight
        return inertia
        # XXX neglecting rotor rotational inertia

    def set_base_motion(self, dof, w, amp):
        self.base_motion = dof
        self.base_motion_amp = amp
        self.system.prescribe(self.base, part=dof,
                              vel=lambda t: -w   *amp*np.sin(w*t),
                              acc=lambda t: -w**2*amp*np.cos(w*t))

    def set_initial_conditions(self, qm0=None, az0=None, rotor_speed=None):
        # initial conditions
        if qm0 is not None:  # initial modal amplitudes
            self.system.q[self.blade1.istrain] = qm0
            self.system.q[self.blade2.istrain] = qm0
            self.system.q[self.blade3.istrain] = qm0
        if az0 is not None:
            self.system.q[self.bearing.istrain][0] = az0
        if self.base_motion is not None:
            self.system.q[self.base.istrain][self.base_motion] = self.base_motion_amp
        if rotor_speed is not None:
            self.system.prescribe(self.bearing, vel=rotor_speed)

    def simulate(self, qm0=None, az0=0.0, rotor_speed=10.0, t1=None,
                 dt=0.01, t0=0.0, init=False):
        # reset
        self.system.q [:] = 0.0
        self.system.qd[:] = 0.0
        self.set_initial_conditions(qm0, az0, rotor_speed)
        
        if t1 is None:
            t1 = 4*pi/rotor_speed if (rotor_speed != 0.0) else 2
        
        if init:
            self.system.find_equilibrium()
        
        # simulate
        self.t,self.y = self.integ.integrate(t1, dt, t0)
        for i,lab in enumerate(self.integ.labels()):
            print "%2d  %s" % (i,lab)
        return self.t, self.y
    
    def lin(self, qm0=None, az0=None, rotor_speed=None, init=False):
        # reset
        self.system.q [:] = 0.0
        self.system.qd[:] = 0.0
        self.set_initial_conditions(qm0, az0, rotor_speed)
        
        if init:
            self.system.find_equilibrium()

        linsys = linearisation.LinearisedSystem(self.system)
        return linsys

    def ani(self, vs=(0,1), t=None, y=None):
        if t is None: t = self.t
        if y is None: y = self.y
        limits = [(-10,10), (-42,42), (-5,110)]
        return dynvis.anim(self.system, t, y, vs, limits[vs[0]], limits[vs[1]])
Exemple #36
0
class Turbine(object):
    def __init__(self, bladed_file, root_length=0, rigid=False):
        # Modal element using data from Bladed model
        print "Loading modes from '%s'..." % bladed_file
        self.blade = Blade(bladed_file)
        self.tower = Tower(bladed_file)
        self.modes = self.blade.modal_rep()

        Ry = rotmat_y(-pi/2)
        Rhb1 = rotmat_x(0 * 2*pi/3)
        Rhb2 = rotmat_x(1 * 2*pi/3)
        Rhb3 = rotmat_x(2 * 2*pi/3)

        self.base = FreeJoint('base')
        self.towerlink = RigidConnection('tower', [0,0,self.tower.hubheight])
        self.bearing = Hinge('bearing', [1,0,0])
        root1 = RigidConnection('root1', root_length*np.dot(Rhb1,[0,0,1]), dot(Rhb1,Ry))
        root2 = RigidConnection('root2', root_length*np.dot(Rhb2,[0,0,1]), dot(Rhb2,Ry))
        root3 = RigidConnection('root3', root_length*np.dot(Rhb3,[0,0,1]), dot(Rhb3,Ry))
        self.blade1 = ModalElement('blade1', self.modes)
        self.blade2 = ModalElement('blade2', self.modes)
        self.blade3 = ModalElement('blade3', self.modes)
        
        self.base.add_leaf(self.towerlink)
        self.towerlink.add_leaf(self.bearing)
        self.bearing.add_leaf(root1)
        self.bearing.add_leaf(root2)
        self.bearing.add_leaf(root3)
        root1.add_leaf(self.blade1)
        root2.add_leaf(self.blade2)
        root3.add_leaf(self.blade3)
        self.system = System(self.base)

        # Prescribed DOF accelerations - constant rotor speed
        self.base_motion = None
        self.base_motion_amp = 0
        self.system.prescribe(self.bearing, vel=0)
        self.system.prescribe(self.base, vel=0)
        if rigid:
            for b in (self.blade1, self.blade2, self.blade3):
                self.system.prescribe(b, vel=0)
        
        # setup integrator
        self.integ = Integrator(self.system, ('pos','vel','acc'))
        self.integ.add_output(dynamics.LoadOutput(self.base.iprox))
        self.integ.add_output(dynamics.LoadOutput(self.towerlink.iprox))
        self.integ.add_output(dynamics.LoadOutput(self.bearing.iprox, local=True))
        self.integ.add_output(dynamics.LoadOutput(self.bearing.idist[0], local=True))
        for b in (self.blade1, self.blade2, self.blade3):
            self.integ.add_output(dynamics.LoadOutput(b.iprox, local=True))
        for b in (self.blade1, self.blade2, self.blade3):
            self.integ.add_output(b.output_deflections())
        for b in (self.blade1, self.blade2, self.blade3):
            self.integ.add_output(b.output_positions())
        for b in (self.blade1, self.blade2, self.blade3):
            self.integ.add_output(dynamics.NodeOutput(b.iprox, local=True, deriv=2))

    def set_base_motion(self, dof, w, amp):
        self.base_motion = dof
        self.base_motion_amp = amp
        self.system.prescribe(self.base, part=dof,
                              vel=lambda t: -w   *amp*np.sin(w*t),
                              acc=lambda t: -w**2*amp*np.cos(w*t))

    def set_initial_conditions(self, qm0=None, az0=None, rotor_speed=None):
        # initial conditions
        if qm0 is not None:  # initial modal amplitudes
            self.system.q[self.blade1.istrain] = qm0
            self.system.q[self.blade2.istrain] = qm0
            self.system.q[self.blade3.istrain] = qm0
        if az0 is not None:
            self.system.q[self.bearing.istrain][0] = az0
        if self.base_motion is not None:
            self.system.q[self.base.istrain][self.base_motion] = self.base_motion_amp
        if rotor_speed is not None:
            self.system.prescribe(self.bearing, vel=rotor_speed)

    def simulate(self, qm0=None, az0=0.0, rotor_speed=10.0, t1=None,
                 dt=0.01, t0=0.0, init=False):
        # reset
        self.system.q [:] = 0.0
        self.system.qd[:] = 0.0
        self.set_initial_conditions(qm0, az0, rotor_speed)
        
        if t1 is None:
            t1 = 4*pi/rotor_speed if (rotor_speed != 0.0) else 2
        
        if init:
            self.system.find_equilibrium()
        
        # simulate
        t,y = self.integ.integrate(t1, dt, t0)
        #for i,lab in enumerate(self.integ.labels):
        #    print "%2d  %s" % (i,lab)
        #return self.t, self.y
        
        # Build results structure
        results = TurbineResults(t, y, self.modes.mode_names)
        return results
    
    def lin(self, qm0=None, az0=None, rotor_speed=None, init=False):
        # reset
        self.system.q [:] = 0.0
        self.system.qd[:] = 0.0
        self.set_initial_conditions(qm0, az0, rotor_speed)
        
        if init:
            self.system.find_equilibrium()
            
        # need initial amplitudes for linearisation point
        if qm0 is None: 
            qm0 = self.system.q[self.blade1.istrain]
        linsys = linearisation.LinearisedSystem(self.system, np.tile(qm0, 3))
        return linsys

    def ani(self, results, x=0, y=1):
        H = self.tower.hubheight + self.blade.radii[-1]
        limits = [(-H,H), (-H,H), (-5,H+5)]
        return dynvis.anim(self.system, results.t, results.strains.as_matrix(),
                           (x, y), limits[x], limits[y])
Exemple #37
0
    def __init__(self, bladed_file, root_length=0):
        # Load modes but use a simple flapped blade
        print "Loading modes from '%s'..." % bladed_file
        self.blade = Blade(bladed_file)
        self.tower = Tower(bladed_file)
        self.modes = self.blade.modal_rep()

        # Calculate equivalent blade properties
        I1 = self.modes.I0[0]
        I2 = self.modes.J0[0,0]
        print I1, I2
        wflap = self.modes.freqs[0]
        bmass = self.modes.mass
        inertia = self.modes.inertia_tensor(np.zeros(len(self.modes.freqs)))
        Xc = [I1 / bmass, 0, 0]
        kflap = I2 * wflap**2

        Ry = rotmat_y(-pi/2)
        Rhb1 = rotmat_x(0 * 2*pi/3)
        Rhb2 = rotmat_x(1 * 2*pi/3)
        Rhb3 = rotmat_x(2 * 2*pi/3)

        self.base = FreeJoint('base')
        self.towerlink = RigidConnection('tower', [0,0,self.tower.hubheight])
        self.bearing = Hinge('bearing', [1,0,0])
        root1 = RigidConnection('root1', root_length*np.dot(Rhb1,[0,0,1]), dot(Rhb1,Ry))
        root2 = RigidConnection('root2', root_length*np.dot(Rhb2,[0,0,1]), dot(Rhb2,Ry))
        root3 = RigidConnection('root3', root_length*np.dot(Rhb3,[0,0,1]), dot(Rhb3,Ry))
        self.flap1 = Hinge('flap1', [0,1,0])
        self.flap2 = Hinge('flap2', [0,1,0])
        self.flap3 = Hinge('flap3', [0,1,0])
        self.blade1 = RigidBody('blade1', bmass, inertia, Xc)
        self.blade2 = RigidBody('blade2', bmass, inertia, Xc)
        self.blade3 = RigidBody('blade3', bmass, inertia, Xc)
        
        self.flap1.stiffness = self.flap2.stiffness = self.flap3.stiffness = kflap
        
        self.base.add_leaf(self.towerlink)
        self.towerlink.add_leaf(self.bearing)
        self.bearing.add_leaf(root1)
        self.bearing.add_leaf(root2)
        self.bearing.add_leaf(root3)
        root1.add_leaf(self.flap1)
        root2.add_leaf(self.flap2)
        root3.add_leaf(self.flap3)
        self.flap1.add_leaf(self.blade1)
        self.flap2.add_leaf(self.blade2)
        self.flap3.add_leaf(self.blade3)
        self.system = System(self.base)

        # Prescribed DOF accelerations - constant rotor speed
        self.base_motion = None
        self.base_motion_amp = 0
        self.system.prescribe(self.bearing, vel=0)
        self.system.prescribe(self.base, vel=0)
        
        # setup integrator
        self.integ = Integrator(self.system, ('pos','vel','acc'))
        self.integ.add_output(dynamics.LoadOutput(self.base.iprox))
        self.integ.add_output(dynamics.LoadOutput(self.towerlink.iprox))
        self.integ.add_output(dynamics.LoadOutput(self.bearing.iprox, local=True))
        self.integ.add_output(dynamics.LoadOutput(self.bearing.idist[0], local=True))
        for b in (self.blade1, self.blade2, self.blade3):
            self.integ.add_output(dynamics.LoadOutput(b.iprox, local=True))
        for b in (self.blade1, self.blade2, self.blade3):
            self.integ.add_output(dynamics.NodeOutput(b.iprox, local=True, deriv=2))
Exemple #38
0
class FlappedBladeTurbine(object):
    def __init__(self, bladed_file, root_length=0):
        # Load modes but use a simple flapped blade
        print "Loading modes from '%s'..." % bladed_file
        self.blade = Blade(bladed_file)
        self.tower = Tower(bladed_file)
        self.modes = self.blade.modal_rep()

        # Calculate equivalent blade properties
        I1 = self.modes.I0[0]
        I2 = self.modes.J0[0,0]
        print I1, I2
        wflap = self.modes.freqs[0]
        bmass = self.modes.mass
        inertia = self.modes.inertia_tensor(np.zeros(len(self.modes.freqs)))
        Xc = [I1 / bmass, 0, 0]
        kflap = I2 * wflap**2

        Ry = rotmat_y(-pi/2)
        Rhb1 = rotmat_x(0 * 2*pi/3)
        Rhb2 = rotmat_x(1 * 2*pi/3)
        Rhb3 = rotmat_x(2 * 2*pi/3)

        self.base = FreeJoint('base')
        self.towerlink = RigidConnection('tower', [0,0,self.tower.hubheight])
        self.bearing = Hinge('bearing', [1,0,0])
        root1 = RigidConnection('root1', root_length*np.dot(Rhb1,[0,0,1]), dot(Rhb1,Ry))
        root2 = RigidConnection('root2', root_length*np.dot(Rhb2,[0,0,1]), dot(Rhb2,Ry))
        root3 = RigidConnection('root3', root_length*np.dot(Rhb3,[0,0,1]), dot(Rhb3,Ry))
        self.flap1 = Hinge('flap1', [0,1,0])
        self.flap2 = Hinge('flap2', [0,1,0])
        self.flap3 = Hinge('flap3', [0,1,0])
        self.blade1 = RigidBody('blade1', bmass, inertia, Xc)
        self.blade2 = RigidBody('blade2', bmass, inertia, Xc)
        self.blade3 = RigidBody('blade3', bmass, inertia, Xc)
        
        self.flap1.stiffness = self.flap2.stiffness = self.flap3.stiffness = kflap
        
        self.base.add_leaf(self.towerlink)
        self.towerlink.add_leaf(self.bearing)
        self.bearing.add_leaf(root1)
        self.bearing.add_leaf(root2)
        self.bearing.add_leaf(root3)
        root1.add_leaf(self.flap1)
        root2.add_leaf(self.flap2)
        root3.add_leaf(self.flap3)
        self.flap1.add_leaf(self.blade1)
        self.flap2.add_leaf(self.blade2)
        self.flap3.add_leaf(self.blade3)
        self.system = System(self.base)

        # Prescribed DOF accelerations - constant rotor speed
        self.base_motion = None
        self.base_motion_amp = 0
        self.system.prescribe(self.bearing, vel=0)
        self.system.prescribe(self.base, vel=0)
        
        # setup integrator
        self.integ = Integrator(self.system, ('pos','vel','acc'))
        self.integ.add_output(dynamics.LoadOutput(self.base.iprox))
        self.integ.add_output(dynamics.LoadOutput(self.towerlink.iprox))
        self.integ.add_output(dynamics.LoadOutput(self.bearing.iprox, local=True))
        self.integ.add_output(dynamics.LoadOutput(self.bearing.idist[0], local=True))
        for b in (self.blade1, self.blade2, self.blade3):
            self.integ.add_output(dynamics.LoadOutput(b.iprox, local=True))
        for b in (self.blade1, self.blade2, self.blade3):
            self.integ.add_output(dynamics.NodeOutput(b.iprox, local=True, deriv=2))

    def set_base_motion(self, dof, w, amp):
        self.base_motion = dof
        self.base_motion_amp = amp
        self.system.prescribe(self.base, part=dof,
                              vel=lambda t: -w   *amp*np.sin(w*t),
                              acc=lambda t: -w**2*amp*np.cos(w*t))

    def set_initial_conditions(self, qm0=None, az0=None, rotor_speed=None):
        # initial conditions
        if qm0 is not None:  # initial hinge angle
            self.system.q[self.flap1.istrain][0] = qm0
            self.system.q[self.flap2.istrain][0] = qm0
            self.system.q[self.flap3.istrain][0] = qm0
        if az0 is not None:
            self.system.q[self.bearing.istrain][0] = az0
        if self.base_motion is not None:
            self.system.q[self.base.istrain][self.base_motion] = self.base_motion_amp
        if rotor_speed is not None:
            self.system.prescribe(self.bearing, vel=rotor_speed)

    def simulate(self, qm0=None, az0=0.0, rotor_speed=10.0, t1=None,
                 dt=0.01, t0=0.0, init=False):
        # reset
        self.system.q [:] = 0.0
        self.system.qd[:] = 0.0
        self.set_initial_conditions(qm0, az0, rotor_speed)
        
        if t1 is None:
            t1 = 4*pi/rotor_speed if (rotor_speed != 0.0) else 2
        
        if init:
            self.system.find_equilibrium()
        
        # simulate
        t,y = self.integ.integrate(t1, dt, t0)
        #for i,lab in enumerate(self.integ.labels):
        #    print "%2d  %s" % (i,lab)
        #return self.t, self.y
        
        # Build results structure
        results = FlappedTurbineResults(t, y)
        return results
    
    def lin(self, qm0=None, az0=None, rotor_speed=None, init=False):
        # reset
        self.system.q [:] = 0.0
        self.system.qd[:] = 0.0
        self.set_initial_conditions(qm0, az0, rotor_speed)
        
        if init:
            self.system.find_equilibrium()
            
        # need initial amplitudes for linearisation point
        if qm0 is None: 
            qm0 = self.system.q[self.blade1.istrain]
        linsys = linearisation.LinearisedSystem(self.system, np.tile(qm0, 3))
        return linsys

    def ani(self, results, x=0, y=1):
        H = self.tower.hubheight + self.blade.radii[-1]
        limits = [(-H,H), (-H,H), (-5,H+5)]
        return dynvis.anim(self.system, results.t, results.strains.as_matrix(),
                           (x, y), limits[x], limits[y])
                           
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

import dynamics
from dynamics import System, ModalElement, Integrator, RigidConnection, rotmat_y
from blade import Blade
from loading import BladeLoading
import dynvis

dynamics.OPT_GRAVITY = False

bladed_path = '/bladed/blade_nrel/parked'

# Modal element using data from Bladed model
print "Loading blade from '%s'..." % bladed_path
blade = Blade(bladed_path+'.$pj')
modes = blade.modal_rep()

# Blade loading
wind_table = np.array([
    [0, 1, 2,  10], # time
    [0, 0, 20, 20], # x
    [0, 0, 0,  0 ], # y
    [0, 0, 0,  0 ], # z
])
loading = BladeLoading(blade, wind_table, None)

# Modal element
base = RigidConnection('base', rotation=rotmat_y(-np.pi/2))
el = ModalElement('el', modes, loading)
base.add_leaf(el)