def test_get_atmosphere_error():
    """Test if 'get_atmosphere' raise an error when altitude is outside
       range (0-84000m) """

    with pytest.raises(ValueError):
        get_atmosphere(-10)

    with pytest.raises(ValueError):
        get_atmosphere(85000)
Exemplo n.º 2
0
def calculate_cl(ref_area, alt, mach, mass, load_fact = 1.05):
    """ Function to clacluate the lif coefficient (cl)

    Function 'calculate_cl' return the lift coefficent value for the given
    input (Reference Area, Altitude, Mach Number, Mass and Load Factor)

    Source:
       * Demonstration can be found in:
         /CEASIOMpy/lib/CLCalculator/doc/Calculate_CL.pdf

    Args:
        ref_area (float): Reference area [m^2]
        alt (float): Altitude [m]
        mach (float): Mach number [-]
        mass (float): Aircraft mass [kg]
        load_fact (float): Load Factor [-] (1.05 by default)

    Returns:
        target_cl (float): Lift coefficent [unit]
    """

    # Get atmosphere values at this altitude
    Atm = get_atmosphere(alt)
    GAMMA = 1.401 # Air heat capacity ratio [-]

    # Calculate lift coeffienct
    weight = mass * Atm.grav
    dyn_pres = 0.5 * GAMMA * Atm.pres * mach**2
    target_cl = weight * load_fact / (dyn_pres * ref_area)
    log.info('A lift coefficent (CL) of %f has been calculated' % target_cl)

    return target_cl
def test_get_atmosphere_84000m():
    """Test atmosphere values at 84000m (max from standard atmosphere)"""

    atm = get_atmosphere(84000)

    assert atm.temp == approx(188.65)
    assert atm.pres == approx(0.4359809588843071)
    assert atm.dens == approx(8.050981206963134e-06)
    assert atm.visc == approx(1.2693534953091123e-05)
    assert atm.sos == approx(275.34263714506693)
    assert atm.re_len_ma == approx(174.6383812958875)
    assert atm.grav == approx(9.553079513419783)
def test_get_atmosphere_10000m():
    """Test atmosphere values at 10000m """

    atm = get_atmosphere(10000)

    assert atm.temp == approx(223.15)
    assert atm.pres == approx(26436.267593807635)
    assert atm.dens == approx(0.4127065373832877)
    assert atm.visc == approx(1.468841666984977e-05)
    assert atm.sos == approx(299.463)
    assert atm.re_len_ma == approx(8414143)
    assert atm.grav == approx(9.775937052994681)
def test_get_atmosphere_0m():
    """Test atmosphere values at 0m (sea level)"""

    atm = get_atmosphere(0)

    assert atm.temp == 288.15
    assert atm.pres == 101325.0
    assert atm.dens == 1.225
    assert atm.visc == approx(1.81205671028e-05)
    assert atm.sos == approx(340.294)
    assert atm.re_len_ma == approx(23004811)
    assert atm.grav == 9.80665
Exemplo n.º 6
0
def get_cl(cpacs_path, cpacs_out_path):
    """ Function to calculate CL requiered as a function of the parameter found
    in the CPACS file.

    Function 'get_cl' find input value in the CPACS file, calculate the
    requiered CL (with calculate_cl) and  save the CL value in
    /cpacs/toolspecific/CEASIOMpy/aerodynamics/su2/targetCL

    Args:
        cpacs_path (str):  Path to CPACS file
        cpacs_out_path (str): Path to CPACS output file

    """

    tixi = open_tixi(cpacs_path)

    # XPath definition
    model_xpath = '/cpacs/vehicles/aircraft/model'
    ref_area_xpath = model_xpath + '/reference/area'
    mtom_xpath = model_xpath + '/analyses/massBreakdown/designMasses/mTOM/mass'
    range_xpath = '/cpacs/toolspecific/CEASIOMpy/ranges'
    cruise_alt_xpath = range_xpath + '/cruiseAltitude'
    cruise_mach_xpath = range_xpath + '/cruiseMach'
    load_fact_xpath = range_xpath + '/loadFactor'
    su2_xpath = '/cpacs/toolspecific/CEASIOMpy/aerodynamics/su2'

    # Requiered input data from CPACS
    ref_area = get_value(tixi, ref_area_xpath)
    mtom = get_value(tixi, mtom_xpath)

    # Requiered input data that could be replace by a default value if missing
    cruise_alt = get_value_or_default(tixi, cruise_alt_xpath, 12000.0)
    cruise_mach = get_value_or_default(tixi, cruise_mach_xpath, 0.78)
    load_fact = get_value_or_default(tixi, load_fact_xpath, 1.05)

    # Get atmosphere from cruise altitude
    Atm = get_atmosphere(cruise_alt)

    # CL calculation
    target_cl = calculate_cl(ref_area, cruise_alt, cruise_mach, mtom,
                             load_fact)

    # Save TargetCL
    create_branch(tixi, su2_xpath)
    create_branch(tixi, su2_xpath + '/targetCL')
    create_branch(tixi, su2_xpath + '/fixedCL')
    tixi.updateDoubleElement(su2_xpath + '/targetCL', target_cl, '%g')
    tixi.updateTextElement(su2_xpath + '/fixedCL', 'YES')
    log.info('Target CL has been saved in the CPACS file')

    close_tixi(tixi, cpacs_out_path)
Exemplo n.º 7
0
def estimate_skin_friction_coef(wetted_area, wing_area, wing_span, mach, alt):
    """ Return an estimation of skin friction drag coefficient.

    Function 'estimate_skin_friction_coef' gives an estimation of the skin
    friction drag coefficient, based on an empirical formala (see source).

    Source:
        * Gerard W. H. van Es.  "Rapid Estimation of the Zero-Lift Drag
          Coefficient of Transport Aircraft", Journal of Aircraft, Vol. 39,
          No. 4 (2002), pp. 597-599. https://doi.org/10.2514/2.2997

    Args:
        wetted_area (float):  Wetted Area of the entire aircraft [m^2]
        wing_area (float):  Main wing area [m^2]
        wing_span (float):  Main wing span [m]
        mach (float):  Cruise Mach number [-]
        alt (float):  Aircraft altitude [m]

    Returns:
        cd0 (float): Drag coefficient due to skin friction [-]
    """

    # Get atmosphere values at this altitude
    Atm = get_atmosphere(alt)

    kinetic_visc = Atm.visc / Atm.dens

    # Get speed from Mach Number
    speed = mach * Atm.sos

    # Reynolds number based on the ratio Wetted Area / Wing Span
    reynolds_number = (wetted_area / wing_span) * speed / kinetic_visc
    log.info('Reynolds number:' + str(round(reynolds_number)))

    # Skin friction coefficient, formula from source (see function description)
    cfe = 0.00258 + 0.00102 * math.exp(-6.28*1e-9*reynolds_number) \
          + 0.00295 * math.exp(-2.01*1e-8*reynolds_number)
    log.info('Skin friction coefficient:' + str(round(cfe, 5)))

    # Drag coefficient due to skin friction
    cd0 = cfe * wetted_area / wing_area
    log.info("Skin friction drag coefficient: " + str(cd0))

    return cd0
Exemplo n.º 8
0
def create_config(cpacs_path, cpacs_out_path, su2_mesh_path,
                  config_output_path):
    """ Function to create configuration file for SU2 calculation

    Function 'create_config' create an SU2 configuration file from SU2 mesh data
    (marker) and CPACS file specific related parameter (/toolSpecific).
    For all other infomation the value from the default SU2 configuration file
    are used. A new configuration file will be saved in
    /ToolOutput/ToolOutput.cfg

    Source:
       * SU2 configuration file  template
         https://github.com/su2code/SU2/blob/master/config_template.cfg

    Args:
        cpacs_path (str):  Path to CPACS file
        cpacs_out_path (str): Path to CPACS output file
        su2_mesh_path (str): Path to SU2 mesh
        config_output_path (str): Path to the output configuration file

    """

    DEFAULT_CONFIG_PATH = MODULE_DIR + '/files/DefaultConfig_v6.cfg'

    # Get value from CPACS
    tixi = open_tixi(cpacs_path)

    su2_xpath = '/cpacs/toolspecific/CEASIOMpy/aerodynamics/su2'

    # Reference values
    ref_xpath = '/cpacs/vehicles/aircraft/model/reference'
    ref_len = get_value(tixi, ref_xpath + '/length')
    ref_area = get_value(tixi, ref_xpath + '/area')

    # Fixed CL parameters
    fixed_cl_xpath = su2_xpath + '/fixedCL'
    target_cl_xpath = su2_xpath + '/targetCL'
    tixi, fixed_cl = get_value_or_default(tixi, fixed_cl_xpath, 'NO')
    tixi, target_cl = get_value_or_default(tixi, target_cl_xpath, 1.0)

    if fixed_cl == 'NO':
        # Get value from the aeroMap (1 point)
        active_aeroMap_xpath = su2_xpath + '/aeroMapUID'
        aeroMap_uid = get_value(tixi, active_aeroMap_xpath)
        aeroMap_path = tixi.uIDGetXPath(aeroMap_uid)
        apm_path = aeroMap_path + '/aeroPerformanceMap'

        #State = get_states(tixi,apm_path)

        #alt = State.alt_list
        alt = get_value(tixi, apm_path + '/altitude')
        mach = get_value(tixi, apm_path + '/machNumber')
        aoa = get_value(tixi, apm_path + '/angleOfAttack')
        aos = get_value(tixi, apm_path + '/angleOfSideslip')

    else:
        range_xpath = '/cpacs/toolspecific/CEASIOMpy/ranges'
        cruise_alt_xpath = range_xpath + '/cruiseAltitude'
        cruise_mach_xpath = range_xpath + '/cruiseMach'

        # value corresponding to fix CL calulation
        aoa = 0.0  # Will not be used
        aos = 0.0
        tixi, mach = get_value_or_default(tixi, cruise_mach_xpath, 0.78)
        tixi, alt = get_value_or_default(tixi, cruise_alt_xpath, 12000)

    Atm = get_atmosphere(alt)
    pressure = Atm.pres
    temp = Atm.temp

    # Settings
    settings_xpath = '/cpacs/toolspecific/CEASIOMpy/aerodynamics/su2/settings'
    max_iter_xpath = settings_xpath + '/maxIter'
    cfl_nb_xpath = settings_xpath + '/cflNumber'
    mg_level_xpath = settings_xpath + '/multigridLevel'

    tixi, max_iter = get_value_or_default(tixi, max_iter_xpath, 200)
    tixi, cfl_nb = get_value_or_default(tixi, cfl_nb_xpath, 1.0)
    tixi, mg_level = get_value_or_default(tixi, mg_level_xpath, 3)

    # Mesh Marker
    bc_wall_xpath = '/cpacs/toolspecific/CEASIOMpy/aerodynamics/su2/boundaryConditions/wall'

    bc_wall_list = get_mesh_marker(su2_mesh_path)
    tixi = create_branch(tixi, bc_wall_xpath)
    bc_wall_str = ';'.join(bc_wall_list)
    tixi.updateTextElement(bc_wall_xpath, bc_wall_str)

    close_tixi(tixi, cpacs_out_path)

    # Open default configuration file
    try:
        config_file_object = open(DEFAULT_CONFIG_PATH, 'r')
        config_file_lines = config_file_object.readlines()
        config_file_object.close()
        log.info('Default configuration file has been found and read')
    except Exception:
        log.exception('Problem to open or read default configuration file')

    # Create a dictionary with all the parameters from the default config file
    config_dict = {}
    for line in config_file_lines:
        if '=' in line:
            (key, val) = line.split('=')
            if val.endswith('\n'):
                val = val[:-1]
            config_dict[key] = val

    config_dict_modif = config_dict

    # General parmeters
    config_dict_modif['MESH_FILENAME'] = su2_mesh_path

    config_dict_modif['REF_LENGTH'] = ref_len
    config_dict_modif['REF_AREA'] = ref_area

    # Settings
    config_dict_modif['EXT_ITER'] = int(max_iter)
    config_dict_modif['CFL_NUMBER'] = cfl_nb
    config_dict_modif['MGLEVEL'] = int(mg_level)

    config_dict_modif['AOA'] = aoa
    config_dict_modif['SIDESLIP_ANGLE'] = aos
    config_dict_modif['MACH_NUMBER'] = mach
    config_dict_modif['FREESTREAM_PRESSURE'] = pressure
    config_dict_modif['FREESTREAM_TEMPERATURE'] = temp

    # If calculation at CL fix (AOA will not be taken into account)
    config_dict_modif['FIXED_CL_MODE'] = fixed_cl
    config_dict_modif['TARGET_CL'] = target_cl
    config_dict_modif['DCL_DALPHA'] = '0.1'
    config_dict_modif['UPDATE_ALPHA'] = '8'
    config_dict_modif['ITER_DCL_DALPHA'] = '80'

    # Mesh Marker
    bc_wall_str = '(' + ','.join(bc_wall_list) + ')'
    config_dict_modif['MARKER_EULER'] = bc_wall_str
    config_dict_modif['MARKER_FAR'] = ' (Farfield)'
    config_dict_modif['MARKER_SYM'] = ' (0)'
    config_dict_modif['MARKER_PLOTTING'] = bc_wall_str
    config_dict_modif['MARKER_MONITORING'] = bc_wall_str
    config_dict_modif['MARKER_MOVING'] = bc_wall_str

    # Change value if needed or add new parameters in the config file
    for key, value in config_dict_modif.items():
        line_nb = 0
        # Double loop! There is probably a possibility to do something better.
        for i, line in enumerate(config_file_lines):
            if '=' in line:
                (key_def, val_def) = line.split('=')
                if key == key_def:
                    line_nb = i
                    break
        if not line_nb:
            config_file_lines.append(str(key) + ' = ' + str(value) + '\n')
        else:
            if val_def != config_dict_modif[key]:
                config_file_lines[line_nb] = str(key) + ' = ' \
                                           + str(config_dict_modif[key]) + '\n'

    config_file_new = open(config_output_path, 'w')
    config_file_new.writelines(config_file_lines)
    config_file_new.close()
    log.info('ToolOutput.cfg has been written in /ToolOutput.')
Exemplo n.º 9
0
def generate_su2_config(cpacs_path, cpacs_out_path, wkdir):
    """Function to create SU2 confif file.

    Function 'generate_su2_config' reads data in the CPACS file and generate
    configuration files for one or multible flight conditions (alt,mach,aoa,aos)

    Source:
        * SU2 config template: https://github.com/su2code/SU2/blob/master/config_template.cfg

    Args:
        cpacs_path (str): Path to CPACS file
        cpacs_out_path (str):Path to CPACS output file
        wkdir (str): Path to the working directory

    """

    # Get value from CPACS
    tixi = cpsf.open_tixi(cpacs_path)
    tigl = cpsf.open_tigl(tixi)

    # Get SU2 mesh path
    su2_mesh_xpath = '/cpacs/toolspecific/CEASIOMpy/filesPath/su2Mesh'
    su2_mesh_path = cpsf.get_value(tixi,su2_mesh_xpath)

    # Get reference values
    ref_xpath = '/cpacs/vehicles/aircraft/model/reference'
    ref_len = cpsf.get_value(tixi,ref_xpath + '/length')
    ref_area = cpsf.get_value(tixi,ref_xpath + '/area')
    ref_ori_moment_x = cpsf.get_value_or_default(tixi,ref_xpath+'/point/x',0.0)
    ref_ori_moment_y = cpsf.get_value_or_default(tixi,ref_xpath+'/point/y',0.0)
    ref_ori_moment_z = cpsf.get_value_or_default(tixi,ref_xpath+'/point/z',0.0)

    # Get SU2 settings
    settings_xpath = SU2_XPATH + '/settings'
    max_iter_xpath = settings_xpath + '/maxIter'
    max_iter = cpsf.get_value_or_default(tixi, max_iter_xpath,200)
    cfl_nb_xpath = settings_xpath + '/cflNumber'
    cfl_nb = cpsf.get_value_or_default(tixi, cfl_nb_xpath,1.0)
    mg_level_xpath =  settings_xpath + '/multigridLevel'
    mg_level = cpsf.get_value_or_default(tixi, mg_level_xpath,3)

    # Mesh Marker
    bc_wall_xpath = SU2_XPATH + '/boundaryConditions/wall'
    bc_wall_list = su2f.get_mesh_marker(su2_mesh_path)
    cpsf.create_branch(tixi, bc_wall_xpath)
    bc_wall_str = ';'.join(bc_wall_list)
    tixi.updateTextElement(bc_wall_xpath,bc_wall_str)

    # Fixed CL parameters
    fixed_cl_xpath = SU2_XPATH + '/fixedCL'
    fixed_cl = cpsf.get_value_or_default(tixi, fixed_cl_xpath,'NO')
    target_cl_xpath = SU2_XPATH + '/targetCL'
    target_cl = cpsf.get_value_or_default(tixi, target_cl_xpath,1.0)

    if fixed_cl == 'NO':
        active_aeroMap_xpath = SU2_XPATH + '/aeroMapUID'
        aeromap_uid = cpsf.get_value(tixi,active_aeroMap_xpath)

        log.info('Configuration file for ""' + aeromap_uid + '"" calculation will be created.')

        # Get parameters of the aeroMap (alt,ma,aoa,aos)
        Param = apmf.get_aeromap(tixi,aeromap_uid)
        param_count = Param.get_count()

        if param_count >= 1:
            alt_list = Param.alt
            mach_list =  Param.mach
            aoa_list = Param.aoa
            aos_list = Param.aos
        else:
            raise ValueError('No parametre have been found in the aeroMap!')

    else: # if fixed_cl == 'YES':
        log.info('Configuration file for fixed CL calculation will be created.')

        range_xpath = '/cpacs/toolspecific/CEASIOMpy/ranges'

        # Parameters fixed CL calulation
        param_count = 1

        # These parameters will not be used
        aoa_list = [0.0]
        aos_list = [0.0]

        cruise_mach_xpath= range_xpath + '/cruiseMach'
        mach = cpsf.get_value_or_default(tixi,cruise_mach_xpath,0.78)
        mach_list = [mach]
        cruise_alt_xpath= range_xpath + '/cruiseAltitude'
        alt = cpsf.get_value_or_default(tixi,cruise_alt_xpath,12000)
        alt_list = [alt]

        aeromap_uid = 'aeroMap_fixedCL_SU2'
        description = 'AeroMap created for SU2 fixed CL value of: ' + str(target_cl)
        apmf.create_empty_aeromap(tixi, aeromap_uid, description)
        Parameters = apmf.AeroCoefficient()
        Parameters.alt = alt_list
        Parameters.mach = mach_list
        Parameters.aoa = aoa_list
        Parameters.aos = aos_list
        apmf.save_parameters(tixi,aeromap_uid,Parameters)
        tixi.updateTextElement(SU2_XPATH+ '/aeroMapUID',aeromap_uid)


    # Get and modify the default configuration file
    cfg = su2f.read_config(DEFAULT_CONFIG_PATH)

    # General parmeters
    cfg['REF_LENGTH'] = ref_len
    cfg['REF_AREA'] = ref_area

    cfg['REF_ORIGIN_MOMENT_X'] = ref_ori_moment_x
    cfg['REF_ORIGIN_MOMENT_Y'] = ref_ori_moment_y
    cfg['REF_ORIGIN_MOMENT_Z'] = ref_ori_moment_z


    # Settings
    cfg['INNER_ITER'] = int(max_iter)
    cfg['CFL_NUMBER'] = cfl_nb
    cfg['MGLEVEL'] = int(mg_level)

    # Fixed CL mode (AOA will not be taken into account)
    cfg['FIXED_CL_MODE'] = fixed_cl
    cfg['TARGET_CL'] = target_cl
    cfg['DCL_DALPHA'] = '0.1'
    cfg['UPDATE_AOA_ITER_LIMIT'] = '50'
    cfg['ITER_DCL_DALPHA'] = '80'
    # TODO: correct value for the 3 previous parameters ??

    # Mesh Marker
    bc_wall_str = '(' + ','.join(bc_wall_list) + ')'
    cfg['MARKER_EULER'] = bc_wall_str
    cfg['MARKER_FAR'] = ' (Farfield)' # TODO: maybe make that a variable
    cfg['MARKER_SYM'] = ' (0)'       # TODO: maybe make that a variable?
    cfg['MARKER_PLOTTING'] = bc_wall_str
    cfg['MARKER_MONITORING'] = bc_wall_str
    cfg['MARKER_MOVING'] = '( NONE )'  # TODO: when do we need to define MARKER_MOVING?
    cfg['DV_MARKER'] = bc_wall_str

    # Parameters which will vary for the different cases (alt,mach,aoa,aos)
    for case_nb in range(param_count):

        cfg['MESH_FILENAME'] = su2_mesh_path

        alt = alt_list[case_nb]
        mach = mach_list[case_nb]
        aoa = aoa_list[case_nb]
        aos = aos_list[case_nb]

        Atm = get_atmosphere(alt)
        pressure = Atm.pres
        temp = Atm.temp

        cfg['MACH_NUMBER'] = mach
        cfg['AOA'] = aoa
        cfg['SIDESLIP_ANGLE'] = aos
        cfg['FREESTREAM_PRESSURE'] = pressure
        cfg['FREESTREAM_TEMPERATURE'] = temp

        cfg['ROTATION_RATE'] = '0.0 0.0 0.0'

        config_file_name = 'ConfigCFD.cfg'


        case_dir_name = ''.join(['Case',str(case_nb).zfill(2),
                                 '_alt',str(alt),
                                 '_mach',str(round(mach,2)),
                                 '_aoa',str(round(aoa,1)),
                                 '_aos',str(round(aos,1))])

        case_dir_path = os.path.join(wkdir,case_dir_name)
        if not os.path.isdir(case_dir_path):
            os.mkdir(case_dir_path)

        config_output_path = os.path.join(wkdir,case_dir_name,config_file_name)

        su2f.write_config(config_output_path,cfg)


        # Damping derivatives
        damping_der_xpath = SU2_XPATH + '/options/clalculateDampingDerivatives'
        damping_der = cpsf.get_value_or_default(tixi,damping_der_xpath,False)

        if damping_der:

            rotation_rate_xpath = SU2_XPATH + '/options/rotationRate'
            rotation_rate = cpsf.get_value_or_default(tixi,rotation_rate_xpath,1.0)

            cfg['GRID_MOVEMENT'] = 'ROTATING_FRAME'

            cfg['ROTATION_RATE'] = str(rotation_rate) + ' 0.0 0.0'
            os.mkdir(os.path.join(wkdir,case_dir_name+'_dp'))
            config_output_path = os.path.join(wkdir,case_dir_name+'_dp',config_file_name)
            su2f.write_config(config_output_path,cfg)

            cfg['ROTATION_RATE'] = '0.0 ' + str(rotation_rate) + ' 0.0'
            os.mkdir(os.path.join(wkdir,case_dir_name+'_dq'))
            config_output_path = os.path.join(wkdir,case_dir_name+'_dq',config_file_name)
            su2f.write_config(config_output_path,cfg)

            cfg['ROTATION_RATE'] = '0.0 0.0 ' + str(rotation_rate)
            os.mkdir(os.path.join(wkdir,case_dir_name+'_dr'))
            config_output_path = os.path.join(wkdir,case_dir_name+'_dr',config_file_name)
            su2f.write_config(config_output_path,cfg)

            log.info('Damping derivatives cases directory has been created.')



        # Control surfaces deflections
        control_surf_xpath = SU2_XPATH + '/options/clalculateCotrolSurfacesDeflections'
        control_surf = cpsf.get_value_or_default(tixi,control_surf_xpath,False)

        if control_surf:

            # Get deformed mesh list
            su2_def_mesh_xpath = SU2_XPATH + '/availableDeformedMesh'
            if tixi.checkElement(su2_def_mesh_xpath):
                su2_def_mesh_list = cpsf.get_string_vector(tixi,su2_def_mesh_xpath)
            else:
                log.warning('No SU2 deformed mesh has been found!')
                su2_def_mesh_list = []

            for su2_def_mesh in su2_def_mesh_list:

                mesh_path = os.path.join(wkdir,'MESH',su2_def_mesh)

                config_dir_path = os.path.join(wkdir,case_dir_name+'_'+su2_def_mesh.split('.')[0])
                os.mkdir(config_dir_path)
                cfg['MESH_FILENAME'] = mesh_path

                config_file_name = 'ConfigCFD.cfg'
                config_output_path = os.path.join(wkdir,config_dir_path,config_file_name)
                su2f.write_config(config_output_path,cfg)


    # TODO: change that, but if it is save in tooloutput it will be erease by results...
    cpsf.close_tixi(tixi,cpacs_path)
Exemplo n.º 10
0
def dynamic_stability_analysis(cpacs_path, cpacs_out_path):
    """Function to analyse a full Aeromap

    Function 'dynamic_stability_analysis' analyses longitudinal dynamic
    stability and directionnal dynamic.

    Args:
        cpacs_path (str): Path to CPACS file
        cpacs_out_path (str):Path to CPACS output file
        plot (boolean): Choise to plot graph or not

    Returns:  (#TODO put that in the documentation)
        *   Adrvertisements certifying if the aircraft is stable or Not
        *   In case of longitudinal dynamic UNstability or unvalid test on data:
                -	Plot cms VS aoa for constant Alt, Mach and different aos
                -	Plot cms VS aoa for const alt and aos and different mach
                -	plot cms VS aoa for constant mach, AOS and different altitudes
        *  In case of directionnal dynamic UNstability or unvalid test on data:
                -	Pcot cml VS aos for constant Alt, Mach and different aoa
                -	Plot cml VS aos for const alt and aoa and different mach
                -	plot cml VS aos for constant mach, AOA and different altitudes
        *  Plot one graph of  cruising angles of attack for different mach and altitudes

    Make the following tests:
        *   Check the CPACS path
        *   For longitudinal dynamic stability analysis:
                -   If there is more than one angle of attack for a given altitude, mach, aos
                -   If cml values are only zeros for a given altitude, mach, aos
                -   If there one aoa value which is repeated for a given altitude, mach, aos
        *   For directionnal dynamic stability analysis:
                -   If there is more than one angle of sideslip for a given altitude, mach, aoa
                -   If cms values are only zeros for a given altitude, mach, aoa
                -   If there one aos value which is repeated for a given altitude, mach, aoa
    """

    # XPATH definition
    aeromap_uid_xpath = DYNAMIC_ANALYSIS_XPATH + '/aeroMapUid'
    aircraft_class_xpath = DYNAMIC_ANALYSIS_XPATH + '/class'  # Classes 1 2 3 4 small, heavy ...
    aircraft_cathegory_xpath = DYNAMIC_ANALYSIS_XPATH + '/category'  # flight phase A B C
    selected_mass_config_xpath = DYNAMIC_ANALYSIS_XPATH + '/massConfiguration'
    longi_analysis_xpath = DYNAMIC_ANALYSIS_XPATH + '/instabilityModes/longitudinal'
    direc_analysis_xpath = DYNAMIC_ANALYSIS_XPATH + '/instabilityModes/lateralDirectional'
    show_plot_xpath = DYNAMIC_ANALYSIS_XPATH + '/showPlots'
    save_plot_xpath = DYNAMIC_ANALYSIS_XPATH + '/savePlots'

    model_xpath = '/cpacs/vehicles/aircraft/model'
    ref_area_xpath = model_xpath + '/reference/area'
    ref_length_xpath = model_xpath + '/reference/length'
    flight_qualities_case_xpath = model_xpath + '/analyses/flyingQualities/fqCase'
    masses_location_xpath = model_xpath + '/analyses/massBreakdown/designMasses'
    # aircraft_class_xpath = flight_qualities_case_xpath + '/class' # Classes 1 2 3 4 small, heavy ...
    # aircraft_cathegory_xpath = flight_qualities_case_xpath + '/cathegory' # flight phase A B C

    # Ask user flight path angles : gamma_e
    thrust_available = None  # Thrust data are not available
    flight_path_angle_deg = [
        0
    ]  # [-15,-10,-5,0,5,10,15] # The user should have the choice to select them !!!!!!!!!!!!!!!!!!!!
    flight_path_angle = [
        angle * (np.pi / 180) for angle in flight_path_angle_deg
    ]  # flight_path_angle in [rad]

    tixi = cpsf.open_tixi(cpacs_path)
    # Get aeromap uid
    aeromap_uid = cpsf.get_value(tixi, aeromap_uid_xpath)
    log.info('The following aeroMap will be analysed: ' + aeromap_uid)

    # Mass configuration: (Maximum landing mass, Maximum ramp mass (the maximum weight authorised for the ground handling), Take off mass, Zero Fuel mass)
    mass_config = cpsf.get_value(tixi, selected_mass_config_xpath)
    log.info('The aircraft mass configuration used for analysis is: ' +
             mass_config)

    # Analyses to do : longitudinal / Lateral-Directional
    longitudinal_analysis = cpsf.get_value(tixi, longi_analysis_xpath)
    lateral_directional_analysis = False
    # lateral_directional_analysis = cpsf.get_value(tixi, direc_analysis_xpath )
    # Plots configuration with Setting GUI
    show_plots = cpsf.get_value_or_default(tixi, show_plot_xpath, False)
    save_plots = cpsf.get_value_or_default(tixi, save_plot_xpath, False)

    mass_config_xpath = masses_location_xpath + '/' + mass_config
    if tixi.checkElement(mass_config_xpath):
        mass_xpath = mass_config_xpath + '/mass'
        I_xx_xpath = mass_config_xpath + '/massInertia/Jxx'
        I_yy_xpath = mass_config_xpath + '/massInertia/Jyy'
        I_zz_xpath = mass_config_xpath + '/massInertia/Jzz'
        I_xz_xpath = mass_config_xpath + '/massInertia/Jxz'
    else:
        raise ValueError(
            'The mass configuration : {} is not defined in the CPACS file !!!'.
            format(mass_config))

    s = cpsf.get_value(
        tixi, ref_area_xpath
    )  # Wing area : s  for non-dimonsionalisation of aero data.
    mac = cpsf.get_value(
        tixi, ref_length_xpath
    )  # ref length for non dimensionalisation, Mean aerodynamic chord: mac,
    # TODO: check that
    b = s / mac

    # TODO: find a way to get that
    xh = 10  # distance Aircaft cg-ac_horizontal-tail-plane.

    m = cpsf.get_value(tixi, mass_xpath)  # aircraft mass dimensional
    I_xx = cpsf.get_value(tixi, I_xx_xpath)  # X inertia dimensional
    I_yy = cpsf.get_value(tixi, I_yy_xpath)  # Y inertia dimensional
    I_zz = cpsf.get_value(tixi, I_zz_xpath)  # Z inertia dimensional
    I_xz = cpsf.get_value(tixi, I_xz_xpath)  # XZ inertia dimensional

    aircraft_class = cpsf.get_value(
        tixi, aircraft_class_xpath)  # aircraft class 1 2 3 4
    flight_phase = cpsf.get_string_vector(
        tixi, aircraft_cathegory_xpath)[0]  # Flight phase A B C

    Coeffs = apmf.get_aeromap(
        tixi, aeromap_uid
    )  # Warning: Empty uID found! This might lead to unknown errors!

    alt_list = Coeffs.alt
    mach_list = Coeffs.mach
    aoa_list = Coeffs.aoa
    aos_list = Coeffs.aos
    cl_list = Coeffs.cl
    cd_list = Coeffs.cd
    cs_list = Coeffs.cs
    cml_list = Coeffs.cml
    cms_list = Coeffs.cms
    cmd_list = Coeffs.cmd
    dcsdrstar_list = Coeffs.dcsdrstar
    dcsdpstar_list = Coeffs.dcsdpstar
    dcldqstar_list = Coeffs.dcldqstar
    dcmsdqstar_list = Coeffs.dcmsdqstar
    dcddqstar_list = Coeffs.dcddqstar
    dcmldqstar_list = Coeffs.dcmldqstar
    dcmddpstar_list = Coeffs.dcmddpstar
    dcmldpstar_list = Coeffs.dcmldpstar
    dcmldrstar_list = Coeffs.dcmldrstar
    dcmddrstar_list = Coeffs.dcmddrstar

    # All different vallues with only one occurence
    alt_unic = get_unic(alt_list)
    mach_unic = get_unic(mach_list)
    aos_unic = get_unic(aos_list)
    aoa_unic = get_unic(aoa_list)

    # TODO get from CPACS
    incrementalMap = False

    for alt in alt_unic:
        idx_alt = [i for i in range(len(alt_list)) if alt_list[i] == alt]
        Atm = get_atmosphere(alt)
        g = Atm.grav
        a = Atm.sos
        rho = Atm.dens

        for mach in mach_unic:
            print('Mach : ', mach)
            idx_mach = [
                i for i in range(len(mach_list)) if mach_list[i] == mach
            ]
            u0, m_adim, i_xx, i_yy, i_zz, i_xz = adimensionalise(
                a, mach, rho, s, b, mac, m, I_xx, I_yy, I_zz,
                I_xz)  # u0 is V0 in Cook

            # Hyp: trim condition when: ( beta = 0 and dCm/dalpha = 0)  OR  ( aos=0 and dcms/daoa = 0 )
            if 0 not in aos_unic:
                log.warning(
                    'The aircraft can not be trimmed (requiring symetric flight condition) as beta never equal to 0 for Alt = {}, mach = {}'
                    .format(alt, mach))
            else:
                idx_aos = [i for i in range(len(aos_list)) if aos_list[i] == 0]
                find_index = get_index(idx_alt, idx_mach, idx_aos)
                # If there is only one data at (alt, mach, aos) then dont make stability anlysis
                if len(find_index) <= 1:
                    log.warning(
                        'Not enough data at : Alt = {} , mach = {}, aos = 0, can not perform stability analysis'
                        .format(alt, mach))
                # If there is at leat 2 data at (alt, mach, aos) then, make stability anlysis
                else:
                    # Calculate trim conditions
                    cms = []
                    aoa = []
                    cl = []
                    for index in find_index:
                        cms.append(cms_list[index])
                        aoa.append(aoa_list[index] * np.pi / 180)
                        cl.append(cl_list[index])

                    cl_required = (m * g) / (0.5 * rho * u0**2 * s)
                    (trim_aoa, idx_trim_before, idx_trim_after,
                     ratio) = trim_condition(
                         alt,
                         mach,
                         cl_required,
                         cl,
                         aoa,
                     )

                    if trim_aoa:
                        trim_aoa_deg = trim_aoa * 180 / np.pi
                        trim_cms = interpolation(cms, idx_trim_before,
                                                 idx_trim_after, ratio)
                        pitch_moment_derivative_rad = (
                            cms[idx_trim_after] - cms[idx_trim_before]) / (
                                aoa[idx_trim_after] - aoa[idx_trim_before])
                        pitch_moment_derivative_deg = pitch_moment_derivative_rad / (
                            180 / np.pi)
                        # Find incremental cms
                        if incrementalMap:
                            for index, mach_number in enumerate(mach_unic, 0):
                                if mach_number == mach:
                                    mach_index = index
                            dcms_before = dcms_list[mach_index * len(aoa_unic)
                                                    + idx_trim_before]
                            dcms_after = dcms_list[mach_index * len(aoa_unic) +
                                                   idx_trim_after]
                            dcms = dcms_before + ratio * (dcms_after -
                                                          dcms_before)
                            trim_elevator = -trim_cms / dcms  # Trim elevator deflection in [°]
                        else:
                            dcms = None
                            trim_elevator = None

                    else:
                        trim_aoa_deg = None
                        trim_cms = None
                        pitch_moment_derivative_deg = None
                        dcms = None
                        trim_elevator = None

                    # Longitudinal dynamic stability,
                    # Stability analysis
                    if longitudinal_analysis and trim_cms:
                        cl = []
                        cd = []
                        dcldqstar = []
                        dcddqstar = []
                        dcmsdqstar = []
                        for index in find_index:
                            cl.append(cl_list[index])
                            cd.append(cd_list[index])
                            dcldqstar.append(dcldqstar_list[index])
                            dcddqstar.append(dcddqstar_list[index])
                            dcmsdqstar.append(dcmsdqstar_list[index])

                        # Trimm variables
                        cd0 = interpolation(cd, idx_trim_before,
                                            idx_trim_after,
                                            ratio)  # Dragg coeff at trim
                        cl0 = interpolation(cl, idx_trim_before,
                                            idx_trim_after,
                                            ratio)  # Lift coeff at trim
                        cl_dividedby_cd_trim = cl0 / cd0  #  cl/cd ratio at trim, at trim aoa

                        # Lift & drag coefficient derivative with respect to AOA at trimm
                        cl_alpha0 = (cl[idx_trim_after] - cl[idx_trim_before]
                                     ) / (aoa[idx_trim_after] -
                                          aoa[idx_trim_before])
                        cd_alpha0 = (cd[idx_trim_after] - cd[idx_trim_before]
                                     ) / (aoa[idx_trim_after] -
                                          aoa[idx_trim_before])
                        print(idx_trim_before, idx_trim_after, ratio)

                        dcddqstar0 = interpolation(dcddqstar, idx_trim_before,
                                                   idx_trim_after,
                                                   ratio)  # x_q
                        dcldqstar0 = interpolation(dcldqstar, idx_trim_before,
                                                   idx_trim_after,
                                                   ratio)  # z_q
                        dcmsdqstar0 = interpolation(dcmsdqstar,
                                                    idx_trim_before,
                                                    idx_trim_after,
                                                    ratio)  # m_q
                        cm_alpha0 = trim_cms

                        # Speed derivatives if there is at least 2 distinct mach values
                        if len(mach_unic) >= 2:
                            dcddm0 = speed_derivative_at_trim(
                                cd_list, mach, mach_list, mach_unic, idx_alt,
                                aoa_list, aos_list, idx_trim_before,
                                idx_trim_after, ratio)

                            if dcddm0 == None:
                                dcddm0 = 0
                                log.warning(
                                    'Not enough data to determine dcddm or (Cd_mach) at trim condition at Alt = {}, mach = {}, aoa = {}, aos = 0. Assumption: dcddm = 0'
                                    .format(alt, mach, round(trim_aoa_deg, 2)))
                            dcldm0 = speed_derivative_at_trim(
                                cl_list, mach, mach_list, mach_unic, idx_alt,
                                aoa_list, aos_list, idx_trim_before,
                                idx_trim_after, ratio)
                            if dcldm0 == None:
                                dcldm0 = 0
                                log.warning(
                                    'Not enough data to determine dcldm (Cl_mach) at trim condition at Alt = {}, mach = {}, aoa = {}, aos = 0. Assumption: dcldm = 0'
                                    .format(alt, mach, round(trim_aoa_deg, 2)))
                        else:
                            dcddm0 = 0
                            dcldm0 = 0
                            log.warning(
                                'Not enough data to determine dcddm (Cd_mach) and dcldm (Cl_mach) at trim condition at Alt = {}, mach = {}, aoa = {}, aos = 0. Assumption: dcddm = dcldm = 0'
                                .format(alt, mach, round(trim_aoa_deg, 2)))

                        # Controls Derivatives to be found in the CPACS (To be calculated)
                        dcddeta0 = 0
                        dcldeta0 = 0
                        dcmsdeta0 = 0
                        dcddtau0 = 0
                        dcldtau0 = 0
                        dcmsdtau0 = 0

                        # Traduction Ceasiom -> Theory
                        Ue = u0 * np.cos(
                            trim_aoa
                        )  # *np.cos(aos) as aos = 0 at trim, cos(aos)=1
                        We = u0 * np.sin(
                            trim_aoa
                        )  # *np.cos(aos) as aos = 0 at trim, cos(aos)=1

                        # Dimentionless State Space variables,
                        # In generalised body axes coordinates ,
                        # simplifications: Ue=V0, We=0, sin(Theta_e)=0 cos(Theta_e)=0
                        if thrust_available:  #   If power data
                            X_u = -(2 * cd0 + mach * dcddm0) + 1 / (
                                0.5 * rho * s * a ^ 2
                            ) * dtaudm0  # dtaudm dimensional Thrust derivative at trim conditions, P340 Michael V. Cook
                        else:  #   Glider Mode
                            X_u = -(2 * cd0 + mach * dcddm0)

                        Z_u = -(2 * cl0 + mach * dcldm0)
                        M_u = 0  # Negligible for subsonic conditions  or better with P289 Yechout (cm_u+2cm0)

                        X_w = (cl0 - cd_alpha0)
                        Z_w = -(cl_alpha0 + cd0)
                        M_w = cm_alpha0

                        X_q = dcddqstar0  # Normally almost = 0
                        Z_q = dcldqstar0
                        M_q = -dcmsdqstar0

                        X_dotw = 0  # Negligible
                        Z_dotw = 1 / 3 * M_q / u0 / (
                            xh / mac
                        )  # Thumb rule : M_alpha_dot = 1/3 Mq , ( not true for 747 :caughey P83,M_alpha_dot = 1/6Mq )
                        M_dotw = 1 / 3 * M_q / u0  # Thumb rule : M_alpha_dot = 1/3 Mq

                        # Controls:
                        X_eta = dcddeta0  # To be found from the cpacs file, and defined by the user!
                        Z_eta = dcldeta0  # To be found from the cpacs file, and defined by the user!
                        M_eta = dcmsdeta0  # To be found from the cpacs file, and defined by the user!

                        X_tau = dcddtau0  # To be found from the cpacs file, and defined by the user!
                        Z_tau = dcldtau0  # To be found from the cpacs file, and defined by the user!
                        M_tau = dcmsdtau0  # To be found from the cpacs file, and defined by the user!
                        # -----------------  Traduction Ceasiom -> Theory   END -----------------------------------

                        # Sign check  (Ref: Thomas Yechout Book, P304)
                        check_sign_longi(cd_alpha0, M_w, cl_alpha0, M_dotw,
                                         Z_dotw, M_q, Z_q, M_eta, Z_eta)

                    # Laterl-Directional
                    if lateral_directional_analysis:
                        cml = []  # N
                        cmd = []  # L
                        aos = []
                        aoa = []  # For Ue We
                        cs = []  # For y_v
                        dcsdpstar = []  # y_p
                        dcmddpstar = []  # l_p
                        dcmldpstar = []  # n_p
                        dcsdrstar = []  # y_r
                        dcmldrstar = []  # n_r
                        dcmddrstar = []  # l_r

                        for index in find_index:
                            cml.append(cml_list[index])  # N , N_v
                            cmd.append(cmd_list[index])  # L ,  L_v
                            aos.append(aos_list[index] * np.pi / 180)
                            aoa.append(aoa_list[index])  # For Ue We
                            cs.append(cs_list[index])
                            dcsdpstar.append(dcsdpstar_list[index])  # y_p
                            dcmddpstar.append(dcmddpstar_list[index])  # l_p
                            dcmldpstar.append(dcmldpstar_list[index])  # n_p
                            dcsdrstar.append(dcsdrstar_list[index])  # y_r
                            dcmldrstar.append(dcmldrstar_list[index])  # n_r
                            dcmddrstar.append(dcmddrstar_list[index])  # l_r

                        #Trimm condition calculation
                        # speed derivatives :  y_v / l_v / n_v  /  Must be devided by speed given that the hyp v=Beta*U
                        if len(aos_unic) >= 2:
                            print('Mach : ', mach, '   and idx_mach : ',
                                  idx_mach)
                            cs_beta0 = speed_derivative_at_trim_lat(
                                cs_list, aos_list, aos_unic, idx_alt, idx_mach,
                                aoa_list, idx_trim_before, idx_trim_after,
                                ratio)  # y_v
                            if cs_beta0 == None:
                                cs_beta0 = 0
                                log.warning(
                                    'Not enough data to determine cs_beta (Y_v) at trim condition at Alt = {}, mach = {}, aoa = {}, aos = 0. Assumption: cs_beta = 0'
                                    .format(alt, mach, round(trim_aoa_deg, 2)))
                            cmd_beta0 = speed_derivative_at_trim_lat(
                                cmd_list, aos_list, aos_unic, idx_alt,
                                idx_mach, aoa_list, idx_trim_before,
                                idx_trim_after, ratio)  # l_v
                            if cmd_beta0 == None:
                                cmd_beta0 = 0
                                log.warning(
                                    'Not enough data to determine cmd_beta (L_v) at trim condition at Alt = {}, mach = {}, aoa = {}, aos = 0. Assumption: cmd_beta = 0'
                                    .format(alt, mach, round(trim_aoa_deg, 2)))
                            cml_beta0 = speed_derivative_at_trim_lat(
                                cml_list, aos_list, aos_unic, idx_alt,
                                idx_mach, aoa_list, idx_trim_before,
                                idx_trim_after, ratio)  # n_v
                            if cml_beta0 == None:
                                cml_beta0 = 0
                                log.warning(
                                    'Not enough data to determine cml_beta (N_v) at trim condition at Alt = {}, mach = {}, aoa = {}, aos = 0. Assumption: cml_beta = 0'
                                    .format(alt, mach, round(trim_aoa_deg, 2)))
                        else:
                            cs_beta0 = 0
                            cmd_beta0 = 0
                            cml_beta0 = 0
                            log.warning(
                                'Not enough data to determine cs_beta (Y_v), cmd_beta (L_v) and cml_beta (N_v) at trim condition at Alt = {}, mach = {}, aoa = {}, aos = 0. Assumption: cs_beta = cmd_beta = cml_beta = 0'
                                .format(alt, mach, round(trim_aoa_deg, 2)))

                        dcsdpstar0 = interpolation(dcsdpstar, idx_trim_before,
                                                   idx_trim_after,
                                                   ratio)  # y_p
                        dcmddpstar0 = interpolation(dcmddpstar,
                                                    idx_trim_before,
                                                    idx_trim_after,
                                                    ratio)  # l_p
                        dcmldpstar0 = interpolation(dcmldpstar,
                                                    idx_trim_before,
                                                    idx_trim_after,
                                                    ratio)  # n_p

                        dcsdrstar0 = interpolation(dcsdrstar, idx_trim_before,
                                                   idx_trim_after,
                                                   ratio)  # y_r
                        dcmldrstar0 = interpolation(dcmldrstar,
                                                    idx_trim_before,
                                                    idx_trim_after,
                                                    ratio)  # n_r
                        dcmddrstar0 = interpolation(dcmddrstar,
                                                    idx_trim_before,
                                                    idx_trim_after,
                                                    ratio)  # l_r

                        # TODO: calculate that and find in the cpacs
                        dcsdxi0 = 0
                        dcmddxi0 = 0
                        dcmldxi0 = 0
                        dcsdzeta0 = 0
                        dcmddzeta0 = 0
                        dcmldzeta0 = 0

                        # Traduction Ceasiom -> Theory
                        Y_v = cs_beta0
                        L_v = cmd_beta0
                        N_v = cml_beta0

                        Y_p = -dcsdpstar0 * mac / b
                        L_p = -dcmddpstar0 * mac / b
                        N_p = dcmldpstar0 * mac / b

                        Y_r = dcsdrstar0 * mac / b
                        N_r = -dcmldrstar0 * mac / b  # mac/b :Because coefficients in ceasiom are nondimensionalised by the mac instead of the span
                        L_r = dcmddrstar0 * mac / b

                        # Controls:
                        # Ailerons
                        Y_xi = dcsdxi0  # To be found from the cpacs file, and defined by the user!
                        L_xi = dcmddxi0  # To be found from the cpacs file, and defined by the user!
                        N_xi = dcmldxi0  # To be found from the cpacs file, and defined by the user!
                        # Rudder
                        Y_zeta = dcsdzeta0  # To be found from the cpacs file, and defined by the user!
                        L_zeta = dcmddzeta0  # To be found from the cpacs file, and defined by the user!
                        N_zeta = dcmldzeta0  # To be found from the cpacs file, and defined by the user!

                        Ue = u0 * np.cos(
                            trim_aoa
                        )  # *np.cos(aos) as aos = 0 at trim, cos(aos)=1
                        We = u0 * np.sin(
                            trim_aoa
                        )  # *np.cos(aos) as aos = 0 at trim, cos(aos)=1

                        # Sign check  (Ref: Thomas Yechout Book, P304)
                        check_sign_lat(Y_v, L_v, N_v, Y_p, L_p, Y_r, L_r, N_r,
                                       L_xi, Y_zeta, L_zeta, N_zeta)

                    if trim_aoa:
                        for angles in flight_path_angle:
                            theta_e = angles + trim_aoa

                            if longitudinal_analysis:
                                (A_longi, B_longi, x_u,z_u,m_u,x_w,z_w,m_w, x_q,z_q,m_q,x_theta,z_theta,m_theta,x_eta,z_eta,m_eta, x_tau,z_tau,m_tau)\
                                                                    = concise_derivative_longi(X_u,Z_u,M_u,X_w,Z_w,M_w,\
                                                                    X_q,Z_q,M_q,X_dotw,Z_dotw,M_dotw,X_eta,Z_eta,M_eta,\
                                                                    X_tau,Z_tau,M_tau, g, theta_e, u0,We,Ue,mac,m_adim,i_yy)

                                C_longi = np.identity(4)
                                D_longi = np.zeros((4, 2))
                                # Identify longitudinal roots
                                if longi_root_identification(
                                        A_longi
                                )[0] == None:  # If longitudinal root not complex conjugate raise warning and plot roots
                                    eg_value_longi = longi_root_identification(
                                        A_longi)[1]
                                    log.warning(
                                        'Longi : charcateristic equation  roots are not complex conjugate : {}'
                                        .format(eg_value_longi))
                                    legend = [
                                        'Root1', 'Root2', 'Root3', 'Root4'
                                    ]
                                    plot_title = 'S-plane longitudinal characteristic equation roots at (Alt = {}, Mach= {}, trimed at aoa = {}°)'.format(
                                        alt, mach, trim_aoa)
                                    plot_splane(eg_value_longi, plot_title,
                                                legend, show_plots, save_plots)
                                else:  # Longitudinal roots are complex conjugate
                                    (sp1, sp2, ph1, ph2, eg_value_longi , eg_vector_longi, eg_vector_longi_magnitude)\
                                            = longi_root_identification(A_longi)
                                    legend = ['sp1', 'sp2', 'ph1', 'ph2']
                                    plot_title = 'S-plane longitudinal characteristic equation roots at (Alt = {}, Mach= {}, trimed at aoa = {}°)'.format(
                                        alt, mach, trim_aoa)
                                    plot_splane(eg_value_longi, plot_title,
                                                legend, show_plots, save_plots)

                                    # Modes parameters : damping ratio, frequence, CAP, time tou double amplitude
                                    Z_w_dimensional = Z_w * (
                                        0.5 * rho * s * u0**2
                                    )  # Z_w* (0.5*rho*s*u0**2)  is the dimensional form of Z_w,   Z_w = -(cl_alpha0 + cd0) P312 Yechout
                                    z_alpha = Z_w_dimensional * u0 / m  # alpha = w/u0 hence,   z_alpha =  Z_w_dimensional * u0      [Newton/rad/Kg :   m/s^2 /rad]
                                    load_factor = -z_alpha / g  #  number of g's/rad (1g/rad 2g/rad  3g/rad)
                                    (sp_freq, sp_damp, sp_cap, ph_freq, ph_damp, ph_t2)\
                                            =  longi_mode_characteristic(sp1,sp2,ph1,ph2,load_factor)

                                    # Rating
                                    sp_damp_rate = short_period_damping_rating(
                                        aircraft_class, sp_damp)
                                    sp_freq_rate = short_period_frequency_rating(
                                        flight_phase, aircraft_class, sp_freq,
                                        load_factor)
                                    # Plot SP freq vs Load factor
                                    legend = 'Alt = {}, Mach= {}, trim aoa = {}°'.format(
                                        alt, mach, trim_aoa)
                                    if flight_phase == 'A':
                                        plot_sp_level_a([load_factor],
                                                        [sp_freq], legend,
                                                        show_plots, save_plots)
                                    elif flight_phase == 'B':
                                        plot_sp_level_b(
                                            x_axis, y_axis, legend, show_plots,
                                            save_plots)
                                    else:
                                        plot_sp_level_c(
                                            x_axis, y_axis, legend, show_plots,
                                            save_plots)
                                    sp_cap_rate = cap_rating(
                                        flight_phase, sp_cap, sp_damp)
                                    ph_rate = phugoid_rating(ph_damp, ph_t2)
                                    # Raise warning if unstable mode in the log file
                                    if sp_damp_rate == None:
                                        log.warning(
                                            'ShortPeriod UNstable at Alt = {}, Mach = {} , due to DampRatio = {} '
                                            .format(alt, mach,
                                                    round(sp_damp, 4)))
                                    if sp_freq_rate == None:
                                        log.warning(
                                            'ShortPeriod UNstable at Alt = {}, Mach = {} , due to UnDampedFreq = {} rad/s '
                                            .format(alt, mach,
                                                    round(sp_freq, 4)))
                                    if sp_cap_rate == None:
                                        log.warning(
                                            'ShortPeriod UNstable at Alt = {}, Mach = {} , with CAP evaluation, DampRatio = {} , CAP = {} '
                                            .format(alt, mach,
                                                    round(sp_damp, 4),
                                                    round(sp_cap, 4)))
                                    if ph_rate == None:
                                        log.warning(
                                            'Phugoid UNstable at Alt = {}, Mach = {} , DampRatio = {} , UnDampedFreq = {} rad/s'
                                            .format(alt, mach,
                                                    round(ph_damp, 4),
                                                    round(ph_freq, 4)))

                                    # TODO
                                    # Compute numerator TF for (Alt, mach, flight_path_angle, aoa_trim, aos=0

                            if lateral_directional_analysis:
                                (A_direc, B_direc,y_v,l_v,n_v,y_p,y_phi,y_psi,l_p,l_phi,l_psi,n_p,y_r,l_r,n_r,n_phi,n_psi, y_xi,l_xi,n_xi, y_zeta,l_zeta,n_zeta)\
                                    = concise_derivative_lat(Y_v,L_v,N_v,Y_p,L_p,N_p,Y_r,L_r,N_r,\
                                                                            Y_xi,L_xi,N_xi, Y_zeta,L_zeta,N_zeta,\
                                                                            g, b, theta_e, u0,We,Ue,m_adim,i_xx,i_zz,i_xz )

                                C_direc = np.identity(5)
                                D_direc = np.zeros((5, 2))

                                if direc_root_identification(
                                        A_direc
                                )[0] == None:  # Lateral-directional roots are correctly identified
                                    eg_value_direc = direc_root_identification(
                                        A_direc)[1]
                                    print(
                                        'Lat-Dir : charcateristic equation  roots are not complex conjugate : {}'
                                        .format(eg_value_direc))
                                    legend = [
                                        'Root1', 'Root2', 'Root3', 'Root4'
                                    ]
                                    plot_title = 'S-plane lateral characteristic equation roots at (Alt = {}, Mach= {}, trimed at aoa = {}°)'.format(
                                        alt, mach, trim_aoa)
                                    plot_splane(eg_value_direc, plot_title,
                                                legend, show_plots, save_plots)
                                else:  # Lateral-directional roots are correctly identified
                                    (roll, spiral, dr1, dr2, eg_value_direc, eg_vector_direc, eg_vector_direc_magnitude)\
                                        = direc_root_identification(A_direc)
                                    legend = ['roll', 'spiral', 'dr1', 'dr2']
                                    plot_title = 'S-plane lateralcharacteristic equation roots at (Alt = {}, Mach= {}, trimed at aoa = {}°)'.format(
                                        alt, mach, trim_aoa)
                                    plot_splane(eg_value_direc, plot_title,
                                                legend, show_plots, save_plots)
                                    (roll_timecst, spiral_timecst, spiral_t2,
                                     dr_freq, dr_damp,
                                     dr_damp_freq) = direc_mode_characteristic(
                                         roll, spiral, dr1, dr2)

                                    # Rating
                                    roll_rate = roll_rating(
                                        flight_phase, aircraft_class,
                                        roll_timecst)
                                    spiral_rate = spiral_rating(
                                        flight_phase, spiral_timecst,
                                        spiral_t2)
                                    dr_rate = dutch_roll_rating(
                                        flight_phase, aircraft_class, dr_damp,
                                        dr_freq, dr_damp_freq)

                                    # Raise warning in the log file if unstable mode
                                    if roll_rate == None:
                                        log.warning(
                                            'Roll mode UNstable at Alt = {}, Mach = {} , due to roll root = {}, roll time contatant = {} s'
                                            .format(alt, mach,
                                                    round(roll_root, 4),
                                                    round(roll_timecst, 4)))
                                    if spiral_rate == None:
                                        log.warning(
                                            'Spiral mode UNstable at Alt = {}, Mach = {} , spiral root = {}, time_double_ampl = {}'
                                            .format(alt, mach,
                                                    round(spiral_root, 4),
                                                    round(spiral_t2, 4)))
                                    if dr_rate == None:
                                        log.warning(
                                            'Dutch Roll UNstable at Alt = {}, Mach = {} , Damping Ratio = {} , frequency = {} rad/s '
                                            .format(alt, mach,
                                                    round(dr_damp, 4),
                                                    round(dr_freq, 4)))
Exemplo n.º 11
0
def static_stability_analysis(cpacs_path, cpacs_out_path):
    """Function to analyse a full Aeromap

    Function 'static_stability_analysis' analyses longitudinal static static
    stability and directionnal static.

    Args:
        cpacs_path (str): Path to CPACS file
        cpacs_out_path (str):Path to CPACS output file
        plot (boolean): Choise to plot graph or not

    Returns:
        *   Adrvertisements certifying if the aircraft is stable or Not
        *   In case of longitudinal static UNstability or unvalid test on data:
                -	Plot cms VS aoa for constant Alt, Mach and different aos
                -	Plot cms VS aoa for const alt and aos=0 and different mach
                -	plot cms VS aoa for constant mach, aos=0 and different altitudes
        *  In case of directionnal static UNstability or unvalid test on data:
                -	Pcot cml VS aos for constant Alt, Mach and different aoa
                -	Plot cml VS aos for const alt and aoa and different mach
                -	plot cml VS aos for constant mach, AOA and different altitudes
        *  Plot one graph of  cruising angles of attack for different mach and altitudes

    Make the following tests:            (To put in the documentation)
        *   Check the CPACS path
        *   For longitudinal static stability analysis:
                -   If there is more than one angle of attack for a given altitude, mach, aos
                -   If cml values are only zeros for a given altitude, mach, aos
                -   If there one aoa value which is repeated for a given altitude, mach, aos
        *   For directionnal static stability analysis:
                -   If there is more than one angle of sideslip for a given altitude, mach, aoa
                -   If cms values are only zeros for a given altitude, mach, aoa
                -   If there one aos value which is repeated for a given altitude, mach, aoa
    """

    # TODO: add as CPACS option
    plot_for_different_mach = False  # To check Mach influence
    plot_for_different_alt = False  # To check Altitude influence

    tixi = cpsf.open_tixi(cpacs_path)

    # Get aeromap uid
    aeromap_uid = cpsf.get_value(tixi, STATIC_ANALYSIS_XPATH + '/aeroMapUid')
    log.info('The following aeroMap will be analysed: ' + aeromap_uid)

    show_plots = cpsf.get_value_or_default(
        tixi, STATIC_ANALYSIS_XPATH + '/showPlots', False)
    save_plots = cpsf.get_value_or_default(
        tixi, STATIC_ANALYSIS_XPATH + '/savePlots', False)

    # Aircraft mass configuration
    selected_mass_config_xpath = STATIC_ANALYSIS_XPATH + '/massConfiguration'
    mass_config = cpsf.get_value(tixi, selected_mass_config_xpath)
    # TODO: use get value or default instead and deal with not mass config
    log.info('The aircraft mass configuration used for analysis is: ' +
             mass_config)

    model_xpath = '/cpacs/vehicles/aircraft/model'
    masses_location_xpath = model_xpath + '/analyses/massBreakdown/designMasses'
    mass_config_xpath = masses_location_xpath + '/' + mass_config
    if tixi.checkElement(mass_config_xpath):
        mass_xpath = mass_config_xpath + '/mass'
        m = cpsf.get_value(tixi, mass_xpath)  # aircraft mass [Kg]
    else:
        raise ValueError(
            ' !!! The mass configuration : {} is not defined in the CPACS file !!!'
            .format(mass_config))

    # Wing plane AREA.
    ref_area_xpath = model_xpath + '/reference/area'
    s = cpsf.get_value(
        tixi, ref_area_xpath
    )  # Wing area : s  for non-dimonsionalisation of aero data.

    Coeffs = apmf.get_aeromap(tixi, aeromap_uid)
    Coeffs.print_coef_list()

    alt_list = Coeffs.alt
    mach_list = Coeffs.mach
    aoa_list = Coeffs.aoa
    aos_list = Coeffs.aos
    cl_list = Coeffs.cl
    cd_list = Coeffs.cd
    cml_list = Coeffs.cml
    cms_list = Coeffs.cms
    cmd_list = Coeffs.cmd

    alt_unic = get_unic(alt_list)
    mach_unic = get_unic(mach_list)
    aos_unic = get_unic(aos_list)
    aoa_unic = get_unic(aoa_list)

    # TODO: get incremental map from CPACS
    # Incremental map elevator
    incrementalMap = False  # if increment map available
    # aeromap_xpath = tixi.uIDGetXPath(aeromap_uid)
    # dcms_xpath = aeromap_xpath + '/aeroPerformanceMap/incrementMaps/incrementMap'  + ' ....to complete'

    # TODO from incremental map
    # dcms_list = [0.52649,0.53744,0.54827,0.55898,0.56955,0.58001,0.59033,0.6005,0.61053,0.62043,0.63018,0.63979,0.64926,0.65859,0.66777,0.67684,0.53495,0.54603,0.55699,0.56783,0.57854,0.58912,0.59957,0.60986,0.62002,0.63004,0.63991,0.64964,0.65923,0.66867,0.67798,0.68717,0.55,0.56131,0.5725,0.58357,0.59451,0.60531,0.61598,0.62649,0.63687,0.64709,0.65718,0.66712,0.67691,0.68658,0.69609,0.70548,0.57333,0.585,0.59655,0.60796,0.61925,0.63038,0.64138,0.65224,0.66294,0.67349,0.68389,0.69415,0.70427,0.71424,0.72408,0.7338,0.60814,0.62033,0.63239,0.6443,0.65607,0.6677,0.67918,0.6905,0.70168,0.7127,0.72357,0.7343,0.74488,0.75532,0.76563,0.77581,0.66057,0.6735,0.68628,0.69891,0.71139,0.72371,0.73588,0.74789,0.75974,0.77144,0.78298,0.79438,0.80562,0.81673,0.82772,0.83858,\
    # 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
    # -0.61653,-0.61254,-0.60842,-0.60419,-0.59988,-0.59549,-0.59105,-0.58658,-0.5821,-0.57762,-0.57318,-0.56879,-0.56447,-0.56025,-0.55616,-0.55221,-0.62605,-0.62194,-0.6177,-0.61336,-0.60894,-0.60444,-0.59988,-0.59531,-0.59072,-0.58614,-0.58159,-0.57711,-0.5727,-0.56841,-0.56423,-0.5602,-0.64293,-0.63862,-0.63418,-0.62963,-0.62499,-0.62029,-0.61554,-0.61076,-0.60598,-0.60123,-0.5965,-0.59185,-0.58728,-0.58282,-0.5785,-0.57433,-0.66906,-0.6644,-0.65963,-0.65475,-0.64978,-0.64476,-0.63971,-0.63461,-0.62954,-0.62449,-0.61949,-0.61456,-0.60973,-0.60503,-0.60048,-0.59609,-0.70787,-0.70268,-0.69739,-0.692,-0.68653,-0.68101,-0.67546,-0.66991,-0.66437,-0.65888,-0.65344,-0.6481,-0.64289,-0.63781,-0.6329,-0.62819,-0.76596,-0.75994,-0.75382,-0.74762,-0.74135,-0.73505,-0.72874,-0.72243,-0.71617,-0.70997,-0.70387,-0.69788,-0.69205,-0.68639,-0.68094,-0.67573]
    # dcms are given for a relative deflection of [-1,0,1] of the

    # # TODO get from CPACS
    # elevator_deflection = 15

    # Gather trim aoa conditions
    trim_alt_longi_list = []
    trim_mach_longi_list = []
    trim_aoa_longi_list = []
    trim_aos_longi_list = []
    trim_legend_longi_list = []
    trim_derivative_longi_list = []

    # Gather trim aos_list for different Alt & mach , for aoa = 0
    trim_alt_lat_list = []
    trim_mach_lat_list = []
    trim_aoa_lat_list = []
    trim_aos_lat_list = []
    trim_legend_lat_list = []
    trim_derivative_lat_list = []

    # Gather trim aos_list for different Alt & mach , for aoa = 0
    trim_alt_direc_list = []
    trim_mach_direc_list = []
    trim_aoa_direc_list = []
    trim_aos_direc_list = []
    trim_legend_direc_list = []
    trim_derivative_direc_list = []

    # To store in cpacs result
    longi_unstable_cases = []
    lat_unstable_cases = []
    direc_unstable_cases = []

    cpacs_stability_longi = 'True'
    cpacs_stability_lat = 'True'
    cpacs_stability_direc = 'True'

    # Aero analyses for all given altitude, mach and aos_list, over different
    for alt in alt_unic:

        Atm = get_atmosphere(alt)
        g = Atm.grav
        a = Atm.sos
        rho = Atm.dens

        # Find index of altitude which have the same value
        idx_alt = [i for i in range(len(alt_list)) if alt_list[i] == alt]

        # Prepar trim condition lists
        trim_alt_longi = []
        trim_mach_longi = []
        trim_aoa_longi = []
        trim_aos_longi = []
        trim_legend_longi = []
        trim_derivative_longi = []

        # Prepar trim condition lists
        trim_alt_lat = []
        trim_mach_lat = []
        trim_aoa_lat = []
        trim_aos_lat = []
        trim_legend_lat = []
        trim_derivative_lat = []

        # Prepar trim condition lists
        trim_alt_direc = []
        trim_mach_direc = []
        trim_aoa_direc = []
        trim_aos_direc = []
        trim_legend_direc = []
        trim_derivative_direc = []

        for mach in mach_unic:
            u0 = a * mach
            # Find index of mach which have the same value
            idx_mach = [
                j for j in range(len(mach_list)) if mach_list[j] == mach
            ]

            # Longitudinal stability
            # Analyse in function of the angle of attack for given, alt, mach and aos_list
            # Prepare variables for plots
            plot_cms = []
            plot_aoa = []
            plot_legend = []
            plot_title = r'Pitch moment coefficeint $C_M$ vs $\alpha$ @ Atl = ' + str(
                alt) + 'm, and Mach = ' + str(mach)
            xlabel = r'$\alpha$ [°]'
            ylabel = r'$C_M$ [-]'
            # Init for determining if it's an unstable case
            longitudinaly_stable = True
            # by default, cms don't  cross 0 line
            crossed = False

            # Find index at which  aos= 0
            idx_aos = [j for j in range(len(aos_list)) if aos_list[j] == 0]
            find_idx = get_index(idx_alt, idx_mach, idx_aos)

            # If find_idx is empty an APM function would have corrected before
            # If there there is only one value  in  find_idx for a given Alt, Mach, aos_list, no analyse can be performed
            if len(find_idx) == 1:
                log.info('Longitudinal : only one data, one aoa(' +str(aoa_list[find_idx[0]]) \
                + '), for Altitude =  '+ str(alt) + '[m] , Mach = ' \
                + str(mach) + '  and aos = 0 , no stability analyse will be performed' )
                cpacs_stability_longi = 'NotCalculated'
            elif len(find_idx
                     ) > 1:  # if there is at least 2 values in find_idx :

                # Find all cms_list values for index corresonding to an altitude, a mach, an aos_list=0, and different aoa_list
                cms = []
                aoa = []
                cl = []
                cd = []
                for index in find_idx:
                    cms.append(cms_list[index])
                    aoa.append(aoa_list[index])
                    cl.append(cl_list[index])
                    cd.append(cd_list[index])

                # Save values which will be plot
                plot_cms.append(cms)
                plot_aoa.append(aoa)
                curve_legend = r'$\beta$ = 0°'
                plot_legend.append(curve_legend)

                # If all aoa values are the same while the calculating the derivative, a division by zero will be prevented.
                aoa_good = True
                for jj in range(len(aoa) - 1):
                    if aoa[jj] == aoa[jj + 1]:
                        aoa_good = False
                        log.error(
                            'Alt = {} , at least 2 aoa values are equal in aoa list: {} at Mach= {}, aos = 0'
                            .format(alt, aoa, mach))
                        break

                if aoa_good:
                    # Required lift for level flight
                    cl_required = (m * g) / (0.5 * rho * u0**2 * s)
                    (trim_aoa, idx_trim_before, idx_trim_after,
                     ratio) = trim_condition(alt, mach, cl_required, cl, aoa)

                    if trim_aoa:
                        trim_cms = interpolation(cms, idx_trim_before,
                                                 idx_trim_after, ratio)
                        pitch_moment_derivative_deg = (
                            cms[idx_trim_after] - cms[idx_trim_before]) / (
                                aoa[idx_trim_after] - aoa[idx_trim_before])
                        # Find incremental cms
                        if incrementalMap:
                            for index, mach_number in enumerate(mach_unic, 0):
                                if mach_number == mach:
                                    mach_index = index
                            dcms_before = dcms_list[mach_index * len(aoa_unic)
                                                    + idx_trim_before]
                            dcms_after = dcms_list[mach_index * len(aoa_unic) +
                                                   idx_trim_after]
                            dcms = dcms_before + ratio * (dcms_after -
                                                          dcms_before)
                            trim_elevator_relative_deflection = -trim_cms / dcms
                            trim_elevator = trim_elevator_relative_deflection * elevator_deflection  # Trim elevator deflection in [°]
                        else:
                            dcms = None
                            trim_elevator = None
                    else:
                        trim_cms = None
                        pitch_moment_derivative_deg = None
                        dcms = None
                        trim_elevator = None

                    fig = plt.figure(figsize=(9, 3))
                    plot_title___ = r'$C_L$ and $C_m$ vs $\alpha$ at Mach = {}'.format(
                        mach)
                    plt.title(plot_title___,
                              fontdict=None,
                              loc='center',
                              pad=None)
                    plt.plot(aoa, cl, marker='o', markersize=4, linewidth=1)
                    plt.plot(aoa,
                             cms,
                             marker='+',
                             markerfacecolor='orange',
                             markersize=12)
                    plt.plot([aoa[0], aoa[-1]], [cl_required, cl_required],
                             markerfacecolor='red',
                             markersize=12)
                    plt.legend([r'$C_L$', r'$C_M$', r'$C_{Lrequired}$'])
                    ax = plt.gca()
                    ax.annotate(r'$\alpha$ [°]',
                                xy=(1, 0),
                                ha='right',
                                va='top',
                                xycoords='axes fraction',
                                fontsize=12)
                    # ax.annotate('Coefficient', xy=(0,1), ha='left', va='center', xycoords='axes fraction', fontsize=12)
                    plt.grid(True)

                    if show_plots:
                        plt.show()

                # Conclusion about stability, if the cms curve has crossed the 0 line and there is not 2 repeated aoa for the same alt, mach and aos.
                # if  idx_trim_before != idx_trim_after allow to know if the cm curve crosses the 0 line. '

                if idx_trim_before != idx_trim_after and aoa_good:
                    if pitch_moment_derivative_deg < 0:
                        log.info('Vehicle longitudinaly staticaly stable.')
                        trim_alt_longi.append(alt)
                        trim_mach_longi.append(mach)
                        trim_aoa_longi.append(trim_aoa)
                        trim_aos_longi.append(0)
                        trim_derivative_longi.append(
                            pitch_moment_derivative_deg)

                    elif pitch_moment_derivative_deg == 0:
                        longitudinaly_stable = False
                        log.error(
                            'Alt = ' + str(alt) +
                            'Vehicle longitudinaly staticaly neutral stable.')
                    else:  #pitch_moment_derivative_deg > 0
                        longitudinaly_stable = False
                        log.error(
                            'Alt = ' + str(alt) +
                            'Vehicle *NOT* longitudinaly staticaly stable.')

                # If not stable store the set [alt, mach, aos] at which the aircraft is unstable.
                if not longitudinaly_stable:
                    longi_unstable_cases.append([alt, mach, 0])
                    # To write in the output CPACS that the aircraft is not longitudinaly stable
                    cpacs_stability_longi = 'False'
            #PLot cms VS aoa for constant Alt, Mach and different aos
            if plot_cms:
                plot_multicurve(plot_cms, plot_aoa, plot_legend, plot_title,
                                xlabel, ylabel, show_plots, save_plots)

            ## LATERAL
            plot_cmd = []
            plot_aos = []
            plot_legend = []
            plot_title = r'Roll moment coefficient $C_L$ vs $\beta$ @ Atl = ' + str(
                alt) + 'm, and Mach = ' + str(mach)
            xlabel = r'$\beta$ [°]'
            ylabel = r'$C_L$ [-]'
            # Init for determinig if it is an unstability case
            laterally_stable = True
            # Find INDEX
            for aoa in aoa_unic:

                # by default, don't  cross 0 line
                crossed = False
                idx_aoa = [
                    j for j in range(len(aoa_list)) if aoa_list[j] == aoa
                ]
                find_idx = get_index(idx_alt, idx_mach, idx_aoa)

                # If find_idx is empty an APM function would have corrected before
                # If there there is only one value  in  find_idx for a given Alt, Mach, aos_list, no analyse can be performed
                if len(find_idx) == 1:
                    log.info('Laterral-Directional : only one data, one aos (' + str(aos_list[find_idx[0]])  \
                              +'), for Altitude = '+str(alt)+'[m], Mach = '   \
                              + str(mach) + ' and aoa = ' + str(aoa)           \
                              + ' no stability analyse performed')
                    cpacs_stability_lat = 'NotCalculated'

                elif len(find_idx
                         ) > 1:  #if there is at least 2 values in find_idx
                    cmd = []
                    aos = []
                    for index in find_idx:
                        cmd.append(
                            -cmd_list[index]
                        )  # menus sign because cmd sign convention on ceasiom is the oposite as books convention
                        aos.append(aos_list[index])
                    aos, cmd = order_correctly(
                        aos,
                        cmd)  # To roder the lists with values for growing aos
                    #  If cmd Curve crosses th 0 line more than once na stability analysis can be performed
                    curve_legend = r'$\alpha$ = ' + str(aoa) + r' °'

                    # Save values which will be plotted
                    plot_cmd.append(cmd)
                    plot_aos.append(aos)
                    plot_legend.append(curve_legend)

                    aos_good = True
                    for jj in range(len(aos) - 1):
                        if aos[jj] == aos[jj + 1]:
                            aos_good = False
                            log.error(
                                'Alt = {} , at least 2 aos values are equal in aos list: {} , Mach= {}, aos = {}'
                                .format(alt, aoa, mach, aos))
                            break

                    if aos_good:
                        cruise_aos, roll_moment_derivative, idx_trim_before, idx_trim_after, ratio = trim_derivative(
                            alt, mach, cmd, aos)

                    if idx_trim_before != idx_trim_after and aos_good:
                        if roll_moment_derivative < 0:
                            log.info('Vehicle laterally staticaly stable.')
                            if aoa == 0:
                                trim_alt_lat.append(alt)
                                trim_mach_lat.append(mach)
                                trim_aoa_lat.append(cruise_aos)
                                trim_aos_lat.append(aoa)
                                trim_derivative_lat.append(
                                    roll_moment_derivative)
                        if roll_moment_derivative == 0:
                            laterally_stable = False
                            log.error(
                                'At alt = ' + str(alt) +
                                'Vehicle laterally staticaly neutral stable.')
                        if roll_moment_derivative > 0:
                            laterally_stable = False
                            log.error(
                                'Alt = ' + str(alt) +
                                'Vehicle *NOT* laterally staticaly stable.')

                    # If not stable store the set [alt, mach, aos] at which the aircraft is unstable.
                    if not laterally_stable:
                        lat_unstable_cases.append([alt, mach, aoa])
                        # To write in the output CPACS that the aircraft is not longitudinaly stable
                        cpacs_stability_lat = 'False'

            #PLot cmd VS aos for constant alt, mach and different aoa if not stable
            if plot_cmd:
                plot_multicurve(plot_cmd, plot_aos, plot_legend, plot_title,
                                xlabel, ylabel, show_plots, save_plots)

            ## Directional
            plot_cml = []
            plot_aos = []
            plot_legend = []
            plot_title = r'Yaw moment coefficient $C_N$ vs $\beta$ @ Atl = ' + str(
                alt) + 'm, and Mach = ' + str(mach)
            xlabel = r'$\beta$ [°]'
            ylabel = r'$C_N$ [-]'
            # Init for determinig if it is an unstability case
            dirrectionaly_stable = True
            # Find INDEX
            for aoa in aoa_unic:
                # by default, don't  cross 0 line
                crossed = False
                idx_aoa = [
                    j for j in range(len(aoa_list)) if aoa_list[j] == aoa
                ]
                find_idx = get_index(idx_alt, idx_mach, idx_aoa)

                # If find_idx is empty an APM function would have corrected before
                # If there there is only one value  in  find_idx for a given Alt, Mach, aos_list, no analyse can be performed
                if len(find_idx) == 1:
                    log.info('Laterral-Directional : only one data, one aos (' + str(aos_list[find_idx[0]])  \
                              +'), for Altitude = '+str(alt)+'[m], Mach = '   \
                              + str(mach) + ' and aoa = ' + str(aoa)           \
                              + ' no stability analyse performed')
                    cpacs_stability_direc = 'NotCalculated'

                elif len(find_idx
                         ) > 1:  #if there is at least 2 values in find_idx
                    cml = []
                    aos = []
                    for index in find_idx:
                        cml.append(
                            -cml_list[index]
                        )  # menus sign because cml sign convention on ceasiom is the oposite as books convention
                        aos.append(aos_list[index])
                    aos, cml = order_correctly(
                        aos, cml)  # To order values with growing aos
                    #  If cml Curve crosses th 0 line more than once na stability analysis can be performed
                    curve_legend = r'$\alpha$ = ' + str(aoa) + r' °'

                    # Save values which will be plot
                    plot_cml.append(cml)
                    plot_aos.append(aos)
                    plot_legend.append(curve_legend)

                    aos_good = True
                    for jj in range(len(aos) - 1):
                        if aos[jj] == aos[jj + 1]:
                            aos_good = False
                            log.error(
                                'Alt = {} , at least 2 aos values are equal in aos list: {} , Mach= {}, aos = {}'
                                .format(alt, aoa, mach, aos))
                            break

                    if aos_good:
                        cruise_aos, side_moment_derivative, idx_trim_before, idx_trim_after, ratio = trim_derivative(
                            alt, mach, cml, aos)

                    if idx_trim_before != idx_trim_after and aos_good:
                        if side_moment_derivative > 0:
                            log.info('Vehicle directionnaly staticaly stable.')
                            if aoa == 0:
                                trim_alt_direc.append(alt)
                                trim_mach_direc.append(mach)
                                trim_aoa_direc.append(cruise_aos)
                                trim_aos_direc.append(aoa)
                                trim_derivative_direc.append(
                                    side_moment_derivative)
                        if side_moment_derivative == 0:
                            dirrectionaly_stable = False
                            log.error(
                                'At alt = ' + str(alt) +
                                'Vehicle directionnaly staticaly neutral stable.'
                            )
                        if side_moment_derivative < 0:
                            dirrectionaly_stable = False
                            log.error(
                                'Alt = ' + str(alt) +
                                'Vehicle *NOT* directionnaly staticaly stable.'
                            )

                    # If not stable store the set [alt, mach, aos] at which the aircraft is unstable.
                    if not dirrectionaly_stable:
                        direc_unstable_cases.append([alt, mach, aoa])
                        # To write in the output CPACS that the aircraft is not longitudinaly stable
                        cpacs_stability_direc = 'False'

            # PLot cml VS aos for constant alt, mach and different aoa if not stable
            if plot_cml:
                plot_multicurve(plot_cml, plot_aos, plot_legend, plot_title,
                                xlabel, ylabel, show_plots, save_plots)

        # Add trim conditions for the given altitude (longi analysis)
        if trim_aoa_longi:
            trim_aoa_longi_list.append(trim_aoa_longi)
            trim_mach_longi_list.append(trim_mach_longi)
            trim_legend_longi_list.append('Altitude = ' + str(alt) + '[m]')
            trim_alt_longi_list.append(trim_alt_longi)
            trim_aos_longi_list.append(trim_aos_longi)
            trim_derivative_longi_list.append(trim_derivative_longi)

        if trim_aos_lat:
            trim_aos_lat_list.append(trim_aos_lat)
            trim_mach_lat_list.append(trim_mach_lat)
            trim_legend_lat_list.append('Alt = ' + str(alt) + '[m]')
            trim_alt_lat_list.append(trim_alt_lat)
            trim_aoa_lat_list.append(trim_aoa_lat)
            trim_derivative_lat_list.append(trim_derivative_lat)

        # Add trim conditions for the given altitude (direcanalysis)
        if trim_aos_direc:
            trim_aos_direc_list.append(trim_aos_direc)
            trim_mach_direc_list.append(trim_mach_direc)
            trim_legend_direc_list.append('Alt = ' + str(alt) + '[m]')
            trim_alt_direc_list.append(trim_alt_direc)
            trim_aoa_direc_list.append(trim_aoa_direc)
            trim_derivative_direc_list.append(trim_derivative_direc)

        # MACH PLOTS
        if plot_for_different_mach:  # To check Altitude Mach
            ## LONGI
            # Plot cms vs aoa for const alt and aos = 0 and different mach
            idx_aos = [k for k in range(len(aos_list)) if aos_list[k] == 0]
            plot_cms = []
            plot_aoa = []
            plot_legend = []
            plot_title = r'Pitch moment coefficient $C_M$ vs $\alpha$ @ Atl = ' + str(
                alt) + r'm, and $\beta$ = 0 °'
            xlabel = r'$\alpha$ [°]'
            ylabel = r'$C_M$ [-]'
            # Init for determinig if it is an unstability case
            longitudinaly_stable = True

            for mach in mach_unic:
                idx_mach = [
                    j for j in range(len(mach_list)) if mach_list[j] == mach
                ]
                find_idx = get_index(idx_alt, idx_aos, idx_mach)

                # If there is only one value in Find_idx
                # An error message has been already printed through the first part of the code
                # Check if it is an unstability case detected previously
                for combination in longi_unstable_cases:
                    if combination[0] == alt and combination[
                            1] == mach and combination[2] == aos:
                        longitudinaly_stable = False

                # If there is at list 2 values in find_idx :
                if len(find_idx) > 1:
                    # Find all cms_list values for index corresonding to an altitude, a mach, an aos_list=0, and different aoa_list
                    cms = []
                    aoa = []
                    for index in find_idx:
                        cms.append(cms_list[index])
                        aoa.append(aoa_list[index])
                    # Save values which will be plot
                    plot_cms.append(cms)
                    plot_aoa.append(aoa)
                    curve_legend = 'Mach = ' + str(mach)
                    plot_legend.append(curve_legend)
            #PLot cms VS aoa for constant Alt, aoa and different mach
            if plot_cms:
                plot_multicurve(plot_cms, plot_aoa, plot_legend, plot_title,
                                xlabel, ylabel, show_plots, save_plots)

            ## LATERAL
            # Plot cmd vs aos for const alt and aoa and different mach
            for aoa in aoa_unic:
                idx_aoa = [
                    k for k in range(len(aoa_list)) if aoa_list[k] == aoa
                ]
                plot_cmd = []
                plot_aos = []
                plot_legend = []
                plot_title = r'Roll moment coefficiel $C_L$ vs $\beta$ @ Atl = ' + str(
                    alt) + r'm, and $\alpha$= ' + str(aoa) + r' °'
                xlabel = r'$\beta$ [°]'
                ylabel = r'$C_L$ [-]'

                # Init for determinig if it is an unstability case
                laterally_stable = True

                for mach in mach_unic:
                    idx_mach = [
                        j for j in range(len(mach_list))
                        if mach_list[j] == mach
                    ]
                    find_idx = get_index(idx_alt, idx_aoa, idx_mach)

                    #If there is only one valur in find_idx
                    # An error message has been already printed through the first part of the code

                    # Check if it is an unstability case detected previously
                    for combination in lat_unstable_cases:
                        if combination[0] == alt and combination[
                                1] == mach and combination[2] == aoa:
                            laterally_stable = False

                    # If there is at list 2 values in find_idx :
                    if len(find_idx) > 1:
                        # Find all cmd_list values for index corresonding to an altitude, a mach, an aos_list=0, and different aoa_list
                        cmd = []
                        aos = []
                        for index in find_idx:
                            cmd.append(-cmd_list[index])
                            aos.append(aos_list[index])
                        aos, cmd = order_correctly(
                            aos, cmd)  # To order values with growing aos

                        # Save values which will be plot
                        plot_cmd.append(cmd)
                        plot_aos.append(aos)
                        curve_legend = 'Mach = ' + str(mach)
                        plot_legend.append(curve_legend)

                if plot_cmd:
                    # Plot cmd VS aos for constant Alt, aoa and different mach
                    plot_multicurve(plot_cmd, plot_aos, plot_legend,
                                    plot_title, xlabel, ylabel, show_plots,
                                    save_plots)

            ## Directional
            # Plot cml vs aos for const alt and aoa and different mach
            for aoa in aoa_unic:
                idx_aoa = [
                    k for k in range(len(aoa_list)) if aoa_list[k] == aoa
                ]
                plot_cml = []
                plot_aos = []
                plot_legend = []
                plot_title = r'Yaw moment coefficiel $C_N$ vs $\beta$ @ Atl = ' + str(
                    alt) + r'm, and $\alpha$= ' + str(aoa) + r' °'
                xlabel = r'$\beta$ [°]'
                ylabel = r'$C_N$ [-]'

                # Init for determinig if it is an unstability case
                dirrectionaly_stable = True

                for mach in mach_unic:
                    idx_mach = [
                        j for j in range(len(mach_list))
                        if mach_list[j] == mach
                    ]
                    find_idx = get_index(idx_alt, idx_aoa, idx_mach)
                    #If there is only one valur in find_idx
                    # An error message has been already printed through the first part of the code

                    # Check if it is an unstability case detected previously
                    for combination in direc_unstable_cases:
                        if combination[0] == alt and combination[
                                1] == mach and combination[2] == aoa:
                            dirrectionaly_stable = False

                    # If there is at list 2 values in find_idx :
                    if len(find_idx) > 1:
                        # Find all cml_list values for index corresonding to an altitude, a mach, an aos_list=0, and different aoa_list
                        cml = []
                        aos = []
                        for index in find_idx:
                            cml.append(-cml_list[index])
                            aos.append(aos_list[index])
                        aos, cml = order_correctly(
                            aos, cml)  # To order values with growing aos

                        # Save values which will be plot
                        plot_cml.append(cml)
                        plot_aos.append(aos)
                        curve_legend = 'Mach = ' + str(mach)
                        plot_legend.append(curve_legend)

                if plot_cml:
                    # Plot cml VS aos for constant Alt, aoa and different mach
                    plot_multicurve(plot_cml, plot_aos, plot_legend,
                                    plot_title, xlabel, ylabel, show_plots,
                                    save_plots)
            ############ MACH  PLOTS END ##########

    # TRIM CONDITIONS PLOTS
    # Plot trim_aoa VS mach for different alt
    # If there is at least 1 element in list of trim conditions then, plot them
    if trim_derivative_longi_list:
        log.info('graph : trim aoa vs mach genrated')
        plot_multicurve(trim_aoa_longi_list, trim_mach_longi_list,
                        trim_legend_longi_list, r'$\alpha_{trim}$ vs Mach',
                        'Mach', r'$\alpha_{trim}$ [°]', show_plots, save_plots)
        log.info('graph : pitch moment derivative at trim vs mach genrated')
        plot_multicurve(trim_derivative_longi_list, trim_mach_longi_list,
                        trim_legend_longi_list,
                        r'$C_{M_{\alpha trim}}$ vs Mach', 'Mach',
                        r'$C_{M_{\alpha trim}}$ [1/°]', show_plots, save_plots)

    if trim_derivative_lat_list:
        log.info('graph : roll moment derivative at trim vs mach genrated')
        plot_multicurve(trim_derivative_lat_list, trim_mach_lat_list,
                        trim_legend_lat_list, r'$C_{L_{\beta trim}}$vs Mach',
                        'Mach', r'$C_{L_{\beta trim}}$ [1/°]', show_plots,
                        save_plots)

    if trim_derivative_direc_list:
        log.info('graph : yaw moment at trim vs mach genrated')
        plot_multicurve(trim_derivative_direc_list, trim_mach_direc_list,
                        trim_legend_direc_list,
                        r'$C_{N_{\beta trim}}$ vs Mach', 'Mach',
                        r'$C_{N_{\beta trim}}$ [1/°]', show_plots, save_plots)

    # ALTITUDE PLOTS
    if plot_for_different_alt:  # To check Altitude Influence
        # plot cms VS aoa for constant mach, aos= 0 and different altitudes:
        # Find index of altitude which have the same value
        idx_aos = [i for i in range(len(aos_list)) if aos_list[i] == 0]
        for mach in mach_unic:
            # Find index of mach which have the same value
            idx_mach = [
                j for j in range(len(mach_list)) if mach_list[j] == mach
            ]
            # Prepare variables for plots
            plot_cms = []
            plot_aoa = []
            plot_legend = []
            plot_title = r'Pitch moment coefficient $C_M$ vs $\alpha$ @ Mach = ' + str(
                mach) + r' and $\beta$ = 0°'
            xlabel = r'$\alpha$ [°]'
            ylabel = r'$C_M$ [-]'

            longitudinaly_stable = True

            # Find index of slip angle which have the same value
            for alt in alt_unic:
                idx_alt = [
                    j for j in range(len(alt_list)) if alt_list[j] == alt
                ]
                find_idx = get_index(idx_aos, idx_mach, idx_alt)

                # If find_idx is empty an APM function would have corrected before
                # If there is only one value  in  find_idx for a given Alt, Mach, aos_list, no analyse can be performed
                # An error message has been already printed through the first part of the code

                # Check if it is an unstability case detected previously
                for combination in longi_unstable_cases:
                    if combination[0] == alt and combination[
                            1] == mach and combination[2] == aos:
                        longitudinaly_stable = False

                # If there is at list 2 values in find_idx :
                if len(find_idx) > 1:
                    # Find all cms_list values for index corresonding to an altitude, a mach, an aos_list=0, and different aoa_list
                    cms = []
                    aoa = []
                    for index in find_idx:
                        cms.append(cms_list[index])
                        aoa.append(aoa_list[index])

                    # Save values which will be plot
                    plot_cms.append(cms)
                    plot_aoa.append(aoa)
                    curve_legend = 'Altitude = ' + str(alt) + ' m'
                    plot_legend.append(curve_legend)

            if plot_cms:
                # PLot cms VS aoa for constant  Mach, aos and different Alt
                plot_multicurve(plot_cms, plot_aoa, plot_legend, plot_title,
                                xlabel, ylabel, show_plots, save_plots)

        ## Lateral
        # plot cmd VS aos for constant mach, aoa_list and different altitudes:
        for aoa in aoa_unic:
            # Find index of altitude which have the same value
            idx_aoa = [i for i in range(len(aoa_list)) if aoa_list[i] == aoa]
            for mach in mach_unic:
                # Find index of mach which have the same value
                idx_mach = [
                    j for j in range(len(mach_list)) if mach_list[j] == mach
                ]
                # Prepare variables for plots
                plot_cmd = []
                plot_aos = []
                plot_legend = []
                plot_title = r'Roll moment coefficient $C_L$ vs $\beta$ @ Mach = ' + str(
                    mach) + r' and $\alpha$= ' + str(aoa) + r' °'
                xlabel = r'$\beta$ [°]'
                ylabel = r'$C_L$ [-]'

                laterally_stable = True

                # Find index of slip angle which have the same value
                for alt in alt_unic:
                    idx_alt = [
                        j for j in range(len(alt_list)) if alt_list[j] == alt
                    ]
                    find_idx = get_index(idx_aoa, idx_mach, idx_alt)
                    # If find_idx is empty an APM function would have corrected before
                    # If there there is only one value  in  find_idx for a given Alt, Mach, aos_list, no analyse can be performed
                    # An error message has been already printed through the first part of the code

                    # Check if it is an unstability case detected previously
                    for combination in lat_unstable_cases:
                        if combination[0] == alt and combination[
                                1] == mach and combination[2] == aoa:
                            laterally_stable = False

                    # If there is at list 2 values in find_idx :
                    if len(find_idx) > 1:
                        # Find all cmd_list values for index corresonding to an altitude, a mach, an aos_list=0, and different aoa_list
                        cmd = []
                        aos = []
                        for index in find_idx:
                            cmd.append(-cmd_list[index])
                            aos.append(aos_list[index])

                        # Save values which will be plot
                        plot_cmd.append(cmd)
                        plot_aos.append(aos)
                        curve_legend = 'Altitude = ' + str(alt) + ' m'
                        plot_legend.append(curve_legend)

                if plot_cmd:
                    # PLot cmd VS aos for constant  Mach, aoa and different alt
                    plot_multicurve(plot_cmd, plot_aos, plot_legend,
                                    plot_title, xlabel, ylabel, show_plots,
                                    save_plots)

        ## DIRECTIONAL
        # plot cml VS aos for constant mach, aoa_list and different altitudes:
        for aoa in aoa_unic:
            # Find index of altitude which have the same value
            idx_aoa = [i for i in range(len(aoa_list)) if aoa_list[i] == aoa]
            for mach in mach_unic:
                # Find index of mach which have the same value
                idx_mach = [
                    j for j in range(len(mach_list)) if mach_list[j] == mach
                ]
                # Prepare variables for plots
                plot_cml = []
                plot_aos = []
                plot_legend = []
                plot_title = r'Yaw moment coefficient $C_N$ vs $\beta$ @ Mach = ' + str(
                    mach) + r' and $\alpha$= ' + str(aoa) + r' °'
                xlabel = r'$\beta$ [°]'
                ylabel = r'$C_N$ [-]'

                dirrectionaly_stable = True

                # Find index of slip angle which have the same value
                for alt in alt_unic:
                    idx_alt = [
                        j for j in range(len(alt_list)) if alt_list[j] == alt
                    ]
                    find_idx = get_index(idx_aoa, idx_mach, idx_alt)

                    # Check if it is an unstability case detected previously
                    for combination in direc_unstable_cases:
                        if combination[0] == alt and combination[
                                1] == mach and combination[2] == aoa:
                            dirrectionaly_stable = False

                    # If there is at list 2 values in find_idx :
                    if len(find_idx) > 1:
                        # Find all cml_list values for index corresonding to an altitude, a mach, an aos_list=0, and different aoa_list
                        cml = []
                        aos = []
                        for index in find_idx:
                            cml.append(-cml_list[index])
                            aos.append(aos_list[index])

                        # Save values which will be plot
                        plot_cml.append(cml)
                        plot_aos.append(aos)
                        curve_legend = 'Altitude = ' + str(alt) + ' m'
                        plot_legend.append(curve_legend)

                if plot_cml:
                    # PLot cml VS aos for constant  Mach, aoa and different alt
                    plot_multicurve(plot_cml, plot_aos, plot_legend,
                                    plot_title, xlabel, ylabel, show_plots,
                                    save_plots)

    # Save in the CPACS file stability results:
    trim_alt_longi_list = extract_subelements(trim_alt_longi_list)
    trim_mach_longi_list = extract_subelements(trim_mach_longi_list)
    trim_aoa_longi_list = extract_subelements(trim_aoa_longi_list)
    trim_aos_longi_list = extract_subelements(trim_aos_longi_list)
    trim_derivative_longi_list = extract_subelements(
        trim_derivative_longi_list)

    trim_alt_lat_list = extract_subelements(trim_alt_lat_list)
    trim_mach_lat_list = extract_subelements(trim_mach_lat_list)
    trim_aoa_lat_list = extract_subelements(trim_aoa_lat_list)
    trim_aos_lat_list = extract_subelements(trim_aos_lat_list)
    trim_derivative_lat_list = extract_subelements(trim_derivative_lat_list)

    trim_alt_direc_list = extract_subelements(trim_alt_direc_list)
    trim_mach_direc_list = extract_subelements(trim_mach_direc_list)
    trim_aoa_direc_list = extract_subelements(trim_aoa_direc_list)
    trim_aos_direc_list = extract_subelements(trim_aos_direc_list)
    trim_derivative_direc_list = extract_subelements(
        trim_derivative_direc_list)

    # xpath definition
    # TODO: add uid of the coresponding aeropm for results
    longi_xpath = STATIC_ANALYSIS_XPATH + '/results/longitudinalStaticStable'
    lat_xpath = STATIC_ANALYSIS_XPATH + '/results/lateralStaticStable'
    direc_xpath = STATIC_ANALYSIS_XPATH + '/results/directionnalStaticStable'
    longi_trim_xpath = STATIC_ANALYSIS_XPATH + '/trimConditions/longitudinal'
    lat_trim_xpath = STATIC_ANALYSIS_XPATH + '/trimConditions/lateral'
    direc_trim_xpath = STATIC_ANALYSIS_XPATH + '/trimConditions/directional'

    cpsf.create_branch(tixi, longi_xpath)
    cpsf.create_branch(tixi, lat_xpath)
    cpsf.create_branch(tixi, direc_xpath)

    # Store in the CPACS the stability results
    tixi.updateTextElement(longi_xpath, str(cpacs_stability_longi))
    tixi.updateTextElement(lat_xpath, str(cpacs_stability_lat))
    tixi.updateTextElement(direc_xpath, str(cpacs_stability_direc))

    cpsf.create_branch(tixi, longi_trim_xpath)
    cpsf.create_branch(tixi, lat_trim_xpath)
    cpsf.create_branch(tixi, direc_trim_xpath)

    # TODO: Normaly this "if" is not required, but the tixi function to add a vector does not support an empty vercor...
    if trim_alt_longi_list:
        cpsf.add_float_vector(tixi, longi_trim_xpath + '/altitude',
                              trim_alt_longi_list)
        cpsf.add_float_vector(tixi, longi_trim_xpath + '/machNumber',
                              trim_mach_longi_list)
        cpsf.add_float_vector(tixi, longi_trim_xpath + '/angleOfAttack',
                              trim_aoa_longi_list)
        cpsf.add_float_vector(tixi, longi_trim_xpath + '/angleOfSideslip',
                              trim_aos_longi_list)
    if trim_alt_lat_list:
        cpsf.add_float_vector(tixi, lat_trim_xpath + '/altitude',
                              trim_alt_lat_list)
        cpsf.add_float_vector(tixi, lat_trim_xpath + '/machNumber',
                              trim_mach_lat_list)
        cpsf.add_float_vector(tixi, lat_trim_xpath + '/angleOfAttack',
                              trim_aoa_lat_list)
        cpsf.add_float_vector(tixi, lat_trim_xpath + '/angleOfSideslip',
                              trim_aos_lat_list)
    if trim_alt_direc_list:
        cpsf.add_float_vector(tixi, direc_trim_xpath + '/altitude',
                              trim_alt_direc_list)
        cpsf.add_float_vector(tixi, direc_trim_xpath + '/machNumber',
                              trim_mach_direc_list)
        cpsf.add_float_vector(tixi, direc_trim_xpath + '/angleOfAttack',
                              trim_aoa_direc_list)
        cpsf.add_float_vector(tixi, direc_trim_xpath + '/angleOfSideslip',
                              trim_aos_direc_list)

    cpsf.close_tixi(tixi, cpacs_out_path)