Example #1
0
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')
Example #2
0
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')
Example #3
0
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
Example #4
0
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