Beispiel #1
0
    def execute(self):

        # ------- node data ----------------
        n = len(self.z)
        node = np.arange(1, n+1)
        x = np.zeros(n)
        y = np.zeros(n)
        z = self.z
        r = np.zeros(n)

        nodes = frame3dd.NodeData(node, x, y, z, r)
        # -----------------------------------

        # ------ reaction data ------------

        # rigid base
        node = self.kidx + 1  # add one because 0-based index but 1-based node numbering
        rigid = float('inf')

        reactions = frame3dd.ReactionData(node, self.kx, self.ky, self.kz, self.ktx, self.kty, self.ktz, rigid)
        # -----------------------------------

        # ------ frame element data ------------
        element = np.arange(1, n)
        N1 = np.arange(1, n)
        N2 = np.arange(2, n+1)

        roll = np.zeros(n-1)

        # average across element b.c. frame3dd uses constant section elements
        Az = 0.5*(self.Az[:-1] + self.Az[1:])
        Asx = 0.5*(self.Asx[:-1] + self.Asx[1:])
        Asy = 0.5*(self.Asy[:-1] + self.Asy[1:])
        Jz = 0.5*(self.Jz[:-1] + self.Jz[1:])
        Ixx = 0.5*(self.Ixx[:-1] + self.Ixx[1:])
        Iyy = 0.5*(self.Iyy[:-1] + self.Iyy[1:])
        E = 0.5*(self.E[:-1] + self.E[1:])
        G = 0.5*(self.G[:-1] + self.G[1:])
        rho = 0.5*(self.rho[:-1] + self.rho[1:])

        elements = frame3dd.ElementData(element, N1, N2, Az, Asx, Asy, Jz,
            Ixx, Iyy, E, G, roll, rho)
        # -----------------------------------


        # ------ options ------------
        options = frame3dd.Options(self.shear, self.geom, self.dx)
        # -----------------------------------

        # initialize frame3dd object
        tower = frame3dd.Frame(nodes, reactions, elements, options)


        # ------ add extra mass ------------

        # extra node inertia data
        N = self.midx + 1

        tower.changeExtraNodeMass(N, self.m, self.mIxx, self.mIyy, self.mIzz, self.mIxy, self.mIxz, self.mIyz,
            self.mrhox, self.mrhoy, self.mrhoz, self.addGravityLoadForExtraMass)

        # ------------------------------------

        # ------- enable dynamic analysis ----------
        tower.enableDynamics(self.nM, self.Mmethod, self.lump, self.tol, self.shift)
        # ----------------------------

        # ------ static load case 1 ------------

        # gravity in the X, Y, Z, directions (global)
        gx = 0.0
        gy = 0.0
        gz = -self.g

        load = frame3dd.StaticLoadCase(gx, gy, gz)


        # point loads
        nF = self.plidx + 1
        load.changePointLoads(nF, self.Fx, self.Fy, self.Fz, self.Mxx, self.Myy, self.Mzz)


        # distributed loads
        Px, Py, Pz = self.Pz, self.Py, -self.Px  # switch to local c.s.
        z = self.z

        # trapezoidally distributed loads
        EL = np.arange(1, n)
        xx1 = np.zeros(n-1)
        xx2 = z[1:] - z[:-1] - 1e-6  # subtract small number b.c. of precision
        wx1 = Px[:-1]
        wx2 = Px[1:]
        xy1 = np.zeros(n-1)
        xy2 = z[1:] - z[:-1] - 1e-6
        wy1 = Py[:-1]
        wy2 = Py[1:]
        xz1 = np.zeros(n-1)
        xz2 = z[1:] - z[:-1] - 1e-6
        wz1 = Pz[:-1]
        wz2 = Pz[1:]

        load.changeTrapezoidalLoads(EL, xx1, xx2, wx1, wx2, xy1, xy2, wy1, wy2, xz1, xz2, wz1, wz2)

        tower.addLoadCase(load)

        # -----------------------------------


        # run the analysis
        displacements, forces, reactions, internalForces, mass, modal = tower.run()
        iCase = 0

        # mass
        self.mass = mass.struct_mass

        # natural frequncies
        self.f1 = modal.freq[0]
        self.f2 = modal.freq[1]

        # deflections due to loading (from tower top and wind/wave loads)
        self.top_deflection = displacements.dx[iCase, n-1]  # in yaw-aligned direction


        # shear and bending (convert from local to global c.s.)
        Fz = forces.Nx[iCase, :]
        Vy = forces.Vy[iCase, :]
        Vx = -forces.Vz[iCase, :]

        Mzz = forces.Txx[iCase, :]
        Myy = forces.Myy[iCase, :]
        Mxx = -forces.Mzz[iCase, :]

        # one per element (first negative b.c. need reaction)
        Fz = np.concatenate([[-Fz[0]], Fz[1::2]])
        Vx = np.concatenate([[-Vx[0]], Vx[1::2]])
        Vy = np.concatenate([[-Vy[0]], Vy[1::2]])

        Mzz = np.concatenate([[-Mzz[0]], Mzz[1::2]])
        Myy = np.concatenate([[-Myy[0]], Myy[1::2]])
        Mxx = np.concatenate([[-Mxx[0]], Mxx[1::2]])

        # axial and shear stress
        ##R = self.d/2.0
        ##x_stress = R*np.cos(self.theta_stress)
        ##y_stress = R*np.sin(self.theta_stress)
        ##axial_stress = Fz/self.Az + Mxx/self.Ixx*y_stress - Myy/self.Iyy*x_stress
#        V = Vy*x_stress/R - Vx*y_stress/R  # shear stress orthogonal to direction x,y
#        shear_stress = 2. * V / self.Az  # coefficient of 2 for a hollow circular section, but should be conservative for other shapes
        axial_stress = Fz/self.Az - np.sqrt(Mxx**2+Myy**2)/self.Iyy*self.d/2.0  #More conservative, just use the tilted bending and add total max shear as well at the same point, if you do not like it go back to the previous lines
        shear_stress = 2. * np.sqrt(Vx**2+Vy**2) / self.Az # coefficient of 2 for a hollow circular section, but should be conservative for other shapes

        # hoop_stress (Eurocode method)
        hoop_stress = hoopStressEurocode(self.z, self.d, self.t, self.L_reinforced, self.qdyn)

        # von mises stress
        self.stress = vonMisesStressUtilization(axial_stress, hoop_stress, shear_stress,
                      self.gamma_f*self.gamma_m*self.gamma_n, self.sigma_y)

        # shell buckling
        self.shell_buckling = shellBucklingEurocode(self.d, self.t, axial_stress, hoop_stress,
                      shear_stress, self.L_reinforced, self.E, self.sigma_y, self.gamma_f, self.gamma_b)

        # global buckling
        tower_height = self.z[-1] - self.z[0]
        M = np.sqrt(Mxx**2 + Myy**2)
        self.global_buckling = bucklingGL(self.d, self.t, Fz, M, tower_height, self.E,
            self.sigma_y, self.gamma_f, self.gamma_b)

        # fatigue
        N_DEL = [365*24*3600*self.life]*len(z)
        self.damage=np.zeros(z.size)
        if any(self.M_DEL):
            M_DEL = np.interp(z, self.z_DEL, self.M_DEL)

            self.damage = fatigue(M_DEL, N_DEL, self.d, self.t, self.m_SN, self.DC, self.gamma_fatigue, stress_factor=1.0, weld_factor=True)
Beispiel #2
0
    def execute(self):

        # ------- node data ----------------
        n = len(self.z)
        node = np.arange(1, n+1)
        x = np.zeros(n)
        y = np.zeros(n)
        z = self.z
        r = np.zeros(n)

        nodes = frame3dd.NodeData(node, x, y, z, r)
        # -----------------------------------

        # ------ reaction data ------------

        # rigid base
        node = self.kidx + 1  # add one because 0-based index but 1-based node numbering
        rigid = float('inf')

        reactions = frame3dd.ReactionData(node, self.kx, self.ky, self.kz, self.ktx, self.kty, self.ktz, rigid)
        # -----------------------------------

        # ------ frame element data ------------
        element = np.arange(1, n)
        N1 = np.arange(1, n)
        N2 = np.arange(2, n+1)

        roll = np.zeros(n-1)

        # average across element b.c. frame3dd uses constant section elements
        Az = 0.5*(self.Az[:-1] + self.Az[1:])
        Asx = 0.5*(self.Asx[:-1] + self.Asx[1:])
        Asy = 0.5*(self.Asy[:-1] + self.Asy[1:])
        Jz = 0.5*(self.Jz[:-1] + self.Jz[1:])
        Ixx = 0.5*(self.Ixx[:-1] + self.Ixx[1:])
        Iyy = 0.5*(self.Iyy[:-1] + self.Iyy[1:])
        E = 0.5*(self.E[:-1] + self.E[1:])
        G = 0.5*(self.G[:-1] + self.G[1:])
        rho = 0.5*(self.rho[:-1] + self.rho[1:])

        elements = frame3dd.ElementData(element, N1, N2, Az, Asx, Asy, Jz,
            Ixx, Iyy, E, G, roll, rho)
        # -----------------------------------


        # ------ options ------------
        options = frame3dd.Options(self.shear, self.geom, self.dx)
        # -----------------------------------

        # initialize frame3dd object
        tower = frame3dd.Frame(nodes, reactions, elements, options)


        # ------ add extra mass ------------

        # extra node inertia data
        N = self.midx + 1

        tower.changeExtraNodeMass(N, self.m, self.mIxx, self.mIyy, self.mIzz, self.mIxy, self.mIxz, self.mIyz,
            self.mrhox, self.mrhoy, self.mrhoz, self.addGravityLoadForExtraMass)

        # ------------------------------------

        # ------- enable dynamic analysis ----------
        tower.enableDynamics(self.nM, self.Mmethod, self.lump, self.tol, self.shift)
        # ----------------------------

        # ------ static load case 1 ------------

        # gravity in the X, Y, Z, directions (global)
        gx = 0.0
        gy = 0.0
        gz = -self.g

        load = frame3dd.StaticLoadCase(gx, gy, gz)


        # point loads
        nF = self.plidx + 1
        load.changePointLoads(nF, self.Fx, self.Fy, self.Fz, self.Mxx, self.Myy, self.Mzz)


        # distributed loads
        Px, Py, Pz = self.Pz, self.Py, -self.Px  # switch to local c.s.
        z = self.z

        # trapezoidally distributed loads
        EL = np.arange(1, n)
        xx1 = np.zeros(n-1)
        xx2 = z[1:] - z[:-1] - 1e-6  # subtract small number b.c. of precision
        wx1 = Px[:-1]
        wx2 = Px[1:]
        xy1 = np.zeros(n-1)
        xy2 = z[1:] - z[:-1] - 1e-6
        wy1 = Py[:-1]
        wy2 = Py[1:]
        xz1 = np.zeros(n-1)
        xz2 = z[1:] - z[:-1] - 1e-6
        wz1 = Pz[:-1]
        wz2 = Pz[1:]


        load.changeTrapezoidalLoads(EL, xx1, xx2, wx1, wx2, xy1, xy2, wy1, wy2, xz1, xz2, wz1, wz2)

        tower.addLoadCase(load)

        # -----------------------------------
        """
        print '*******************************************************************'
        print '*******************************************************************'
        #print 'tower: ', tower.__dict__
        print "c_nodes: ", tower.__dict__['c_nodes'].__dict__
        print "c_reactions: ", tower.__dict__['c_reactions'].__dict__
        print "c_condensation: ", tower.__dict__['c_condensation'].__dict__
        print "c_elements: ", tower.__dict__['c_elements'].__dict__
        #print "loadCases: ", tower.__dict__['StaticLoadCase'].__dict__
        print "c_extraInertia: ", tower.__dict__['c_extraInertia'].__dict__
        print "c_other: ", tower.__dict__['c_other'].__dict__
        print "c_extraMass: ", tower.__dict__['c_extraMass'].__dict__
        """
        # run the analysis
        displacements, forces, reactions, internalForces, mass, modal = tower.run()
        iCase = 0

        # mass
        self.mass = mass.struct_mass

        # natural frequncies
        self.f1 = modal.freq[0]
        self.f2 = modal.freq[1]

        # deflections due to loading (from tower top and wind/wave loads)
        self.top_deflection = displacements.dx[iCase, n-1]  # in yaw-aligned direction


        # shear and bending (convert from local to global c.s.)
        Fz = forces.Nx[iCase, :]
        Vy = forces.Vy[iCase, :]
        Vx = -forces.Vz[iCase, :]

        Mzz = forces.Txx[iCase, :]
        Myy = forces.Myy[iCase, :]
        Mxx = -forces.Mzz[iCase, :]

        # one per element (first negative b.c. need reaction)
        Fz = np.concatenate([[-Fz[0]], Fz[1::2]])
        Vx = np.concatenate([[-Vx[0]], Vx[1::2]])
        Vy = np.concatenate([[-Vy[0]], Vy[1::2]])

        Mzz = np.concatenate([[-Mzz[0]], Mzz[1::2]])
        Myy = np.concatenate([[-Myy[0]], Myy[1::2]])
        Mxx = np.concatenate([[-Mxx[0]], Mxx[1::2]])

        # axial and shear stress
        ##R = self.d/2.0
        ##x_stress = R*np.cos(self.theta_stress)
        ##y_stress = R*np.sin(self.theta_stress)
        ##axial_stress = Fz/self.Az + Mxx/self.Ixx*y_stress - Myy/self.Iyy*x_stress
#        V = Vy*x_stress/R - Vx*y_stress/R  # shear stress orthogonal to direction x,y
#        shear_stress = 2. * V / self.Az  # coefficient of 2 for a hollow circular section, but should be conservative for other shapes
        axial_stress = Fz/self.Az - np.sqrt(Mxx**2+Myy**2)/self.Iyy*self.d/2.0  #More conservative, just use the tilted bending and add total max shear as well at the same point, if you do not like it go back to the previous lines

        shear_stress = 2. * np.sqrt(Vx**2+Vy**2) / self.Az # coefficient of 2 for a hollow circular section, but should be conservative for other shapes

        # hoop_stress (Eurocode method)
        hoop_stress = hoopStressEurocode(self.z, self.d, self.t, self.L_reinforced, self.qdyn)

        # von mises stress
        self.stress = vonMisesStressUtilization(axial_stress, hoop_stress, shear_stress,
                      self.gamma_f*self.gamma_m*self.gamma_n, self.sigma_y)

        # shell buckling
        self.shell_buckling = shellBucklingEurocode(self.d, self.t, axial_stress, hoop_stress,
                      shear_stress, self.L_reinforced, self.E, self.sigma_y, self.gamma_f, self.gamma_b)

        # global buckling
        tower_height = self.z[-1] - self.z[0]
        M = np.sqrt(Mxx**2 + Myy**2)
        self.global_buckling = bucklingGL(self.d, self.t, Fz, M, tower_height, self.E,
            self.sigma_y, self.gamma_f, self.gamma_b)

        # fatigue
        N_DEL = [365*24*3600*self.life]*len(z)
        self.damage=np.zeros(z.size)
        if any(self.M_DEL):
            M_DEL = np.interp(z, self.z_DEL, self.M_DEL)

            self.damage = fatigue(M_DEL, N_DEL, self.d, self.t, self.m_SN, self.DC, self.gamma_fatigue, stress_factor=1.0, weld_factor=True)
Beispiel #3
0
    def execute(self):

        #simplify nomenclature
        gamma_f = self.IECpsfIns.gamma_f
        gamma_m = self.IECpsfIns.gamma_m
        gamma_n = self.IECpsfIns.gamma_n
        gamma_b = self.IECpsfIns.gamma_b
        gamma_g = self.IECpsfIns.gamma_g

        ##z = self.towerWindLoads.z  # this should be the same for wind and wave
        ##Px = self.towerWindLoads.Px + self.towerWaveLoads.Px
        ##Py = self.towerWindLoads.Py + self.towerWaveLoads.Py
        ##Pz = self.towerWindLoads.Pz + self.towerWaveLoads.Pz
        z =self.towerWWLoads.z
        Px=self.towerWWLoads.Px
        Py=self.towerWWLoads.Py
        Pz=self.towerWWLoads.Pz
        #Make it all along x
        Px=np.sqrt(Px**2+Py**2)
        Py *=0.

        Twr = self.Twr_data.TwrObj
        # #Augment with z etc as needed by JTwrUtilization
        # Twr.nodes=self.Twrouts.nodes
        # Twr.RNAmass=self.Twrouts.TopMass[0]
        # Twr.CMzoff=self.Twrouts.TopMass[-1]
        # Twr.RNA_Thrust=np.sqrt((self.RNA_F[0:2]**2).sum())
        # Twr.RNA_M=self.RNA_F[3:]
        # #GLUtil,EUshUtil=TwrUtil.JTwrUtilization(Twr,self.wind_dict,self.water_dict,wind_load=self.twr_wndload,water_load=self.twr_wtrload,ploton=False,gravity=self.gravity)#
        # #max_GLUtil=np.nanmax(GLUtil)
        # #max_EUUtil=np.nanmax(EUshUtil)

        twr_z = self.Twr_data.nodes[2, :]
        d_top = self.Dt
        t_top = self.tt
        twr_D = np.hstack((Twr.D, d_top))  # add top diameter
        twr_t = np.hstack((Twr.t, t_top))  # add top thickness
        twr_A = np.hstack((Twr.Area, np.pi/4.*(d_top**2-(d_top-2.*t_top)**2)))  # add top diameter station
        # twr_Amid = np.pi/4.*(twr_D-twr_t)**2  # Mid surface inscribed area (for torsion)
        twr_Jyy = np.hstack((Twr.Jyy, np.pi/64.*(d_top**4-(d_top-2.*t_top)**4)))  # add top diameter station

        # if twr_z.size > twr_D.size:  # this may include a fictitious z for the attachment to RNA (CMzoff)
        #     twr_z = twr_z[:-1]  # pop the last element #Need to remove RNA joint, not part of tower

        # n = twr_z.shape[0]  # number of stations along the tower (removing top one in case there is a rigid link on top)

        #Take care of the fact we get materials spearately for each tower element
        rho = np.array([mat.rho for mat in Twr.mat])  # I wonder whether these 3 could be condensed into 1 loop instead of 3
        E = np.array([mat.E for mat in Twr.mat])
        fy = np.array([mat.fy for mat in Twr.mat])
        #replicate the last item since mat was for elements not nodes
        rho = np.hstack((rho, rho[-1]))
        E = np.hstack((E, E[-1]))
        fy = np.hstack((fy, fy[-1]))



        #Calculate internal loads
        # MTTg = Twr.RNAmass*self.gravity  # [N]  Tower top weight

        n = len(z)

        Vx = np.zeros(n)
        Vy = np.zeros(n)
        Fz = np.zeros(n)
        Mx = np.zeros(n)
        My = np.zeros(n)
        Tz = np.zeros(n)
        Vx[-1] = self.top_F[0]
        Vy[-1] = self.top_F[1]
        Fz[-1] = self.top_F[2]
        Mx[-1] = self.top_M[0]
        My[-1] = self.top_M[1]
        Tz[-1] = self.top_M[2]

        for i in reversed(range(n-1)):
            delta_z=z[i+1]-z[i]
            vol=frustum(twr_D[i],twr_D[i+1],delta_z)[0]-frustum(twr_D[i]-2.*twr_t[i], twr_D[i+1]-2.*twr_t[i+1], delta_z)[0]

            Vx[i] = Vx[i+1] + 0.5*(Px[i] + Px[i+1])*delta_z
            Vy[i] = Vy[i+1] + 0.5*(Py[i] + Py[i+1])*delta_z
            Fz[i] = Fz[i+1] + 0.5*(Pz[i] + Pz[i+1])*delta_z - 0.5*(rho[i]+rho[i+1])*self.g*vol  #This was missing self weight of tower shell

            Mx[i] = Mx[i+1] + Vy[i+1]*(z[i+1]-z[i]) + (Py[i]/6.0 + Py[i+1]/3.0)*(z[i+1]-z[i])**2
            My[i] = My[i+1] + Vx[i+1]*(z[i+1]-z[i]) + (Px[i]/6.0 + Px[i+1]/3.0)*(z[i+1]-z[i])**2
            Tz[i] = Tz[i+1]



        L_reinforced = self.L_reinforced*np.ones_like(twr_D)

        # axial and shear stress (all stress evaluated on +x yaw side)
        axial_stress = Fz/twr_A - np.sqrt(Mx**2+My**2)/twr_Jyy*twr_D/2.0
        shear_stress = 2 * np.sqrt(Vx**2+Vy**2) / twr_A

        #hoop_stress = hoopStressEurocode(self.towerWindLoads, self.towerWaveLoads,
        #    z, twr_D, twr_t, L_reinforced)
        hoop_stress = hoopStressEurocode(z, twr_D, twr_t, L_reinforced,self.towerWWLoads.qdyn)

        # von mises stress
        VMutil = vonMisesStressUtilization(axial_stress, hoop_stress, shear_stress,
            gamma_f*gamma_m*gamma_n, fy)

        self.utilization.StressUtil = VMutil   #  utilization

        # shell buckling
        shell_buckling = shellBucklingEurocode(twr_D, twr_t, axial_stress, hoop_stress,
            shear_stress, L_reinforced, E, fy, gamma_f, gamma_b)

        self.utilization.EUshUtil = shell_buckling  #utilization

        # global buckling
        tower_height = z[-1] - z[0]
        tower_buckling = bucklingGL(twr_D, twr_t, Fz, My, tower_height, E, fy, gamma_f, gamma_b, gamma_g)

        self.utilization.GLUtil = tower_buckling  #utilization
Beispiel #4
0
    def solve_nonlinear(self, params, unknowns, resids):

        # ------- node data ----------------
        z = params['z']
        n = len(z)
        node = np.arange(1, n+1)
        x = np.zeros(n)
        y = np.zeros(n)
        r = np.zeros(n)

        nodes = frame3dd.NodeData(node, x, y, z, r)
        # -----------------------------------

        # ------ reaction data ------------

        # rigid base
        node = params['kidx'] + np.ones(len(params['kidx']), dtype=int)  # add one because 0-based index but 1-based node numbering
        rigid = float('inf')

        reactions = frame3dd.ReactionData(node, params['kx'], params['ky'], params['kz'], params['ktx'], params['kty'], params['ktz'], rigid)
        # -----------------------------------

        # ------ frame element data ------------
        element = np.arange(1, n)
        N1 = np.arange(1, n)
        N2 = np.arange(2, n+1)

        roll = np.zeros(n-1)

        # average across element b.c. frame3dd uses constant section elements
        Az = 0.5*(params['Az'][:-1] + params['Az'][1:])
        Asx = 0.5*(params['Asx'][:-1] + params['Asx'][1:])
        Asy = 0.5*(params['Asy'][:-1] + params['Asy'][1:])
        Jz = 0.5*(params['Jz'][:-1] + params['Jz'][1:])
        Ixx = 0.5*(params['Ixx'][:-1] + params['Ixx'][1:])
        Iyy = 0.5*(params['Iyy'][:-1] + params['Iyy'][1:])
        E = 0.5*(params['E'][:-1] + params['E'][1:])
        G = 0.5*(params['G'][:-1] + params['G'][1:])
        rho = 0.5*(params['rho'][:-1] + params['rho'][1:])

        elements = frame3dd.ElementData(element, N1, N2, Az, Asx, Asy, Jz,
            Ixx, Iyy, E, G, roll, rho)
        # -----------------------------------


        # ------ options ------------
        options = frame3dd.Options(params['shear'], params['geom'], params['dx'])
        # -----------------------------------

        # initialize frame3dd object
        tower = frame3dd.Frame(nodes, reactions, elements, options)


        # ------ add extra mass ------------

        # extra node inertia data
        N = params['midx'] + np.ones(len(params['midx']), dtype=int)

        tower.changeExtraNodeMass(N, params['m'], params['mIxx'], params['mIyy'], params['mIzz'], params['mIxy'], params['mIxz'], params['mIyz'],
            params['mrhox'], params['mrhoy'], params['mrhoz'], params['addGravityLoadForExtraMass'])

        # ------------------------------------

        # ------- enable dynamic analysis ----------
        tower.enableDynamics(params['nM'], params['Mmethod'], params['lump'], params['tol'], params['shift'])
        # ----------------------------

        # ------ static load case 1 ------------

        # gravity in the X, Y, Z, directions (global)
        gx = 0.0
        gy = 0.0
        gz = -params['g']

        load = frame3dd.StaticLoadCase(gx, gy, gz)

        # point loads
        nF = params['plidx'] + np.ones(len(params['plidx']), dtype=int)
        load.changePointLoads(nF, params['Fx'], params['Fy'], params['Fz'], params['Mxx'], params['Myy'], params['Mzz'])

        # distributed loads
        Px, Py, Pz = params['Pz'], params['Py'], -params['Px']  # switch to local c.s.
        z = params['z']

        # trapezoidally distributed loads
        EL = np.arange(1, n)
        xx1 = np.zeros(n-1)
        xx2 = z[1:] - z[:-1] - np.ones(n-1)*1e-6  # subtract small number b.c. of precision
        wx1 = Px[:-1]
        wx2 = Px[1:]
        xy1 = np.zeros(n-1)
        xy2 = z[1:] - z[:-1] - np.ones(n-1)*1e-6
        wy1 = Py[:-1]
        wy2 = Py[1:]
        xz1 = np.zeros(n-1)
        xz2 = z[1:] - z[:-1] - np.ones(n-1)*1e-6
        wz1 = Pz[:-1]
        wz2 = Pz[1:]

        load.changeTrapezoidalLoads(EL, xx1, xx2, wx1, wx2, xy1, xy2, wy1, wy2, xz1, xz2, wz1, wz2)

        tower.addLoadCase(load)

        # -----------------------------------
        # run the analysis
        displacements, forces, reactions, internalForces, mass, modal = tower.run()
        iCase = 0

        # mass
        unknowns['mass'] = mass.struct_mass

        # natural frequncies
        unknowns['f1'] = modal.freq[0]
        unknowns['f2'] = modal.freq[1]

        # deflections due to loading (from tower top and wind/wave loads)
        unknowns['top_deflection'] = displacements.dx[iCase, n-1]  # in yaw-aligned direction


        # shear and bending (convert from local to global c.s.)
        Fz = forces.Nx[iCase, :]
        Vy = forces.Vy[iCase, :]
        Vx = -forces.Vz[iCase, :]

        Mzz = forces.Txx[iCase, :]
        Myy = forces.Myy[iCase, :]
        Mxx = -forces.Mzz[iCase, :]

        # one per element (first negative b.c. need reaction)
        Fz = np.concatenate([[-Fz[0]], Fz[1::2]])
        Vx = np.concatenate([[-Vx[0]], Vx[1::2]])
        Vy = np.concatenate([[-Vy[0]], Vy[1::2]])

        Mzz = np.concatenate([[-Mzz[0]], Mzz[1::2]])
        Myy = np.concatenate([[-Myy[0]], Myy[1::2]])
        Mxx = np.concatenate([[-Mxx[0]], Mxx[1::2]])

        # axial and shear stress
        ##R = self.d/2.0
        ##x_stress = R*np.cos(self.theta_stress)
        ##y_stress = R*np.sin(self.theta_stress)
        ##axial_stress = Fz/self.Az + Mxx/self.Ixx*y_stress - Myy/self.Iyy*x_stress
#        V = Vy*x_stress/R - Vx*y_stress/R  # shear stress orthogonal to direction x,y
#        shear_stress = 2. * V / self.Az  # coefficient of 2 for a hollow circular section, but should be conservative for other shapes
        axial_stress = Fz/params['Az'] - np.sqrt(Mxx**2+Myy**2)/params['Iyy']*params['d']/2.0  #More conservative, just use the tilted bending and add total max shear as well at the same point, if you do not like it go back to the previous lines

        shear_stress = 2. * np.sqrt(Vx**2+Vy**2) / params['Az'] # coefficient of 2 for a hollow circular section, but should be conservative for other shapes

        # hoop_stress (Eurocode method)
        hoop_stress = hoopStressEurocode(params['z'], params['d'], params['t'], params['L_reinforced'], params['qdyn'])

        # von mises stress
        unknowns['stress'] = vonMisesStressUtilization(axial_stress, hoop_stress, shear_stress,
                      params['gamma_f']*params['gamma_m']*params['gamma_n'], params['sigma_y'])

        # shell buckling
        unknowns['shell_buckling'] = shellBucklingEurocode(params['d'], params['t'], axial_stress, hoop_stress,
                      shear_stress, params['L_reinforced'], params['E'], params['sigma_y'], params['gamma_f'], params['gamma_b'])

        # global buckling
        tower_height = params['z'][-1] - params['z'][0]
        M = np.sqrt(Mxx**2 + Myy**2)
        unknowns['global_buckling'] = bucklingGL(params['d'], params['t'], Fz, M, tower_height, params['E'],
            params['sigma_y'], params['gamma_f'], params['gamma_b'])

        # fatigue
        N_DEL = [365*24*3600*params['life']]*len(z)
        unknowns['damage']=np.zeros(z.size)
        if any(params['M_DEL']):
            M_DEL = np.interp(z, params['z_DEL'], params['M_DEL'])

            unknowns['damage'] = fatigue(M_DEL, N_DEL, params['d'], params['t'], params['m_SN'], params['DC'], params['gamma_fatigue'], stress_factor=1.0, weld_factor=True)
Beispiel #5
0
    def solve_nonlinear(self, params, unknowns, resids):

        # ------- node data ----------------
        z = params['z']
        n = len(z)
        node = np.arange(1, n+1)
        x = np.zeros(n)
        y = np.zeros(n)
        r = np.zeros(n)

        nodes = frame3dd.NodeData(node, x, y, z, r)
        # -----------------------------------

        # ------ reaction data ------------

        # rigid base
        node = params['kidx'] + np.ones(len(params['kidx']), dtype=np.int_)  # add one because 0-based index but 1-based node numbering
        rigid = float('inf')

        reactions = frame3dd.ReactionData(node, params['kx'], params['ky'], params['kz'], params['ktx'], params['kty'], params['ktz'], rigid)
        # -----------------------------------

        # ------ frame element data ------------
        element = np.arange(1, n)
        N1 = np.arange(1, n)
        N2 = np.arange(2, n+1)

        roll = np.zeros(n-1)

        # average across element b.c. frame3dd uses constant section elements
        # TODO: Use nodal2sectional
        Az  = params['Az']
        Asx = params['Asx']
        Asy = params['Asy']
        Jz  = params['Jz']
        Ixx = params['Ixx']
        Iyy = params['Iyy']
        E   = params['E']*np.ones(Az.shape)
        G   = params['G']*np.ones(Az.shape)
        rho = params['rho']*np.ones(Az.shape)

        elements = frame3dd.ElementData(element, N1, N2, Az, Asx, Asy, Jz, Ixx, Iyy, E, G, roll, rho)
        # -----------------------------------


        # ------ options ------------
        options = frame3dd.Options(params['shear'], params['geom'], params['dx'])
        # -----------------------------------

        # initialize frame3dd object
        cylinder = frame3dd.Frame(nodes, reactions, elements, options)


        # ------ add extra mass ------------

        # extra node inertia data
        N = params['midx'] + np.ones(len(params['midx']), dtype=np.int_)

        cylinder.changeExtraNodeMass(N, params['m'], params['mIxx'], params['mIyy'], params['mIzz'], params['mIxy'], params['mIxz'], params['mIyz'],
            params['mrhox'], params['mrhoy'], params['mrhoz'], params['addGravityLoadForExtraMass'])

        # ------------------------------------

        # ------- enable dynamic analysis ----------
        cylinder.enableDynamics(params['nM'], params['Mmethod'], params['lump'], params['tol'], params['shift'])
        # ----------------------------

        # ------ static load case 1 ------------

        # gravity in the X, Y, Z, directions (global)
        gx = 0.0
        gy = 0.0
        gz = -gravity

        load = frame3dd.StaticLoadCase(gx, gy, gz)

        # point loads
        nF = params['plidx'] + np.ones(len(params['plidx']), dtype=int)
        load.changePointLoads(nF, params['Fx'], params['Fy'], params['Fz'], params['Mxx'], params['Myy'], params['Mzz'])

        # distributed loads
        Px, Py, Pz = params['Pz'], params['Py'], -params['Px']  # switch to local c.s.
        z = params['z']

        # trapezoidally distributed loads
        EL = np.arange(1, n)
        xx1 = xy1 = xz1 = np.zeros(n-1)
        xx2 = xy2 = xz2 = np.diff(z) - 1e-6  # subtract small number b.c. of precision
        wx1 = Px[:-1]
        wx2 = Px[1:]
        wy1 = Py[:-1]
        wy2 = Py[1:]
        wz1 = Pz[:-1]
        wz2 = Pz[1:]

        load.changeTrapezoidalLoads(EL, xx1, xx2, wx1, wx2, xy1, xy2, wy1, wy2, xz1, xz2, wz1, wz2)

        cylinder.addLoadCase(load)
        # Debugging
        #cylinder.write('temp.3dd')
        # -----------------------------------
        # run the analysis
        displacements, forces, reactions, internalForces, mass, modal = cylinder.run()
        iCase = 0

        # mass
        unknowns['mass'] = mass.struct_mass

        # natural frequncies
        unknowns['f1'] = modal.freq[0]
        unknowns['f2'] = modal.freq[1]

        # deflections due to loading (from cylinder top and wind/wave loads)
        unknowns['top_deflection'] = displacements.dx[iCase, n-1]  # in yaw-aligned direction

        # shear and bending, one per element (convert from local to global c.s.)
        Fz = forces.Nx[iCase, 1::2]
        Vy = forces.Vy[iCase, 1::2]
        Vx = -forces.Vz[iCase, 1::2]

        Mzz = forces.Txx[iCase, 1::2]
        Myy = forces.Myy[iCase, 1::2]
        Mxx = -forces.Mzz[iCase, 1::2]

        # Record total forces and moments
        unknowns['base_F'] = -1.0 * np.array([reactions.Fx.sum(), reactions.Fy.sum(), reactions.Fz.sum()])
        unknowns['base_M'] = -1.0 * np.array([reactions.Mxx.sum(), reactions.Myy.sum(), reactions.Mzz.sum()])

        unknowns['Fz_out']  = Fz
        unknowns['Vx_out']  = Vx
        unknowns['Vy_out']  = Vy
        unknowns['Mxx_out'] = Mxx
        unknowns['Myy_out'] = Myy
        unknowns['Mzz_out'] = Mzz

        # axial and shear stress
        d,_    = nodal2sectional(params['d'])
        qdyn,_ = nodal2sectional(params['qdyn'])
        
        ##R = self.d/2.0
        ##x_stress = R*np.cos(self.theta_stress)
        ##y_stress = R*np.sin(self.theta_stress)
        ##axial_stress = Fz/self.Az + Mxx/self.Ixx*y_stress - Myy/self.Iyy*x_stress
#        V = Vy*x_stress/R - Vx*y_stress/R  # shear stress orthogonal to direction x,y
#        shear_stress = 2. * V / self.Az  # coefficient of 2 for a hollow circular section, but should be conservative for other shapes
        unknowns['axial_stress'] = Fz/params['Az'] - np.sqrt(Mxx**2+Myy**2)/params['Iyy']*d/2.0  #More conservative, just use the tilted bending and add total max shear as well at the same point, if you do not like it go back to the previous lines

        unknowns['shear_stress'] = 2. * np.sqrt(Vx**2+Vy**2) / params['Az'] # coefficient of 2 for a hollow circular section, but should be conservative for other shapes

        # hoop_stress (Eurocode method)
        L_reinforced = params['L_reinforced'] * np.ones(Fz.shape)
        unknowns['hoop_stress_euro'] = hoopStressEurocode(params['z'], d, params['t'], L_reinforced, qdyn)

        # Simpler hoop stress used in API calculations
        unknowns['hoop_stress'] = hoopStress(d, params['t'], qdyn)
Beispiel #6
0
    def solve_nonlinear(self, params, unknowns, resids):

        # ------- node data ----------------
        z = params['z']
        n = len(z)
        node = np.arange(1, n + 1)
        x = np.zeros(n)
        y = np.zeros(n)
        r = np.zeros(n)

        nodes = frame3dd.NodeData(node, x, y, z, r)
        # -----------------------------------

        # ------ reaction data ------------

        # rigid base
        node = params['kidx'] + np.ones(
            len(params['kidx']), dtype=np.int_
        )  # add one because 0-based index but 1-based node numbering
        rigid = float('inf')

        reactions = frame3dd.ReactionData(node, params['kx'], params['ky'],
                                          params['kz'], params['ktx'],
                                          params['kty'], params['ktz'], rigid)
        # -----------------------------------

        # ------ frame element data ------------
        element = np.arange(1, n)
        N1 = np.arange(1, n)
        N2 = np.arange(2, n + 1)

        roll = np.zeros(n - 1)

        # average across element b.c. frame3dd uses constant section elements
        # TODO: Use nodal2sectional
        Az = params['Az']
        Asx = params['Asx']
        Asy = params['Asy']
        Jz = params['Jz']
        Ixx = params['Ixx']
        Iyy = params['Iyy']
        E = params['E'] * np.ones(Az.shape)
        G = params['G'] * np.ones(Az.shape)
        rho = params['rho'] * np.ones(Az.shape)

        elements = frame3dd.ElementData(element, N1, N2, Az, Asx, Asy, Jz, Ixx,
                                        Iyy, E, G, roll, rho)
        # -----------------------------------

        # ------ options ------------
        options = frame3dd.Options(params['shear'], params['geom'],
                                   params['dx'])
        # -----------------------------------

        # initialize frame3dd object
        cylinder = frame3dd.Frame(nodes, reactions, elements, options)

        # ------ add extra mass ------------

        # extra node inertia data
        N = params['midx'] + np.ones(len(params['midx']), dtype=np.int_)

        cylinder.changeExtraNodeMass(N, params['m'], params['mIxx'],
                                     params['mIyy'], params['mIzz'],
                                     params['mIxy'], params['mIxz'],
                                     params['mIyz'], params['mrhox'],
                                     params['mrhoy'], params['mrhoz'],
                                     params['addGravityLoadForExtraMass'])

        # ------------------------------------

        # ------- enable dynamic analysis ----------
        cylinder.enableDynamics(params['nM'], params['Mmethod'],
                                params['lump'], params['tol'], params['shift'])
        # ----------------------------

        # ------ static load case 1 ------------

        # gravity in the X, Y, Z, directions (global)
        gx = 0.0
        gy = 0.0
        gz = -gravity

        load = frame3dd.StaticLoadCase(gx, gy, gz)

        # point loads
        nF = params['plidx'] + np.ones(len(params['plidx']), dtype=int)
        load.changePointLoads(nF, params['Fx'], params['Fy'], params['Fz'],
                              params['Mxx'], params['Myy'], params['Mzz'])

        # distributed loads
        Px, Py, Pz = params['Pz'], params['Py'], -params[
            'Px']  # switch to local c.s.
        z = params['z']

        # trapezoidally distributed loads
        EL = np.arange(1, n)
        xx1 = xy1 = xz1 = np.zeros(n - 1)
        xx2 = xy2 = xz2 = np.diff(
            z) - 1e-6  # subtract small number b.c. of precision
        wx1 = Px[:-1]
        wx2 = Px[1:]
        wy1 = Py[:-1]
        wy2 = Py[1:]
        wz1 = Pz[:-1]
        wz2 = Pz[1:]

        load.changeTrapezoidalLoads(EL, xx1, xx2, wx1, wx2, xy1, xy2, wy1, wy2,
                                    xz1, xz2, wz1, wz2)

        cylinder.addLoadCase(load)
        # Debugging
        #cylinder.write('temp.3dd')
        # -----------------------------------
        # run the analysis
        displacements, forces, reactions, internalForces, mass, modal = cylinder.run(
        )
        iCase = 0

        # mass
        unknowns['mass'] = mass.struct_mass

        # natural frequncies
        unknowns['f1'] = modal.freq[0]
        unknowns['f2'] = modal.freq[1]

        # deflections due to loading (from cylinder top and wind/wave loads)
        unknowns['top_deflection'] = displacements.dx[
            iCase, n - 1]  # in yaw-aligned direction

        # shear and bending, one per element (convert from local to global c.s.)
        Fz = forces.Nx[iCase, 1::2]
        Vy = forces.Vy[iCase, 1::2]
        Vx = -forces.Vz[iCase, 1::2]

        Mzz = forces.Txx[iCase, 1::2]
        Myy = forces.Myy[iCase, 1::2]
        Mxx = -forces.Mzz[iCase, 1::2]

        # Record total forces and moments
        unknowns['base_F'] = -1.0 * np.array(
            [reactions.Fx.sum(),
             reactions.Fy.sum(),
             reactions.Fz.sum()])
        unknowns['base_M'] = -1.0 * np.array(
            [reactions.Mxx.sum(),
             reactions.Myy.sum(),
             reactions.Mzz.sum()])

        unknowns['Fz_out'] = Fz
        unknowns['Vx_out'] = Vx
        unknowns['Vy_out'] = Vy
        unknowns['Mxx_out'] = Mxx
        unknowns['Myy_out'] = Myy
        unknowns['Mzz_out'] = Mzz

        # axial and shear stress
        d, _ = nodal2sectional(params['d'])
        qdyn, _ = nodal2sectional(params['qdyn'])

        ##R = self.d/2.0
        ##x_stress = R*np.cos(self.theta_stress)
        ##y_stress = R*np.sin(self.theta_stress)
        ##axial_stress = Fz/self.Az + Mxx/self.Ixx*y_stress - Myy/self.Iyy*x_stress
        #        V = Vy*x_stress/R - Vx*y_stress/R  # shear stress orthogonal to direction x,y
        #        shear_stress = 2. * V / self.Az  # coefficient of 2 for a hollow circular section, but should be conservative for other shapes
        unknowns['axial_stress'] = Fz / params['Az'] - np.sqrt(
            Mxx**2 + Myy**2
        ) / params[
            'Iyy'] * d / 2.0  #More conservative, just use the tilted bending and add total max shear as well at the same point, if you do not like it go back to the previous lines

        unknowns['shear_stress'] = 2. * np.sqrt(Vx**2 + Vy**2) / params[
            'Az']  # coefficient of 2 for a hollow circular section, but should be conservative for other shapes

        # hoop_stress (Eurocode method)
        L_reinforced = params['L_reinforced'] * np.ones(Fz.shape)
        unknowns['hoop_stress_euro'] = hoopStressEurocode(
            params['z'], d, params['t'], L_reinforced, qdyn)

        # Simpler hoop stress used in API calculations
        unknowns['hoop_stress'] = hoopStress(d, params['t'], qdyn)
Beispiel #7
0
    def solve_nonlinear(self, params, unknowns, resids):

        # ------- node data ----------------
        z = params['z']
        n = len(z)
        node = np.arange(1, n + 1)
        x = np.zeros(n)
        y = np.zeros(n)
        r = np.zeros(n)

        nodes = frame3dd.NodeData(node, x, y, z, r)
        # -----------------------------------

        # ------ reaction data ------------

        # rigid base
        node = params['kidx'] + np.ones(
            len(params['kidx']), dtype=int
        )  # add one because 0-based index but 1-based node numbering
        rigid = float('inf')

        reactions = frame3dd.ReactionData(node, params['kx'], params['ky'],
                                          params['kz'], params['ktx'],
                                          params['kty'], params['ktz'], rigid)
        # -----------------------------------

        # ------ frame element data ------------
        element = np.arange(1, n)
        N1 = np.arange(1, n)
        N2 = np.arange(2, n + 1)

        roll = np.zeros(n - 1)

        # average across element b.c. frame3dd uses constant section elements
        Az = 0.5 * (params['Az'][:-1] + params['Az'][1:])
        Asx = 0.5 * (params['Asx'][:-1] + params['Asx'][1:])
        Asy = 0.5 * (params['Asy'][:-1] + params['Asy'][1:])
        Jz = 0.5 * (params['Jz'][:-1] + params['Jz'][1:])
        Ixx = 0.5 * (params['Ixx'][:-1] + params['Ixx'][1:])
        Iyy = 0.5 * (params['Iyy'][:-1] + params['Iyy'][1:])
        E = 0.5 * (params['E'][:-1] + params['E'][1:])
        G = 0.5 * (params['G'][:-1] + params['G'][1:])
        rho = 0.5 * (params['rho'][:-1] + params['rho'][1:])

        elements = frame3dd.ElementData(element, N1, N2, Az, Asx, Asy, Jz, Ixx,
                                        Iyy, E, G, roll, rho)
        # -----------------------------------

        # ------ options ------------
        options = frame3dd.Options(params['shear'], params['geom'],
                                   params['dx'])
        # -----------------------------------

        # initialize frame3dd object
        tower = frame3dd.Frame(nodes, reactions, elements, options)

        # ------ add extra mass ------------

        # extra node inertia data
        N = params['midx'] + np.ones(len(params['midx']), dtype=int)

        tower.changeExtraNodeMass(N, params['m'], params['mIxx'],
                                  params['mIyy'], params['mIzz'],
                                  params['mIxy'], params['mIxz'],
                                  params['mIyz'], params['mrhox'],
                                  params['mrhoy'], params['mrhoz'],
                                  params['addGravityLoadForExtraMass'])

        # ------------------------------------

        # ------- enable dynamic analysis ----------
        tower.enableDynamics(params['nM'], params['Mmethod'], params['lump'],
                             params['tol'], params['shift'])
        # ----------------------------

        # ------ static load case 1 ------------

        # gravity in the X, Y, Z, directions (global)
        gx = 0.0
        gy = 0.0
        gz = -params['g']

        load = frame3dd.StaticLoadCase(gx, gy, gz)

        # point loads
        nF = params['plidx'] + np.ones(len(params['plidx']), dtype=int)
        load.changePointLoads(nF, params['Fx'], params['Fy'], params['Fz'],
                              params['Mxx'], params['Myy'], params['Mzz'])

        # distributed loads
        Px, Py, Pz = params['Pz'], params['Py'], -params[
            'Px']  # switch to local c.s.
        z = params['z']

        # trapezoidally distributed loads
        EL = np.arange(1, n)
        xx1 = np.zeros(n - 1)
        xx2 = z[1:] - z[:-1] - np.ones(
            n - 1) * 1e-6  # subtract small number b.c. of precision
        wx1 = Px[:-1]
        wx2 = Px[1:]
        xy1 = np.zeros(n - 1)
        xy2 = z[1:] - z[:-1] - np.ones(n - 1) * 1e-6
        wy1 = Py[:-1]
        wy2 = Py[1:]
        xz1 = np.zeros(n - 1)
        xz2 = z[1:] - z[:-1] - np.ones(n - 1) * 1e-6
        wz1 = Pz[:-1]
        wz2 = Pz[1:]

        load.changeTrapezoidalLoads(EL, xx1, xx2, wx1, wx2, xy1, xy2, wy1, wy2,
                                    xz1, xz2, wz1, wz2)

        tower.addLoadCase(load)

        # -----------------------------------
        # run the analysis
        displacements, forces, reactions, internalForces, mass, modal = tower.run(
        )
        iCase = 0

        # mass
        unknowns['mass'] = mass.struct_mass

        # natural frequncies
        unknowns['f1'] = modal.freq[0]
        unknowns['f2'] = modal.freq[1]

        # deflections due to loading (from tower top and wind/wave loads)
        unknowns['top_deflection'] = displacements.dx[
            iCase, n - 1]  # in yaw-aligned direction

        # shear and bending (convert from local to global c.s.)
        Fz = forces.Nx[iCase, :]
        Vy = forces.Vy[iCase, :]
        Vx = -forces.Vz[iCase, :]

        Mzz = forces.Txx[iCase, :]
        Myy = forces.Myy[iCase, :]
        Mxx = -forces.Mzz[iCase, :]

        # one per element (first negative b.c. need reaction)
        Fz = np.concatenate([[-Fz[0]], Fz[1::2]])
        Vx = np.concatenate([[-Vx[0]], Vx[1::2]])
        Vy = np.concatenate([[-Vy[0]], Vy[1::2]])

        Mzz = np.concatenate([[-Mzz[0]], Mzz[1::2]])
        Myy = np.concatenate([[-Myy[0]], Myy[1::2]])
        Mxx = np.concatenate([[-Mxx[0]], Mxx[1::2]])

        # axial and shear stress
        ##R = self.d/2.0
        ##x_stress = R*np.cos(self.theta_stress)
        ##y_stress = R*np.sin(self.theta_stress)
        ##axial_stress = Fz/self.Az + Mxx/self.Ixx*y_stress - Myy/self.Iyy*x_stress
        #        V = Vy*x_stress/R - Vx*y_stress/R  # shear stress orthogonal to direction x,y
        #        shear_stress = 2. * V / self.Az  # coefficient of 2 for a hollow circular section, but should be conservative for other shapes
        axial_stress = Fz / params['Az'] - np.sqrt(
            Mxx**2 + Myy**2
        ) / params['Iyy'] * params[
            'd'] / 2.0  #More conservative, just use the tilted bending and add total max shear as well at the same point, if you do not like it go back to the previous lines

        shear_stress = 2. * np.sqrt(Vx**2 + Vy**2) / params[
            'Az']  # coefficient of 2 for a hollow circular section, but should be conservative for other shapes

        # hoop_stress (Eurocode method)
        hoop_stress = hoopStressEurocode(params['z'], params['d'], params['t'],
                                         params['L_reinforced'],
                                         params['qdyn'])

        # von mises stress
        unknowns['stress'] = vonMisesStressUtilization(
            axial_stress, hoop_stress, shear_stress,
            params['gamma_f'] * params['gamma_m'] * params['gamma_n'],
            params['sigma_y'])

        # shell buckling
        unknowns['shell_buckling'] = shellBucklingEurocode(
            params['d'], params['t'], axial_stress, hoop_stress, shear_stress,
            params['L_reinforced'], params['E'], params['sigma_y'],
            params['gamma_f'], params['gamma_b'])

        # global buckling
        tower_height = params['z'][-1] - params['z'][0]
        M = np.sqrt(Mxx**2 + Myy**2)
        unknowns['global_buckling'] = bucklingGL(params['d'], params['t'], Fz,
                                                 M, tower_height, params['E'],
                                                 params['sigma_y'],
                                                 params['gamma_f'],
                                                 params['gamma_b'])

        # fatigue
        N_DEL = [365 * 24 * 3600 * params['life']] * len(z)
        unknowns['damage'] = np.zeros(z.size)
        if any(params['M_DEL']):
            M_DEL = np.interp(z, params['z_DEL'], params['M_DEL'])

            unknowns['damage'] = fatigue(M_DEL,
                                         N_DEL,
                                         params['d'],
                                         params['t'],
                                         params['m_SN'],
                                         params['DC'],
                                         params['gamma_fatigue'],
                                         stress_factor=1.0,
                                         weld_factor=True)