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)
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)
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
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)
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)
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)
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)