class RotorStructureBase(Component):
    """
    Base class for models that can predict blade deflections
    based on load distribution
    """

    blade_disps = VarTree(BeamDisplacementsVT(), iotype='out', desc='Blade deflections and rotations')
class AeroElasticSolverBase(Component):
    """
    base class for aeroelastic solvers that computes both steady state aerodynamic loads
    and structural deflections in a monolithic analysis for a single case
    """

    wt = VarTree(AeroelasticHAWTVT(), iotype='in', desc='Turbine definition')
    inflow = VarTree(TurbineEnvironmentVT(), iotype='in', desc='Inflow conditions')

    oper = VarTree(RotorOperationalData(), iotype='out', desc='Operational data')
    rotor_loads = VarTree(RotorLoadsVT(), iotype='out', desc='Rotor torque, power, and thrust')
    blade_loads = VarTree(DistributedLoadsVT(), iotype='out', desc='Spanwise load distributions')
    blade_disps = VarTree(BeamDisplacementsVT(), iotype='out', desc='Blade deflections and rotations')
class AEsolver(Component):

    wt = VarTree(AeroelasticHAWTVT(), iotype='in', desc='Turbine definition')
    inflow = VarTree(TurbineEnvironmentVT(), iotype='in', desc='Inflow conditions')

    oper = VarTree(RotorOperationalData(), iotype='out', desc='Operational data')
    rotor_loads = VarTree(RotorLoadsVT(), iotype='out', desc='Rotor torque, power, and thrust')
    blade_loads = VarTree(DistributedLoadsExtVT(), iotype='out', desc='Spanwise load distributions')
    blade_disps = VarTree(BeamDisplacementsVT(), iotype='out', desc='Blade deflections and rotations')

    def execute(self):

        print ''
        print 'running aeroelastic analysis ...'
    def execute(self):
        super(HAWC2SOutputIDO, self).execute()

        # hack to get around bug in OpenMDAO that causes
        # the outputs to refer to the same object
        self.blade_loads = copy.deepcopy(self.blade_loads)
        self.blade_disps = copy.deepcopy(self.blade_disps)
        self.rotor_loads = copy.deepcopy(self.rotor_loads)
        self.oper = copy.deepcopy(self.oper)

        data = self.operational_data

        self.oper.wsp = data[:, 0]
        self.oper.pitch = data[:, 1]
        self.oper.rpm = data[:, 2]

        data = self.rotor_loads_data
        if data.shape[0] == 0:
            return

        self.rotor_loads.wsp = data[:, 0]

        # keeping this for now so I don't break any code relying on this output
        self.rotor_loads.rpm = data[:, 9]
        self.rotor_loads.pitch = data[:, 8]

        self.rotor_loads.P = data[:, 1] * 1000.
        self.rotor_loads.Q = data[:, 1] * 1000. / data[:, 9] * 2. * np.pi / 60.
        self.rotor_loads.T = data[:, 2] * 1000.
        self.rotor_loads.CP = data[:, 3]
        self.rotor_loads.CT = data[:, 4]
        self.hub_loads.Mx = data[:, 6] * 1000.
        self.hub_loads.My = data[:, 7] * 1000.
        self.hub_loads.Mz = data[:, 5]

        self.blade_disps.tip_pos = np.zeros((self.rotor_loads.wsp.shape[0], 3))
        self.blade_disps.tip_rot = np.zeros(self.rotor_loads.wsp.shape[0])
        self.blade_loads.loads_array = []
        self.blade_disps.disps_array = []
        Fx_array = []
        Fy_array = []
        for i, wsp in enumerate(self.rotor_loads.wsp):
            data = self.blade_loads_data[i]
            if len(data.shape) == 1:
                data = data.reshape(1, data.shape[0])
            loads = DistributedLoadsExtVT()
            disps = BeamDisplacementsVT()
            loads.s = data[:, 0]  # / self.blade_length
            loads.aoa = data[:, 4] * 180. / np.pi
            loads.Ft = data[:, 6]
            loads.Fn = data[:, 7]
            loads.cl = data[:, 16]
            loads.cd = data[:, 17]
            loads.cm = data[:, 18]
            loads.ct = data[:, 32]
            loads.cp = data[:, 33]
            loads.v_a = data[:, 26]
            loads.v_t = data[:, 27]
            Fx_array.append(np.trapz(data[:, 6], x=data[:, 0]))
            Fy_array.append(np.trapz(data[:, 7], x=data[:, 0]))
            disps.main_axis = data[:, 13:16]
            disps.main_axis[:, 2] *= -1.
            disps.x = disps.main_axis[:, 0]
            disps.y = disps.main_axis[:, 1]
            disps.z = disps.main_axis[:, 2]
            disps.rot_z = data[:, 28] * 180. / np.pi
            self.blade_loads.loads_array.append(loads)

            self.blade_disps.disps_array.append(disps)
            self.blade_disps.tip_pos[i, :] = disps.main_axis[-1, :]
            self.blade_disps.tip_rot[i] = np.interp(0.98, loads.s, data[:, 28] * 180. / np.pi)
        self.hub_loads.Fx = np.asarray(Fx_array)
        self.hub_loads.Fy = np.asarray(Fy_array)