# RCAIDE/Methods/Energy/Propulsors/Electric_Ducted_Fan_Propulsor/compute_ducted_fan_performance.py
# 
# 
# Created:  Jul 2023, M. Clarke

# ----------------------------------------------------------------------------------------------------------------------
#  IMPORT
# ----------------------------------------------------------------------------------------------------------------------
# RCAIDE imports    
from RCAIDE.Library.Methods.Powertrain.Modulators.Electronic_Speed_Controller.compute_esc_performance    import * 
from RCAIDE.Library.Methods.Powertrain.Converters.Motor.compute_motor_performance                        import *
from RCAIDE.Library.Methods.Powertrain.Converters.Ducted_Fan.compute_ducted_fan_performance              import * 

# pacakge imports  
import numpy as np 
from copy import deepcopy

# ----------------------------------------------------------------------------------------------------------------------
# compute_electric_ducted_fan_performance
# ----------------------------------------------------------------------------------------------------------------------  
def compute_electric_ducted_fan_performance(propulsor, state, center_of_gravity=[[0.0, 0.0, 0.0]]):
    """
    Computes the performance of an electric ducted fan propulsion system.
    
    Parameters
    ----------
    propulsor : RCAIDE.Library.Components.Propulsors.Electric_Ducted_Fan
        Electric ducted fan propulsor component with the following attributes:
            - tag : str
                Identifier for the propulsor
            - motor : Data
                Electric motor component
                    - tag : str
                        Identifier for the motor
            - ducted_fan : Data
                Ducted fan component
                    - tag : str
                        Identifier for the ducted fan
                    - origin : list of lists
                        Origin coordinates [[x, y, z]] [m]
                    - tip_radius : float
                        Tip radius of the ducted fan [m]
            - electronic_speed_controller : Data
                ESC component
                    - tag : str
                        Identifier for the ESC
    state : RCAIDE.Framework.Mission.Common.State
        State object containing:
            - conditions : Data
                Flight conditions
                    - freestream : Data
                        Freestream properties
                            - speed_of_sound : numpy.ndarray
                                Speed of sound [m/s]
                    - energy : Data
                        Energy conditions
                            - propulsors : dict
                                Propulsor energy conditions indexed by tag
                            - converters : dict
                                Converter energy conditions indexed by tag
                            - modulators : dict
                                Modulator energy conditions indexed by tag
            - ones_row : function
                Function to create array of ones with specified length
    center_of_gravity : list of lists, optional
        Center of gravity coordinates [[x, y, z]] [m]
        Default: [[0.0, 0.0, 0.0]]
    
    Returns
    -------
    thrust : numpy.ndarray
        Thrust force vector [N]
    moment : numpy.ndarray
        Moment vector [N·m]
    power : numpy.ndarray
        Mechanical power [W]
    power_elec : numpy.ndarray
        Electrical power [W]
    stored_results_flag : bool
        Flag indicating if results are stored
    stored_propulsor_tag : str
        Tag of the propulsor with stored results
    
    Notes
    -----
    This function computes the performance of an electric ducted fan propulsion system
    by linking the electronic speed controller (ESC), motor, and ducted fan. It performs 
    the following steps:
        - Sets the ESC throttle based on the propulsor throttle
        - Computes the ESC output voltage
        - Transfers the ESC output voltage to the motor
        - Computes the motor performance (angular velocity and torque)
        - Transfers the motor angular velocity to the ducted fan
        - Calculates the ducted fan tip Mach number
        - Transfers the propulsor thrust vector angle to the ducted fan
        - Computes the ducted fan performance
        - Calculates the moment generated by the ducted fan thrust
        - Computes the ESC input current
        - Stores the results in the conditions data structure
    
    **Major Assumptions**
        * The ESC throttle directly controls the output voltage
        * The motor angular velocity directly drives the ducted fan
        * Moment is calculated relative to the aircraft center of gravity
    
    See Also
    --------
    RCAIDE.Library.Methods.Powertrain.Converters.Motor.compute_motor_performance
    RCAIDE.Library.Methods.Powertrain.Converters.Ducted_Fan.compute_ducted_fan_performance
    RCAIDE.Library.Methods.Powertrain.Modulators.Electronic_Speed_Controller
    """
    conditions                 = state.conditions     
    EDF_conditions             = conditions.energy.propulsors[propulsor.tag] 
    eta                        = EDF_conditions.throttle
    motor                      = propulsor.motor 
    ducted_fan                 = propulsor.ducted_fan 
    esc                        = propulsor.electronic_speed_controller 
    conditions.energy.modulators[esc.tag].throttle         = eta 
    compute_voltage_out_from_throttle(esc,conditions)

    # Assign conditions to the ducted_fan
    conditions.energy.converters[motor.tag].inputs.voltage = conditions.energy.modulators[esc.tag].outputs.voltage
    compute_motor_performance(motor,conditions) 
    
    # Spin the ducted_fan  
    conditions.energy.converters[ducted_fan.tag].omega              = conditions.energy.converters[motor.tag].outputs.omega 
    conditions.energy.converters[ducted_fan.tag].tip_mach           = (conditions.energy.converters[motor.tag].outputs.omega * ducted_fan.tip_radius) / conditions.freestream.speed_of_sound
    conditions.energy.converters[ducted_fan.tag].throttle           = conditions.energy.modulators[esc.tag].throttle 
    conditions.energy.converters[ducted_fan.tag].commanded_thrust_vector_angle =  conditions.energy.propulsors[propulsor.tag].commanded_thrust_vector_angle
    compute_ducted_fan_performance(ducted_fan,conditions)

    # Compute moment 
    moment_vector      = 0*state.ones_row(3)
    moment_vector[:,0] = ducted_fan.origin[0][0]  -  center_of_gravity[0][0] 
    moment_vector[:,1] = ducted_fan.origin[0][1]  -  center_of_gravity[0][1] 
    moment_vector[:,2] = ducted_fan.origin[0][2]  -  center_of_gravity[0][2]
    moment             =  np.cross(moment_vector, conditions.energy.converters[ducted_fan.tag].thrust) 
    
    # Detemine esc current 
    conditions.energy.modulators[esc.tag].outputs.current = conditions.energy.converters[motor.tag].inputs.current
    compute_current_in_from_throttle(esc,conditions)   
    
    stored_results_flag            = True
    stored_propulsor_tag           = propulsor.tag 
    
    # compute total forces and moments from propulsor (future work would be to add moments from motors)
    EDF_conditions.thrust      = conditions.energy.converters[ducted_fan.tag].thrust  
    EDF_conditions.moment      = moment
    EDF_conditions.power       = conditions.energy.converters[ducted_fan.tag].power  
    return EDF_conditions.thrust,EDF_conditions.moment,EDF_conditions.power,conditions.energy.modulators[esc.tag].inputs.power,stored_results_flag,stored_propulsor_tag 
                
def reuse_stored_electric_ducted_fan_data(propulsor,state,network,stored_propulsor_tag,center_of_gravity= [[0.0, 0.0,0.0]]):
    '''Reuses results from one propulsor for identical propulsors
    
    Assumptions: 
    N/A

    Source:
    N/A

    Inputs:  
    conditions           - operating conditions data structure    [-] 
    voltage              - system voltage                         [V]
    bus                  - bus                                    [-] 
    propulsors           - propulsor data structure               [-] 
    total_thrust         - thrust of propulsor group              [N]
    total_power          - power of propulsor group               [W]
    total_current        - current of propulsor group             [A]

    Outputs:  
    total_thrust         - thrust of propulsor group              [N]
    total_power          - power of propulsor group               [W]
    total_current        - current of propulsor group             [A] 
    
    Properties Used: 
    N.A.        
    '''
    # unpack
    conditions                 = state.conditions  
    motor                      = propulsor.motor 
    ducted_fan                 = propulsor.ducted_fan 
    esc                        = propulsor.electronic_speed_controller  
    motor_0                    = network.propulsors[stored_propulsor_tag].motor 
    ducted_fan_0               = network.propulsors[stored_propulsor_tag].ducted_fan 
    esc_0                      = network.propulsors[stored_propulsor_tag].electronic_speed_controller 
    
    # deep copy results 
    conditions.energy.converters[motor.tag]        = deepcopy(conditions.energy.converters[motor_0.tag])
    conditions.energy.converters[ducted_fan.tag]   = deepcopy(conditions.energy.converters[ducted_fan_0.tag])
    conditions.energy.modulators[esc.tag]          = deepcopy(conditions.energy.modulators[esc_0.tag])
  
    # compute moment 
    thrust_vector           = conditions.energy.converters[ducted_fan.tag].thrust  
    P_mech                  = conditions.energy.converters[ducted_fan.tag].power 
    P_elec                  = conditions.energy.modulators[esc.tag].inputs.power   
    moment_vector           = 0*state.ones_row(3) 
    moment_vector[:,0]      = ducted_fan.origin[0][0]  -  center_of_gravity[0][0] 
    moment_vector[:,1]      = ducted_fan.origin[0][1]  -  center_of_gravity[0][1] 
    moment_vector[:,2]      = ducted_fan.origin[0][2]  -  center_of_gravity[0][2]
    moment                  =  np.cross(moment_vector, thrust_vector)
    
    # pack results 
    conditions.energy.converters[ducted_fan.tag].moment = moment  
    conditions.energy.propulsors[propulsor.tag].thrust  = thrust_vector  
    conditions.energy.propulsors[propulsor.tag].moment  = moment      
    return thrust_vector,moment,P_mech,P_elec