class UtilAssembly(Assembly): # inputs #RNA_FM = Array(np.zeros([3,2]),iotype='in', desc='rotor aerodynamic forces & moments in hub-aligned,yawed coordinate system') RNA_F = Array(np.zeros([3]),iotype='in', desc='rotor aerodynamic forces in hub-aligned,yawed coordinate system') RNA_M = Array(np.zeros([3]),iotype='in', desc='rotor aerodynamic moments in hub-aligned,yawed coordinate system') rna_weightM = Bool(True, iotype='in', desc='flag to consider or not the RNA weight effect on Moment') r_hub = Array(np.zeros([3]),iotype='in', desc='position of rotor hub relative to tower top in yaw-aligned c.s.') r_cm = Array(np.zeros([3]),iotype='in', desc='position of RNA CM relative to tower top in yaw-aligned c.s.') tilt = Float(iotype='in', units='deg') g = Float(9.81, iotype='in', units='m/s**2', desc='Gravity Acceleration (ABSOLUTE VALUE!)') towerWindLoads = VarTree(FluidLoads(), iotype='in', desc='Aero loads in inertial coordinate system') towerWaveLoads = VarTree(FluidLoads(), iotype='in', desc='Hydro loads in inertial coordinate system') Dt = Float(iotype='in', units='m', desc='TowerTop OD from Tower') tt = Float(iotype='in', units='m', desc='TowerTop wall thickness from Tower') Twr_data = VarTree(TwrGeoOutputs(), iotype='in', desc='Tower Node data') # From Tower L_reinforced = Float(30.0, iotype='in', desc='reinforcement length') IECpsfIns = VarTree(IEC_PSFS(), iotype='in', desc='Basic IEC psfs') # outputs tower_utilization = VarTree(TowerUtilOutputs(), iotype='out', desc='Tower Utilization Basic Outputs') jacket_utilization = VarTree(JacketUtilOutputs(), iotype='out', desc='Jacket Utilization Basic Outputs') def configure(self): self.add('PrepArray',PrepArray()) self.add('rotorLoads', RotorLoads()) self.add('WWloads', AeroHydroLoads()) self.add('TwrUtil', TwrUtilization()) self.add('JcktUtil', JcktUtilization()) #self.driver.workflow.add(['PrepArray', 'rotorLoads', 'JcktUtil', 'TwrUtil']) self.driver.workflow.add(['rotorLoads', 'JcktUtil', 'TwrUtil']) #connections to PrepArray #self.connect('RNA_FM', 'PrepArray.RNA_FM') # connections to rotorLoads self.connect('RNA_F', 'rotorLoads.F') self.connect('RNA_M', 'rotorLoads.M') self.connect('rna_weightM','rotorLoads.rna_weightM') self.connect('r_hub', 'rotorLoads.r_hub') self.connect('r_cm', 'rotorLoads.rna_cm') self.connect('Twr_data.TopMass[0]', 'rotorLoads.m_RNA') self.connect('tilt', 'rotorLoads.tilt') self.connect('g', 'rotorLoads.g') #connections to WWLoads self.connect('towerWindLoads', 'WWloads.windLoads') self.connect('towerWaveLoads', 'WWloads.waveLoads') self.connect('towerWindLoads.z', 'WWloads.z') # connections to TwrUtil self.connect('rotorLoads.top_F', 'TwrUtil.top_F') self.connect('rotorLoads.top_M', 'TwrUtil.top_M') ##self.connect('towerWindLoads', 'TwrUtil.towerWindLoads') ##self.connect('towerWaveLoads', 'TwrUtil.towerWaveLoads') self.connect('WWloads.outloads', 'TwrUtil.towerWWLoads') #self.connect('yaw', 'distLoads.yaw') set yaw=0, i.e. the rotor is aligned with the wind, although 45 deg from global x self.connect('Dt', 'TwrUtil.Dt') self.connect('tt', 'TwrUtil.tt') self.connect('Twr_data', 'TwrUtil.Twr_data') self.connect('L_reinforced', 'TwrUtil.L_reinforced') self.connect('IECpsfIns', 'TwrUtil.IECpsfIns') self.connect('g', 'TwrUtil.g') #Connections to inputs of JcktUtilization self.create_passthrough('JcktUtil.Legmems') self.create_passthrough('JcktUtil.Pilemems') self.create_passthrough('JcktUtil.Twrmems') self.create_passthrough('JcktUtil.TPmems') self.create_passthrough('JcktUtil.MbrFrcs') self.create_passthrough('JcktUtil.nlegs') self.create_passthrough('JcktUtil.XjntIDs') self.create_passthrough('JcktUtil.KjntIDs') self.create_passthrough('JcktUtil.JcktGeoOut') #Connections to outputs self.connect('TwrUtil.utilization', 'tower_utilization') self.connect('JcktUtil.Utilouts', 'jacket_utilization')
class JcktLoad(Assembly): # normally these connections come from other components nlegs = Int(iotype='in') # comes from JcktGeoIn.nlegs nodes = Array(iotype='in') # comes from JcktGeoOut.nodes pillegDs = Array(dtype=np.float, units='m', iotype='in', desc='ODs of pile and leg #1.') twrDs = Array(units='m', iotype='in', desc='ODs of Tower.') #twrTs = Array(iotype='in') Twrmems = Array( dtype=int, iotype='in', desc= 'Connectivity Array for all members of the tower portion only (including rigid member at the top if requested)' ) Legmems = Array( dtype=int, iotype='in', desc='Connectivity Array for all members of the Leg 1 portion only') Pilemems = Array( dtype=int, iotype='in', desc='Connectivity Array for all members of the Pile 1 portion only ') VPFlag = Bool( False, units=None, iotype='in', desc= 'Vertical Pile Flag [Y/N]: If True the Mudbrace is put at the expected joint with Piles, i.e. at the bottom of leg.' ) al_bat3D = Float(units='rad', iotype='in', desc='Batter Angle in 3D.') RNA_F = Array( dtype=np.float, iotype='in', desc='Unfactored Rotor Forces and Moments excluding weight. Array(6).') TwrRigidTop = Bool(units=None, iotype='in', desc='Rigid Member used in tower top or not.') RNAinputs = VarTree(RNAprops(), iotype='in', desc='Basic Inertial Properties of RNA') #CMzoff =Float( units='m', iotype='in',desc='RNA CM z offset from Tower Top Flange as It comes out of Tower Processing (trumped by RigidTop incase)') # RNA CMzoff [m] gravity = Float(units='m/s**2', iotype='in', desc='Gravity Acceleration (ABSOLUTE VALUE!)') windIns = VarTree(WindInputs(), iotype='in') waterIns = VarTree(WaterInputs(), iotype='in') # #Following inputs are for utilization, I would keep this separate # topF = Array(iotype='in') # topM = Array(iotype='in') # n_reinforced = Int(3, iotype='in') # E = Float(210e9, iotype='in', units='N/m**2', desc='material modulus of elasticity') # sigma_y = Float(450.0e6, iotype='in', units='N/m**2', desc='yield stress') # gamma_f = Float(1.35, iotype='in', desc='safety factor on loads') # gamma_m = Float(1.1, iotype='in', desc='safety factor on materials') # gamma_n = Float(1.0, iotype='in', desc='safety factor on consequence of failure') #inputs # JcktGeoIn= VarTree(JcktGeoInputs(), iotype='in', desc='Jacket Geometry Basic Inputs') # JcktGeoOut = VarTree(JcktGeoOutputs(), iotype='in', desc='Geometry of the Jacket -Node Coordinates and Member Connectivity') # gravity =Float( units='m/s**2',iotype='in',desc='Acceleration Gravity.') #outputs Loadouts = VarTree(LoadOutputs(), iotype='out', desc='Jacket Loading Basic Outputs') twrWindLoads = VarTree( FluidLoads(), iotype='out', desc='Tower wind loads in inertial coordinate system') twrWaveLoads = VarTree( FluidLoads(), iotype='out', desc='Tower wave loads in inertial coordinate system') #stress = Array(iotype='out', units='N/m**2', desc='von Mises stress along tower on downwind side (yaw-aligned +x). normalized by yield stress. includes safety factors.') #z_buckling = Array(iotype='out', units='m', desc='z-locations along tower where shell buckling is evaluted') #buckling = Array(iotype='out', desc='a shell buckling constraint. should be <= 0 for feasibility. includes safety factors') def configure(self): self.add('pre', JcktLoadPre()) self.add('windj', PowerWind()) self.add('windt', PowerWind()) self.add('wavej', LinearWaves()) self.add('wavet', LinearWaves()) self.add('windLoadsj', TowerWindDrag()) self.add('windLoadst', TowerWindDrag()) self.add('waveLoadsj', TowerWaveDrag()) self.add('waveLoadst', TowerWaveDrag()) self.add('post', JcktLoadPost()) self.driver.workflow.add([ 'pre', 'windj', 'windt', 'wavej', 'wavet', 'windLoadsj', 'windLoadst', 'waveLoadsj', 'waveLoadst', 'post' ]) # connections to pre self.connect('nlegs', 'pre.nlegs') self.connect('nodes', 'pre.nodes') self.connect('pillegDs', 'pre.pillegDs') self.connect('twrDs', 'pre.twrDs') #self.connect('twrTs', 'pre.twrTs') self.connect('Twrmems', 'pre.Twrmems') self.connect('Legmems', 'pre.Legmems') self.connect('Pilemems', 'pre.Pilemems') # connections to windj/t self.connect('windIns.U50HH', ['windj.Uref', 'windt.Uref']) self.connect('windIns.HH + waterIns.wdepth + waterIns.z_floor', ['windj.zref', 'windt.zref']) self.connect('pre.pillegZs_out', 'windj.z') self.connect('pre.twrZs_out', 'windt.z') self.connect('waterIns.wdepth + waterIns.z_floor', ['windj.z0', 'windt.z0']) self.connect('windIns.psi', ['windj.betaWind', 'windt.betaWind']) self.connect('windIns.al_shear', ['windj.shearExp', 'windt.shearExp']) # connections to wavej/t self.connect('waterIns.Uc', ['wavej.Uc', 'wavet.Uc']) self.connect('waterIns.wdepth + waterIns.z_floor', ['wavej.z_surface', 'wavet.z_surface']) self.connect('waterIns.HW', ['wavej.hmax', 'wavet.hmax']) self.connect('waterIns.wdepth', ['wavej.wdepth', 'wavet.wdepth']) #CJB+ # self.connect('waterIns.T', ['wavej.T', 'wavet.T']) self.connect('waterIns.T', 'wavej.T') self.connect('waterIns.T', 'wavet.T') self.connect('waterIns.z_floor', ['wavej.z_floor', 'wavet.z_floor']) self.connect('gravity', ['wavej.g', 'wavet.g']) self.connect('waterIns.psi', ['wavej.betaWave', 'wavet.betaWave']) self.connect('pre.pillegZs_out', 'wavej.z') self.connect('pre.twrZs_out', 'wavet.z') # connections to windLoadsj self.connect('windj.U', 'windLoadsj.U') self.connect('windj.beta', 'windLoadsj.beta') self.connect('windIns.rho', 'windLoadsj.rho') self.connect('windIns.mu', 'windLoadsj.mu') self.connect('windIns.Cdj', 'windLoadsj.cd_usr') self.connect('pre.pillegZs_out', 'windLoadsj.z') self.connect('pre.pillegDs_out', 'windLoadsj.d') # connections to windLoadst self.connect('windt.U', 'windLoadst.U') self.connect('windt.beta', 'windLoadst.beta') self.connect('windIns.rho', 'windLoadst.rho') self.connect('windIns.mu', 'windLoadst.mu') self.connect('windIns.Cdt', 'windLoadst.cd_usr') self.connect('pre.twrZs_out', 'windLoadst.z') self.connect('pre.twrDs_out', 'windLoadst.d') # connections to waveLoadsj self.connect('wavej.U', 'waveLoadsj.U') self.connect('wavej.A', 'waveLoadsj.A') self.connect('wavej.beta', 'waveLoadsj.beta') self.connect('wavej.U0', 'waveLoadsj.U0') self.connect('wavej.A0', 'waveLoadsj.A0') self.connect('wavej.beta0', 'waveLoadsj.beta0') self.connect('waterIns.rho', 'waveLoadsj.rho') self.connect('waterIns.mu', 'waveLoadsj.mu') self.connect('waterIns.Cm', 'waveLoadsj.cm') self.connect('waterIns.Cd', 'waveLoadsj.cd_usr') self.connect('waterIns.wlevel', 'waveLoadsj.wlevel') self.connect('pre.pillegZs_out', 'waveLoadsj.z') self.connect('pre.pillegDs_out', 'waveLoadsj.d') # connections to waveLoadst self.connect('wavet.U', 'waveLoadst.U') self.connect('wavet.A', 'waveLoadst.A') self.connect('wavet.beta', 'waveLoadst.beta') self.connect('wavet.U0', 'waveLoadst.U0') self.connect('wavet.A0', 'waveLoadst.A0') self.connect('wavet.beta0', 'waveLoadst.beta0') self.connect('waterIns.rho', 'waveLoadst.rho') self.connect('waterIns.mu', 'waveLoadst.mu') self.connect('waterIns.Cm', 'waveLoadst.cm') self.connect('waterIns.Cd', 'waveLoadst.cd_usr') self.connect('waterIns.wlevel', 'waveLoadst.wlevel') self.connect('pre.twrZs_out', 'waveLoadst.z') self.connect('pre.twrDs_out', 'waveLoadst.d') # connections to post self.connect('windLoadst.windLoads', 'post.towerWindLoads') self.connect('waveLoadst.waveLoads', 'post.towerWaveLoads') self.connect('windLoadsj.windLoads', 'post.pileLegWindLoads') self.connect('waveLoadsj.waveLoads', 'post.pileLegWaveLoads') self.connect('pre.pilendIDs', 'post.pilendIDs') self.connect('pre.legndIDs', 'post.legndIDs') self.connect('pre.twrndIDs', 'post.twrndIDs') self.connect('nlegs', 'post.nlegs') self.connect('TwrRigidTop', 'post.TwrRigidTop') self.connect('RNAinputs', 'post.RNAinputs') self.connect('RNA_F', 'post.RNA_F') self.connect('al_bat3D', 'post.al_bat3D') self.connect('VPFlag', 'post.VPFlag') self.connect('waterIns.wdepth', 'post.wdepth') # connections to outputs self.connect('post.Loadouts', 'Loadouts') #self.connect('windLoadst.windLoads','twrWindLoads') #self.connect('waveLoadst.waveLoads','twrWaveLoads') self.create_passthrough('windLoadst.windLoads') self.create_passthrough('waveLoadst.waveLoads')
class TwrUtilization(Component): "THIS UTILIZATION WORKS WITH WIND AND MAIN THRUST LOADS ALONG GLOBAL X ONLY" #inputs ##towerWindLoads = VarTree(FluidLoads(), iotype='in', desc='Aero loads in inertial coordinate system.') ##towerWaveLoads = VarTree(FluidLoads(), iotype='in', desc='Hydro loads in inertial coordinate system.') towerWWLoads=VarTree(FluidLoads(), iotype='in', desc='Combined distributed loads and z coordinates as coming from AeroHydroLoads component.') top_F = Array(iotype='in') top_M = Array(iotype='in') Dt = Float(iotype='in', units='m', desc='TowerTop OD from Tower.') tt = Float(iotype='in', units='m', desc='TowerTop wall thickness from Tower.') Twr_data = VarTree(TwrGeoOutputs(), iotype='in', desc='Tower Node data.') # From Tower L_reinforced = Float(30.0, iotype='in', desc='reinforcement length.') IECpsfIns= VarTree(IEC_PSFS(), iotype='in', desc='Basic IEC psfs.') g = Float(9.81, iotype='in', units='m/s**2', desc='Gravity Acceleration (ABSOLUTE VALUE!).') #outputs utilization = VarTree(TowerUtilOutputs(), iotype='out') # stress = Array(iotype='out', units='N/m**2', desc='von Mises stress along tower on downwind side (yaw-aligned +x). normalized by yield stress. includes safety factors.') # shell_buckling = Array(iotype='out', desc='shell buckling utilization. should be <= 1 for feasibility. includes safety factors') # tower_buckling = Array(iotype='out', desc='tower buckling constraint. should be <= 1 for feasibility. includes safety factors') 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
class JcktLoadPost(Component): # inputs towerWindLoads = VarTree(FluidLoads(), iotype='in', desc='aero loads in inertial coordinate system') towerWaveLoads = VarTree(FluidLoads(), iotype='in', desc='aero loads in inertial coordinate system') pileLegWindLoads = VarTree(FluidLoads(), iotype='in', desc='aero loads in inertial coordinate system') pileLegWaveLoads = VarTree(FluidLoads(), iotype='in', desc='aero loads in inertial coordinate system') nlegs = Int(units=None, iotype='in', desc='Number of Jacket Legs') # comes from JcktGeoIn.nlegs al_bat3D = Float(units='rad', iotype='in', desc='Batter Angle in 3D.') VPFlag = Bool( False, units=None, iotype='in', desc= 'Vertical Pile Flag [Y/N]: If True the Mudbrace is put at the expected joint with Piles, i.e. at the bottom of leg.' ) RNA_F = Array( dtype=np.float, iotype='in', desc= 'Unfactored Rotor Forces and Moments excluding weight, aligned with rotor CS. Array(6)' ) RNAinputs = VarTree(RNAprops(), iotype='in', desc='Basic Inertial Properties of RNA') TwrRigidTop = Bool(units=None, iotype='in', desc='Rigid Member used in tower top or not') #CMzoff=Float( units='m', iotype='in',desc='RNA CM z offset from Tower Top Flange as It comes out of Tower Processing (trumped by RigidTop incase)') # RNA CMzoff [m] legndIDs = Array(iotype='in', units=None, desc='Node IDs for the legs') twrndIDs = Array(iotype='in', units=None, desc='Node IDs for the tower') pilendIDs = Array(iotype='in', units=None, desc='Node IDs for the piles') wdepth = Float( iotype='in', units='m', desc= 'Water Depth, needed to refine deltaz at the node just below surface') # outputs Loadouts = VarTree(LoadOutputs(), iotype='out', desc='Jacket Loading Basic Outputs') def execute(self): #simplify nomenclature psi_wi = self.towerWindLoads.beta[ 0] #psi is stored in beta already, however for legs and piles it needs to be adjusted for psi psi_wa = self.towerWaveLoads.beta[0] twrZs = self.towerWindLoads.z pillegZs = self.pileLegWaveLoads.z pillegDs = self.pileLegWaveLoads.d nlegs = self.nlegs al_bat3D = self.al_bat3D VPFlag = self.VPFlag TwrRigidTop = ( self.RNAinputs.CMoff[2] != 0. ) and self.TwrRigidTop #This makes sure we do not have TwrRigidTop=True with CMzoff=0., no length segment that is. pilendIDs = self.pilendIDs legndIDs = self.legndIDs twrndIDs = self.twrndIDs wdepth = self.wdepth #COSINE MATRIX FROM LOCAL TO GLOBAL COORDINATE SYSTEMS DIRCOSwind = np.array([[cosd(psi_wi), -sind(psi_wi), 0.], [sind(psi_wi), cosd(psi_wi), 0.], [0., 0., 1.]]) DIRCOSwave = np.array([[cosd(psi_wa), -sind(psi_wa), 0.], [sind(psi_wa), cosd(psi_wa), 0.], [0., 0., 1.]]) #I need to get the right node IDs to be able to assign forces for Frame3DD #Get the values of the loads at the nodes of pile and leg 1 #for the other legs, the wdrag is going to be the same if nlegs == 4: #Diagonal loading condition ONLY for the time being pileidx = np.array([]) #Initialize in case empty piles if pilendIDs.size: pileidx = pilendIDs[ 0:pilendIDs.size / nlegs] #np.arange(Pilemems[0,0],Pilemems[-1,1]/nlegs+1)-1 #indices of pile 1 legidx = legndIDs[ 0:legndIDs.size / nlegs] #legidx=np.arange(Legmems[0,0],Legmems[-1,1]/nlegs+1)-1 #indices of leg 1 deltaz = ( (np.roll(pillegZs, -1) - pillegZs) / 2 )[0: -1] #these are the appropriate 1/2 deltazs for the entire pile-leg assembly deltaz = np.hstack( (deltaz[0], (np.roll(deltaz, -1) + deltaz)[0:-1], deltaz[-1]) ) #This is the actual DeltaZ to be assigned at each node starting from the 2nd and ending at the one before last #Correct the delta z for the node just below water to avoid jumps in loading - use refined mesh however #idx_bw=np.nonzero(pillegZs<= wdepth)[0][-1] #CJB- Original code. Modified line below idx_bw = np.nonzero(pillegZs <= 0)[0][ -1] #CJBe THe MSL is now at z=0, not z=wdepth #Also Attempt at using load at z=0 ###Px0=self.pileLegWaveLoads.Px_i0overd2*pillegDs[idx_bw]**2+self.pileLegWaveLoads.Px_d0overd*pillegDs[idx_bw] ###Py0=self.pileLegWaveLoads.Py_i0overd2*pillegDs[idx_bw]**2+self.pileLegWaveLoads.Py_d0overd*pillegDs[idx_bw] Px0 = self.pileLegWaveLoads.Px0 Py0 = self.pileLegWaveLoads.Py0 #deltaz0=(wdepth-pillegZs[idx_bw]) #CJB- Original code. Modified line below deltaz0 = np.abs(0 - pillegZs[idx_bw]) #CJBe #if (wdepth-pillegZs[idx_bw])> deltaz[idx_bw]/2.: #point with its deltaz entriely below surface #CJB- Original code. Modified line below if np.abs(0 - pillegZs[idx_bw]) > np.abs( deltaz[idx_bw] / 2.): #CJBe Remove wdepth deltaz0 -= deltaz[ idx_bw] / 2. #note deltaz0 before deltaz as deltaz gets modified #deltaz[idx_bw]=deltaz[idx_bw]/2. -(pillegZs[idx_bw]-wdepth) else: deltaz[idx_bw] = deltaz[idx_bw] / 2. waDrag0 = np.sqrt( Px0**2. + Py0**2. ) * deltaz0 #In case add this one to the original wDrag, or alternatively do awhat I do below, increasing Deltaz waDrag = np.sqrt( self.pileLegWaveLoads.Px**2. + self.pileLegWaveLoads.Py**2. ) * deltaz #[N] forces from waves, considered normal to the sloping 1 leg. waDrag[idx_bw] += waDrag0 wiDrag = np.sqrt( self.pileLegWindLoads.Px**2. + self.pileLegWindLoads.Py**2. ) * deltaz * ( np.cos(al_bat3D) )**2 #[N] forces from wind normal to the sloping 1 leg. Wind is horizontal, that's why cos^2 #Forces on legs 1 (and 3 though sign of Fz reversed) junk = np.zeros(waDrag.size) waDrag1_3 = ( np.dot( DIRCOSwave, np.vstack((waDrag * np.cos(al_bat3D), junk, waDrag * np.sin(al_bat3D)))) ).T #[n,3] [N] This is an approx for air drag, as it is not normal to the leg to begin with, but it makes it easier wiDrag1_3 = (np.dot( DIRCOSwind, np.vstack((wiDrag * np.cos(al_bat3D), junk, wiDrag * np.sin(al_bat3D))))).T #[n,3] [N] #Forces on legs 2 (and 4), it is as if they were vertical waDrag2_4 = (np.dot(DIRCOSwave, np.vstack((waDrag, junk, waDrag * 0.)))).T #[n,3] [N] wiDrag2_4 = (np.dot(DIRCOSwind, np.vstack((wiDrag, junk, wiDrag * 0.)))).T #[n,3] [N] #Add them together wDrag1_3 = waDrag1_3 + wiDrag1_3 #[N] wDrag2_4 = waDrag2_4 + wiDrag2_4 #[N] wDrag = waDrag + wiDrag #[N] wl_idx = np.nonzero(wDrag)[ 0] #indices of the pile-leg nodes where loading is applied-- if VPFlag and pileidx: #Need to account for the cos #Forces on legs 1 (and 3 though sign of Fz reversed) wDrag1_3[pileidx] = wDrag2_4[pileidx] n_pillegLds = wl_idx.size n_loads = n_pillegLds * nlegs #Total number of wave load nodes pilleg_ndsfrc = np.zeros([n_loads, 7]) nNodespile = pileidx.size #number of nodes in 1 pile nNodesleg = legidx.size #number of nodes in 1 leg for ii in range(0, nlegs): #Cycle through legs idx0 = nNodespile * ii #start index for pile IDs idx1 = idx0 + nNodespile #stop index for pile IDs idx2 = nNodesleg * ii #start index for leg IDs idx3 = idx2 + nNodesleg #stop index for leg IDs nd_ids = np.vstack((self.pilendIDs[idx0:idx1], self.legndIDs[idx2:idx3]))[wl_idx] if np.mod(ii, 2) == 0: lds = wDrag2_4[wl_idx, :] elif ii == 1: lds = np.hstack( (wDrag1_3[wl_idx, 0:2], -wDrag1_3[wl_idx, 2].reshape(-1, 1))) else: lds = wDrag1_3[wl_idx, :] idx0 = n_pillegLds * ii #start index in pilleg_ndsfrc idx1 = idx0 + n_pillegLds #stop index in pilleg_ndsfrc pilleg_ndsfrc[idx0:idx1, :-3] = np.hstack((nd_ids, lds)) #Store wave loads self.Loadouts.wdrag1_3 = wDrag1_3 self.Loadouts.wdrag2_4 = wDrag2_4 else: #3legged jacket in progress #TO DO THIS CASE sys.exit("nlegs <>4 not implemented yet for loading") #-----Need to add hydrostatic loading -BUOYANCY FORCE #IT is a distributed force along the non-flooded members #Still TO DO #________________________________TOWER__________________________________# #Now get the values of the loads at the nodes of Tower junk = (np.roll(twrZs, -1) - np.roll(twrZs, 1))[1:-1] / 2. deltaz = np.hstack( ((twrZs[1] - twrZs[0]) / 2., junk, (twrZs[-1] - twrZs[-2]) / 2.)) #these are the appropriate deltazs #just wind loading for tower but wtrload perhaps in the future for tripods junk = ( np.sqrt(self.towerWindLoads.Px**2 + self.towerWindLoads.Py**2) * deltaz).reshape([-1, 1]) #[N] forces #decompose along local x,y and add z component junk = np.hstack((junk, np.zeros([junk.size, 2]))).T self.Loadouts.twr_dragf = np.dot( DIRCOSwind, junk) #*np.sin(wind_dict['psi']), junk*0.]) #Tower Loads for Frame3DD n_twrlds = (twrndIDs.shape[0] - TwrRigidTop) * ( self.Loadouts.twr_dragf.any() ) #distributed loads along tower+RNA node load twr_ndsfrc = np.zeros([n_twrlds, 7]) twr_ndsfrc[0:n_twrlds, :-3] = np.hstack( (twrndIDs[0:len(twrndIDs) - TwrRigidTop].reshape([-1, 1]), self.Loadouts.twr_dragf.T)) #____________ADD CONCENTRATED RNA FORCES________________________________# #First account for moment transfer in global coordinate system as if yaw=0 RNAload = np.copy(self.RNA_F.reshape( [2, 3]).T) #RNAload: 1columns forces, 2nd column moments #Store yaw-rotated forces and moments at the hub still [3,2] self.Loadouts.RNAload = np.dot(DIRCOSwind, RNAload) Deltax = self.RNAinputs.Thoff[0] - self.RNAinputs.CMoff[0] Deltay = self.RNAinputs.Thoff[1] - self.RNAinputs.CMoff[1] Deltaz = self.RNAinputs.Thoff[2] - self.RNAinputs.CMoff[2] if not (TwrRigidTop): Deltax = self.RNAinputs.Thoff[0] Deltay = self.RNAinputs.Thoff[1] Deltaz = self.RNAinputs.Thoff[2] #Rotor Loads - no weight yet RNAload[0, 1] += -RNAload[1, 0] * Deltaz + RNAload[ 2, 0] * Deltay #Mxx=-Fy*Deltaz +Fz*deltay RNAload[1, 1] += RNAload[0, 0] * Deltaz - RNAload[ 2, 0] * Deltax #Myy= Fx*deltaz-Fz*deltax RNAload[2, 1] += -RNAload[0, 0] * Deltay + RNAload[ 1, 0] * Deltax #Mzz=-Fx*Deltay +Fy*deltax #Then rotate them in the global coordinate system for PYFRAME to account for yaw RNAload = np.dot( DIRCOSwind, RNAload ) #Gravity is already accounted for by Frame3DD for concentrated masses: Jacket.Twr.RNAmass*aux['g_z'],np.zeros(3))) self.RNA_F.reshape([2,3]).T if not (TwrRigidTop): twr_ndsfrc[-1, 1:] += RNAload.T.flatten() else: twr_ndsfrc = np.vstack( (twr_ndsfrc, np.hstack((twrndIDs[-1], RNAload.T.flatten())))) # If we apply this load at tower-top and not tower-top+CMzoff we need to account for different DeltaZ ## if not(TwrRigidTop): ## RNAload[0,1] -= RNAload[1,0]*ThOffset_z #*CMzoff #Mxx ## RNAload[1,1] += RNAload[0,0]*CMzoff #Myy ## twr_ndsfrc[-1,1:] += RNAload.T.flatten() ## else: #Put forces at the RNA.CM location ## RNAload[0,1] -= RNAload[1,0]*ThOffset_z #*CMzoff #Mxx ## RNAload[1,1] += RNAload[0,0]*CMzoff #Myy ## ## twr_ndsfrc[-1,:] += np.hstack((twrndIDs[-1],RNAload.T.flatten())) #STACK ALLOF THEM TOGETHER self.Loadouts.nds_frc = np.vstack( (pilleg_ndsfrc, twr_ndsfrc)) #Concentrated loads