def compute(self, inputs, outputs): frame3dd_opt = self.options["frame3dd_opt"] # ------- node data ---------------- z = inputs["z"] n = len(z) node = np.arange(1, n + 1) x = np.zeros(n) y = np.zeros(n) r = np.zeros(n) nodes = pyframe3dd.NodeData(node, x, y, z, r) # ----------------------------------- # ------ reaction data ------------ # rigid base node = inputs["kidx"] + np.ones(len(inputs["kidx"])) # add one because 0-based index but 1-based node numbering rigid = RIGID reactions = pyframe3dd.ReactionData( node, inputs["kx"], inputs["ky"], inputs["kz"], inputs["ktx"], inputs["kty"], inputs["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 = inputs["Az"] Asx = inputs["Asx"] Asy = inputs["Asy"] Jz = inputs["Jz"] Ixx = inputs["Ixx"] Iyy = inputs["Iyy"] E = inputs["E"] G = inputs["G"] rho = inputs["rho"] elements = pyframe3dd.ElementData(element, N1, N2, Az, Asx, Asy, Jz, Ixx, Iyy, E, G, roll, rho) # ----------------------------------- # ------ options ------------ dx = -1.0 options = pyframe3dd.Options(frame3dd_opt["shear"], frame3dd_opt["geom"], dx) # ----------------------------------- # initialize frame3dd object cylinder = pyframe3dd.Frame(nodes, reactions, elements, options) # ------ add extra mass ------------ # extra node inertia data N = inputs["midx"] + np.ones(len(inputs["midx"])) add_gravity = True cylinder.changeExtraNodeMass( N, inputs["m"], inputs["mIxx"], inputs["mIyy"], inputs["mIzz"], inputs["mIxy"], inputs["mIxz"], inputs["mIyz"], inputs["mrhox"], inputs["mrhoy"], inputs["mrhoz"], add_gravity, ) # ------------------------------------ # ------- enable dynamic analysis ---------- Mmethod = 1 lump = 0 shift = 0.0 # Run twice the number of modes to ensure that we can ignore the torsional modes and still get the desired number of fore-aft, side-side modes cylinder.enableDynamics(2 * NFREQ, Mmethod, lump, frame3dd_opt["tol"], shift) # ---------------------------- # ------ static load case 1 ------------ # gravity in the X, Y, Z, directions (global) gx = 0.0 gy = 0.0 gz = -gravity load = pyframe3dd.StaticLoadCase(gx, gy, gz) # point loads nF = inputs["plidx"] + np.ones(len(inputs["plidx"])) load.changePointLoads(nF, inputs["Fx"], inputs["Fy"], inputs["Fz"], inputs["Mxx"], inputs["Myy"], inputs["Mzz"]) # distributed loads Px, Py, Pz = inputs["Pz"], inputs["Py"], -inputs["Px"] # switch to local c.s. z = inputs["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 outputs["mass"] = mass.struct_mass # natural frequncies outputs["f1"] = modal.freq[0] outputs["f2"] = modal.freq[1] outputs["freqs"] = modal.freq[:NFREQ] # Get all mode shapes in batch NFREQ2 = int(NFREQ / 2) freq_x, freq_y, mshapes_x, mshapes_y = util.get_xy_mode_shapes( z, modal.freq, modal.xdsp, modal.ydsp, modal.zdsp, modal.xmpf, modal.ympf, modal.zmpf ) outputs["x_mode_freqs"] = freq_x[:NFREQ2] outputs["y_mode_freqs"] = freq_y[:NFREQ2] outputs["x_mode_shapes"] = mshapes_x[:NFREQ2, :] outputs["y_mode_shapes"] = mshapes_y[:NFREQ2, :] # deflections due to loading (from cylinder top and wind/wave loads) outputs["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 outputs["base_F"] = -1.0 * np.array([reactions.Fx.sum(), reactions.Fy.sum(), reactions.Fz.sum()]) outputs["base_M"] = -1.0 * np.array([reactions.Mxx.sum(), reactions.Myy.sum(), reactions.Mzz.sum()]) outputs["Fz_out"] = Fz outputs["Vx_out"] = Vx outputs["Vy_out"] = Vy outputs["Mxx_out"] = Mxx outputs["Myy_out"] = Myy outputs["Mzz_out"] = Mzz # axial and shear stress d, _ = util.nodal2sectional(inputs["d"]) qdyn, _ = util.nodal2sectional(inputs["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 outputs["axial_stress"] = ( Fz / inputs["Az"] - np.sqrt(Mxx ** 2 + Myy ** 2) / inputs["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 outputs["shear_stress"] = ( 2.0 * np.sqrt(Vx ** 2 + Vy ** 2) / inputs["Az"] ) # coefficient of 2 for a hollow circular section, but should be conservative for other shapes # hoop_stress (Eurocode method) L_reinforced = self.options["buckling_length"] * np.ones(Fz.shape) outputs["hoop_stress_euro"] = hoopStressEurocode(inputs["z"], d, inputs["t"], L_reinforced, qdyn) # Simpler hoop stress used in API calculations outputs["hoop_stress"] = hoopStress(d, inputs["t"], qdyn)
def compute(self, inputs, outputs): frame3dd_opt = self.options["frame3dd_opt"] # ------- node data ---------------- z = inputs["z"] n = len(z) node = np.arange(1, n + 1) x = np.zeros(n) y = np.zeros(n) r = np.zeros(n) nodes = pyframe3dd.NodeData(node, x, y, z, r) # ----------------------------------- # ------ reaction data ------------ # rigid base node = inputs["kidx"] + np.ones(len(inputs["kidx"])) # add one because 0-based index but 1-based node numbering rigid = RIGID reactions = pyframe3dd.ReactionData( node, inputs["kx"], inputs["ky"], inputs["kz"], inputs["ktx"], inputs["kty"], inputs["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) # Element properties Az = inputs["Az"] Asx = inputs["Asx"] Asy = inputs["Asy"] Jz = inputs["Jz"] Ixx = inputs["Ixx"] Iyy = inputs["Iyy"] E = inputs["E"] G = inputs["G"] rho = inputs["rho"] elements = pyframe3dd.ElementData(element, N1, N2, Az, Asx, Asy, Jz, Ixx, Iyy, E, G, roll, rho) # ----------------------------------- # ------ options ------------ dx = -1.0 options = pyframe3dd.Options(frame3dd_opt["shear"], frame3dd_opt["geom"], dx) # ----------------------------------- # initialize frame3dd object cylinder = pyframe3dd.Frame(nodes, reactions, elements, options) # ------ add extra mass ------------ # extra node inertia data N = inputs["midx"] + np.ones(len(inputs["midx"])) add_gravity = True cylinder.changeExtraNodeMass( N, inputs["m"], inputs["mIxx"], inputs["mIyy"], inputs["mIzz"], inputs["mIxy"], inputs["mIxz"], inputs["mIyz"], inputs["mrhox"], inputs["mrhoy"], inputs["mrhoz"], add_gravity, ) # ------------------------------------ # ------- enable dynamic analysis ---------- Mmethod = 1 lump = 0 shift = 0.0 # Run twice the number of modes to ensure that we can ignore the torsional modes and still get the desired number of fore-aft, side-side modes cylinder.enableDynamics(2 * NFREQ, Mmethod, lump, frame3dd_opt["tol"], shift) # ---------------------------- # ------ static load case 1 ------------ # gravity in the X, Y, Z, directions (global) gx = 0.0 gy = 0.0 gz = -gravity load = pyframe3dd.StaticLoadCase(gx, gy, gz) # point loads nF = inputs["plidx"] + np.ones(len(inputs["plidx"])) load.changePointLoads(nF, inputs["Fx"], inputs["Fy"], inputs["Fz"], inputs["Mxx"], inputs["Myy"], inputs["Mzz"]) # distributed loads Px, Py, Pz = inputs["Pz"], inputs["Py"], -inputs["Px"] # switch to local c.s. z = inputs["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() ic = 0 # mass outputs["mass"] = mass.struct_mass # natural frequncies outputs["f1"] = modal.freq[0] outputs["f2"] = modal.freq[1] outputs["structural_frequencies"] = modal.freq[:NFREQ] # Get all mode shapes in batch NFREQ2 = int(NFREQ / 2) freq_x, freq_y, freq_z, mshapes_x, mshapes_y, mshapes_z = util.get_xyz_mode_shapes( z, modal.freq, modal.xdsp, modal.ydsp, modal.zdsp, modal.xmpf, modal.ympf, modal.zmpf ) outputs["fore_aft_freqs"] = freq_x[:NFREQ2] outputs["side_side_freqs"] = freq_y[:NFREQ2] outputs["torsion_freqs"] = freq_z[:NFREQ2] outputs["fore_aft_modes"] = mshapes_x[:NFREQ2, :] outputs["side_side_modes"] = mshapes_y[:NFREQ2, :] outputs["torsion_modes"] = mshapes_z[:NFREQ2, :] # deflections due to loading (from cylinder top and wind/wave loads) outputs["tower_deflection"] = np.sqrt( displacements.dx[ic, :] ** 2 + displacements.dy[ic, :] ** 2 ) # in yaw-aligned direction outputs["top_deflection"] = outputs["tower_deflection"][-1] # Record total forces and moments ibase = 2 * int(inputs["kidx"].max()) outputs["base_F"] = -np.r_[-forces.Vz[ic, ibase], forces.Vy[ic, ibase], forces.Nx[ic, ibase]] outputs["base_M"] = -np.r_[-forces.Mzz[ic, ibase], forces.Myy[ic, ibase], forces.Txx[ic, ibase]] # Forces and moments along the structure outputs["tower_Fz"] = forces.Nx[ic, 1::2] outputs["tower_Vx"] = -forces.Vz[ic, 1::2] outputs["tower_Vy"] = forces.Vy[ic, 1::2] outputs["tower_Mxx"] = -forces.Mzz[ic, 1::2] outputs["tower_Myy"] = forces.Myy[ic, 1::2] outputs["tower_Mzz"] = forces.Txx[ic, 1::2]
dx = 20.0 # x-axis increment for internal forces other = pyframe3dd.Options(shear, geom, dx) # 3 ----------------------------------- # 4 ------ initialize frame3dd object frame = pyframe3dd.Frame(nodes, reactions, elements, other) # 4 ----------------------------------- # 5 ------ static load case 1 ------------ # gravity in the X, Y, Z, directions (global) gx = 0.0 gy = 0.0 gz = -9806.33 load = pyframe3dd.StaticLoadCase(gx, gy, gz) # point load nF = np.array([1]) Fx = np.array([100.0]) Fy = np.array([-200.0]) Fz = np.array([-100.0]) Mxx = np.array([0.0]) Myy = np.array([0.0]) Mzz = np.array([0.0]) load.changePointLoads(nF, Fx, Fy, Fz, Mxx, Myy, Mzz) frame.addLoadCase(load) # 5 -----------------------------------
def compute(self, inputs, outputs): # Unpack variables opt = self.options["options"] n_attach = opt["mooring"]["n_attach"] m_rna = float(inputs["rna_mass"]) cg_rna = inputs["rna_cg"] I_rna = inputs["rna_I"] I_trans = inputs["transition_piece_I"] fairlead_joints = inputs["mooring_fairlead_joints"] mooringF = inputs["mooring_neutral_load"] # Create frame3dd instance: nodes, elements, reactions, and options for frame in ["tower", "system"]: nodes = inputs[frame + "_nodes"] nnode = np.where(nodes[:, 0] == NULL)[0][0] nodes = nodes[:nnode, :] rnode = np.zeros(nnode) # inputs[frame + "_Rnode"][:nnode] Fnode = inputs[frame + "_Fnode"][:nnode, :] Mnode = np.zeros((nnode, 3)) ihub = np.argmax(nodes[:, 2]) - 1 itrans = util.closest_node(nodes, inputs["transition_node"]) N1 = np.int_(inputs[frame + "_elem_n1"]) nelem = np.where(N1 == NULL)[0][0] N1 = N1[:nelem] N2 = np.int_(inputs[frame + "_elem_n2"][:nelem]) A = inputs[frame + "_elem_A"][:nelem] Asx = inputs[frame + "_elem_Asx"][:nelem] Asy = inputs[frame + "_elem_Asy"][:nelem] Ixx = inputs[frame + "_elem_Ixx"][:nelem] Iyy = inputs[frame + "_elem_Iyy"][:nelem] Izz = inputs[frame + "_elem_Izz"][:nelem] rho = inputs[frame + "_elem_rho"][:nelem] E = inputs[frame + "_elem_E"][:nelem] G = inputs[frame + "_elem_G"][:nelem] roll = np.zeros(nelem) inodes = np.arange(nnode) + 1 node_obj = pyframe3dd.NodeData(inodes, nodes[:, 0], nodes[:, 1], nodes[:, 2], rnode) ielem = np.arange(nelem) + 1 elem_obj = pyframe3dd.ElementData(ielem, N1 + 1, N2 + 1, A, Asx, Asy, Izz, Ixx, Iyy, E, G, roll, rho) # TODO: Hydro_K + Mooring_K for tower (system too?) rid = np.array([itrans]) # np.array([np.argmin(nodes[:, 2])]) Rx = Ry = Rz = Rxx = Ryy = Rzz = np.array([RIGID]) react_obj = pyframe3dd.ReactionData(rid + 1, Rx, Ry, Rz, Rxx, Ryy, Rzz, rigid=RIGID) frame3dd_opt = opt["WISDEM"]["FloatingSE"]["frame3dd"] opt_obj = pyframe3dd.Options(frame3dd_opt["shear"], frame3dd_opt["geom"], -1.0) myframe = pyframe3dd.Frame(node_obj, react_obj, elem_obj, opt_obj) # Added mass m_trans = float(inputs["transition_piece_mass"]) if frame == "tower": # TODO: Added mass and stiffness m_trans += float(inputs["platform_mass"]) cg_trans = inputs["transition_node"] - inputs[ "platform_center_of_mass"] else: cg_trans = np.zeros(3) add_gravity = True mID = np.array([itrans, ihub], dtype=np.int_).flatten() m_add = np.array([m_trans, m_rna]).flatten() I_add = np.c_[I_trans, I_rna] cg_add = np.c_[cg_trans, cg_rna] myframe.changeExtraNodeMass( mID + 1, m_add, I_add[0, :], I_add[1, :], I_add[2, :], I_add[3, :], I_add[4, :], I_add[5, :], cg_add[0, :], cg_add[1, :], cg_add[2, :], add_gravity, ) # Dynamics if frame == "tower" and frame3dd_opt["modal"]: Mmethod = 1 lump = 0 shift = 0.0 myframe.enableDynamics(2 * NFREQ, Mmethod, lump, frame3dd_opt["tol"], shift) # Initialize loading with gravity, mooring line forces, and buoyancy (already in nodal forces) gx = gy = 0.0 gz = -gravity load_obj = pyframe3dd.StaticLoadCase(gx, gy, gz) if frame == "system": for k in range(n_attach): ind = util.closest_node(nodes, fairlead_joints[k, :]) Fnode[ind, :] += mooringF[k, :] Fnode[ihub, :] += inputs["rna_F"] Mnode[ihub, :] += inputs["rna_M"] nF = np.where(np.abs(Fnode).sum(axis=1) > 0.0)[0] load_obj.changePointLoads(nF + 1, Fnode[nF, 0], Fnode[nF, 1], Fnode[nF, 2], Mnode[nF, 0], Mnode[nF, 1], Mnode[nF, 2]) # Add the load case and run myframe.addLoadCase(load_obj) # myframe.write(frame + ".3dd") displacements, forces, reactions, internalForces, mass, modal = myframe.run( ) # natural frequncies if frame == "tower" and frame3dd_opt["modal"]: outputs[frame + "_freqs"] = modal.freq[:NFREQ] # Get all mode shapes in batch NFREQ2 = int(NFREQ / 2) freq_x, freq_y, mshapes_x, mshapes_y = util.get_xy_mode_shapes( nodes[:, 2], modal.freq, modal.xdsp, modal.ydsp, modal.zdsp, modal.xmpf, modal.ympf, modal.zmpf) outputs[frame + "_fore_aft_freqs"] = freq_x[:NFREQ2] outputs[frame + "_side_side_freqs"] = freq_y[:NFREQ2] outputs[frame + "_fore_aft_modes"] = mshapes_x[:NFREQ2, :] outputs[frame + "_side_side_modes"] = mshapes_y[:NFREQ2, :] # Determine forces F_sum = -1.0 * np.array( [reactions.Fx.sum(), reactions.Fy.sum(), reactions.Fz.sum()]) M_sum = -1.0 * np.array([ reactions.Mxx.sum(), reactions.Myy.sum(), reactions.Mzz.sum() ]) L = np.sqrt(np.sum((nodes[N2, :] - nodes[N1, :])**2, axis=1))
def run_hcurve(FrIn, optFlag=True): Fr = FrIn.reshape((self.n_span-1, 2)) # Will only worry about radial loads Fy = Mx = My = Mz = np.zeros(self.n_span-1) # Output containers RF_derailH = np.zeros(2) # Derailment reaction force r_check = np.zeros((self.n_span, 2)) # Envelope constraint strainPS = np.zeros((self.n_span, 2)) strainSS = np.zeros((self.n_span, 2)) # Look over bend to PS/SS cases for k in range(2): if k == 0: blade, r_outer, angs = blade1, r_envelopeH_outer1, arcsH[1:] else: blade, r_outer, angs = blade2, r_envelopeH_outer2, np.pi-arcsH[1:] # Load case: gravity + blade bending to conform to outer boundary load = pyframe3dd.StaticLoadCase(gx, gy, gz) # Put radial loads in x,z plane Fx = -1e4*Fr[:,k]*np.cos(angs) Fz = -1e4*Fr[:,k]*np.sin(angs) load.changePointLoads(inode[1:], Fx, Fy, Fz, Mx, My, Mz) # Add load case to Frame3DD objects and run blade.clearLoadCases() blade.addLoadCase(load) # Run the case displacements, forces, forces_rxn, internalForces, mass, modal = blade.run() # Check solved blade shape against envelope r_check[:,k] = np.sqrt( (blade.nx+displacements.dx[0,:])**2 + (blade.nz+displacements.dz[0,:])**2) - r_outer # Derailing reaction force on root node # - Lateral force on wheels (multiply by 0.5 for 2 wheel sets) # - Moment around axis perpendicular to ground RF_derailH[k] = 0.5*np.sqrt(forces_rxn.Fx**2 + forces_rxn.Fz**2) + np.abs(forces_rxn.Myy)/flatcar_tc_length # Element shear and bending, one per element, which are already in principle directions in Hansen's notation # Zero-ing out axial stress as there shouldn't be any for pure beam bending Fz = np.r_[-forces.Nx[ 0,0], forces.Nx[ 0, 1::2]] M1 = np.r_[-forces.Myy[0,0], forces.Myy[0, 1::2]] M2 = np.r_[ forces.Mzz[0,0], -forces.Mzz[0, 1::2]] if PBEAM: M1,M2 = rotate(M1,M2) # Compute strain at the two points: pressure/suction side extremes strainPS[:,k] = -(M1/EI11*ps2 - M2/EI22*ps1 + Fz/EA) # negative sign because Hansen c3 is opposite of Precomp z strainSS[:,k] = -(M1/EI11*ss2 - M2/EI22*ss1 + Fz/EA) if optFlag: # First constraint is compliance with outer boundary cboundary = np.maximum(r_check, 0) # Second constraint is reaction forces for derailment: crxn = np.maximum(RF_derailH - (0.5 * mass_car_8axle * gravity)/max_LV, 0.0) # Third constraint is keeping the strains reasonable cstrainPS = np.maximum(np.abs(strainPS) - max_strains, 0.0) cstrainSS = np.maximum(np.abs(strainSS) - max_strains, 0.0) # Accumulate constraints cons = np.array([np.sum(cboundary) , np.sum(crxn) , np.sum(cstrainPS) , np.sum(cstrainSS)]) return -cons else: return RF_derailH, strainPS, strainSS
def compute(self, inputs, outputs): frame3dd_opt = self.options['frame3dd_opt'] # ------- node data ---------------- z = inputs['z'] n = len(z) node = np.arange(1, n + 1) x = np.zeros(n) y = np.zeros(n) r = np.zeros(n) nodes = pyframe3dd.NodeData(node, x, y, z, r) # ----------------------------------- # ------ reaction data ------------ # rigid base node = inputs['kidx'] + np.ones( len(inputs['kidx'] )) # add one because 0-based index but 1-based node numbering rigid = RIGID reactions = pyframe3dd.ReactionData(node, inputs['kx'], inputs['ky'], inputs['kz'], inputs['ktx'], inputs['kty'], inputs['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 = inputs['Az'] Asx = inputs['Asx'] Asy = inputs['Asy'] Jz = inputs['Jz'] Ixx = inputs['Ixx'] Iyy = inputs['Iyy'] E = inputs['E'] G = inputs['G'] rho = inputs['rho'] elements = pyframe3dd.ElementData(element, N1, N2, Az, Asx, Asy, Jz, Ixx, Iyy, E, G, roll, rho) # ----------------------------------- # ------ options ------------ dx = -1.0 options = pyframe3dd.Options(frame3dd_opt['shear'], frame3dd_opt['geom'], dx) # ----------------------------------- # initialize frame3dd object cylinder = pyframe3dd.Frame(nodes, reactions, elements, options) # ------ add extra mass ------------ # extra node inertia data N = inputs['midx'] + np.ones(len(inputs['midx'])) add_gravity = True cylinder.changeExtraNodeMass(N, inputs['m'], inputs['mIxx'], inputs['mIyy'], inputs['mIzz'], inputs['mIxy'], inputs['mIxz'], inputs['mIyz'], inputs['mrhox'], inputs['mrhoy'], inputs['mrhoz'], add_gravity) # ------------------------------------ # ------- enable dynamic analysis ---------- Mmethod = 1 lump = 0 shift = 0.0 cylinder.enableDynamics(NFREQ, Mmethod, lump, frame3dd_opt['tol'], shift) # ---------------------------- # ------ static load case 1 ------------ # gravity in the X, Y, Z, directions (global) gx = 0.0 gy = 0.0 gz = -gravity load = pyframe3dd.StaticLoadCase(gx, gy, gz) # point loads nF = inputs['plidx'] + np.ones(len(inputs['plidx'])) load.changePointLoads(nF, inputs['Fx'], inputs['Fy'], inputs['Fz'], inputs['Mxx'], inputs['Myy'], inputs['Mzz']) # distributed loads Px, Py, Pz = inputs['Pz'], inputs['Py'], -inputs[ 'Px'] # switch to local c.s. z = inputs['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 outputs['mass'] = mass.struct_mass # natural frequncies outputs['f1'] = modal.freq[0] outputs['f2'] = modal.freq[1] outputs['freqs'] = modal.freq # Get all mode shapes in batch freq_x, freq_y, mshapes_x, mshapes_y = util.get_mode_shapes( z, modal.freq, modal.xdsp, modal.ydsp, modal.zdsp, modal.xmpf, modal.ympf, modal.zmpf) outputs['x_mode_freqs'] = freq_x outputs['y_mode_freqs'] = freq_y outputs['x_mode_shapes'] = mshapes_x outputs['y_mode_shapes'] = mshapes_y # deflections due to loading (from cylinder top and wind/wave loads) outputs['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 outputs['base_F'] = -1.0 * np.array( [reactions.Fx.sum(), reactions.Fy.sum(), reactions.Fz.sum()]) outputs['base_M'] = -1.0 * np.array( [reactions.Mxx.sum(), reactions.Myy.sum(), reactions.Mzz.sum()]) outputs['Fz_out'] = Fz outputs['Vx_out'] = Vx outputs['Vy_out'] = Vy outputs['Mxx_out'] = Mxx outputs['Myy_out'] = Myy outputs['Mzz_out'] = Mzz # axial and shear stress d, _ = util.nodal2sectional(inputs['d']) qdyn, _ = util.nodal2sectional(inputs['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 outputs['axial_stress'] = Fz / inputs['Az'] - np.sqrt( Mxx**2 + Myy**2 ) / inputs[ '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 outputs['shear_stress'] = 2. * np.sqrt(Vx**2 + Vy**2) / inputs[ 'Az'] # coefficient of 2 for a hollow circular section, but should be conservative for other shapes # hoop_stress (Eurocode method) L_reinforced = self.options['buckling_length'] * np.ones(Fz.shape) outputs['hoop_stress_euro'] = hoopStressEurocode( inputs['z'], d, inputs['t'], L_reinforced, qdyn) # Simpler hoop stress used in API calculations outputs['hoop_stress'] = hoopStress(d, inputs['t'], qdyn)