예제 #1
0
    def compute(self, inputs, outputs):
        """Make a prediction"""
        xp = []
        for name in self.xd.index:
            xp.append(inputs[name][0])

        xp = np.array([xp])
        yp = self.Model.sm.predict_values(xp)

        for i, name in enumerate(self.yd.index):
            outputs[name] = yp[0][i]

        module = [m for m in Rt.modules if m.name == self.module_name][0]

        # Write the inouts to the CPACS
        tixi = open_tixi(str(module.cpacs_in))
        write_inouts(self.xd, xp, tixi)
        write_inouts(self.yd, yp, tixi)
        tixi.save(str(module.cpacs_out))
예제 #2
0
def main(cpacs_path, cpacs_out_path):

    log.info("----- Start of " + os.path.basename(__file__) + " -----")

    # Get number of proc to use
    tixi = open_tixi(cpacs_path)
    nb_proc = get_value_or_default(tixi, SU2_XPATH + "/settings/nbProc", 1)

    # Get results directory
    results_dir = get_results_directory("SU2Run")

    # Temporary CPACS to be stored after "generate_su2_cfd_config"
    cpacs_tmp_cfg = os.path.join(os.path.dirname(cpacs_out_path),
                                 "ConfigTMP.xml")

    # Execute SU2 functions
    generate_su2_cfd_config(cpacs_path, cpacs_tmp_cfg, results_dir)
    run_SU2_multi(results_dir, nb_proc)
    get_su2_results(cpacs_tmp_cfg, cpacs_out_path, results_dir)

    log.info("----- End of " + os.path.basename(__file__) + " -----")
예제 #3
0
    def setup(self):
        """Setup inputs and outputs"""

        module = [m for m in Rt.modules if m.name == self.module_name][0]

        # Take CPACS file from the optimisation
        cpacs_path = str(module.cpacs_in)
        tixi = open_tixi(cpacs_path)
        self.Model = load_surrogate(tixi)
        tixi.save(cpacs_path)

        df = self.Model.df
        df.set_index("Name", inplace=True)
        for name in df.index:
            if df.loc[name, "type"] == "obj":
                self.add_output(name)
            elif df.loc[name, "type"] == "des":
                self.add_input(name)

        self.xd = df.loc[[name for name in df.index if df.loc[name, "type"] == "des"]]
        self.yd = df.loc[[name for name in df.index if df.loc[name, "type"] == "obj"]]
예제 #4
0
def get_engine_inputs(ui, ed, cpacs_in):
    """ Function to extract from the xml file the required input data,
        the code will use the default value when they are missing.

        INPUT
        (class) ui     --Arg.: UserInputs class.
        (class) ed     --Arg.: EngineData class.
        ##======= Class ares defined in the InputClasses folder =======##
        (char) cpacs_in  --Arg.: Relative location of the xml file in the
                                 ToolInput folder (cpacs option) or
                                 relative location of the temp. xml file in
                                 the ToolOutput folder (input option).
        OUTPUT
        (class) ed       --Out.: EngineData class.
        (file) cpacs_in  --Out.: Updated cpasc file
    """

    log.info("Starting engine data extraction from CPACS file")

    tixi = open_tixi(cpacs_in)

    create_branch(tixi, PROP_XPATH, False)
    # Propulsion =============================================================
    if not tixi.checkElement(PROP_XPATH + "/turboprop"):
        create_branch(tixi, PROP_XPATH, False)
        tixi.createElement(PROP_XPATH, "turboprop")
        if ed.TURBOPROP:
            tixi.updateTextElement(PROP_XPATH + "/turboprop", "True")
        else:
            tixi.updateTextElement(PROP_XPATH + "/turboprop", "False")
    else:
        temp = tixi.getTextElement(PROP_XPATH + "/turboprop")
        if temp == "False":
            ed.TURBOPROP = False
        else:
            ed.TURBOPROP = True
    if not tixi.checkElement(PROP_XPATH + "/auxiliaryPowerUnit"):
        tixi.createElement(PROP_XPATH, "auxiliaryPowerUnit")
        if ed.APU:
            tixi.updateTextElement(PROP_XPATH + "/auxiliaryPowerUnit", "True")
        else:
            tixi.updateTextElement(PROP_XPATH + "/auxiliaryPowerUnit", "False")
    else:
        temp = tixi.getTextElement(PROP_XPATH + "/auxiliaryPowerUnit")
        if temp == "False":
            ed.APU = False
        else:
            ed.APU = True
    if not tixi.checkElement(PROP_XPATH + "/engineNumber"):
        tixi.createElement(PROP_XPATH, "engineNumber")
        tixi.updateIntegerElement(PROP_XPATH + "/engineNumber", ed.NE, "%i")
    else:
        ed.NE = tixi.getIntegerElement(PROP_XPATH + "/engineNumber")

    # Engines (TODO: check this _XPATH)
    mp = []
    tp = []
    EN_XPATH = "/cpacs/vehicles/engines"
    if tixi.checkElement(EN_XPATH):
        for e in range(0, ed.NE - 1):
            if ed.NE > 1:
                EN_XPATH += "/engine" + str(e + 1)
            else:
                EN_XPATH += "/engine"
            if not tixi.checkElement(EN_XPATH):
                raise Exception("Engine definition inclomplete, missing" +
                                " one or more engines in the cpacs file")
            if not tixi.checkElement(EN_XPATH + "/name"):
                ed.EN_NAME.append("Engine_" + str(e + 1))
                tixi.createElement(EN_XPATH, "name")
                tixi.updateTextElement(EN_XPATH + "/name", ed.EN_NAME[e])
            else:
                if e > len(ed.EN_NAME):
                    ed.EN_NAME.append(tixi.getTextElement(EN_XPATH + "/name"))
            ENA_XPATH = EN_XPATH + "/analysis/mass"
            if tixi.checkElement(ENA_XPATH + "/mass"):
                ed.en_mass = tixi.getDoubleElement(ENA_XPATH + "/mass")
                mp.append(ed.en_mass)
                if e > 0 and ed.en_mass != mp[e - 1]:
                    raise Exception("The engines have different masses, this" +
                                    " can lead to an unbalanced aircraft")
            elif ed.en_mass:
                tixi.createElement(ENA_XPATH, "mass")
                tixi.updateDoubleElement(ENA_XPATH + "/mass", ed.en_mass, "%g")
            else:
                raise Exception("Engine definition inclomplete, missing" +
                                " engine mass in the cpacs file")

            if tixi.checkElement(EN_XPATH + "/analysis/thrust00"):
                ed.max_thrust = tixi.getDoubleElement(EN_XPATH +
                                                      "/analysis/thrust00")
                tp.append(ed.max_thrust)
                if e > 0 and ed.max_thrust != tp[e - 1]:
                    raise Exception("The engines have different thrust, this")
                    # + ' can lead to an unbalanced flight')
            elif ed.max_thrust:
                tixi.createElement(EN_XPATH, "/analysisthrust00")
                tixi.updateDoubleElement(EN_XPATH + "/analysis/thrust00",
                                         ed.max_thrust, "%g")
            else:
                raise Exception("Engine definition inclomplete, missing" +
                                " engine thrust in the cpacs file")
    log.info("Data from CPACS file succesfully extracted")

    tixi.save(cpacs_in)

    return ed
예제 #5
0
def get_user_inputs(ed, ui, adui, cpacs_in):
    """Function to extract from the xml file the required input data,
        the code will use the default value when they are missing.

    Function 'get_user_inputs' ...

    Args:
        ed (int): EngineData class.
        ui (class): UserInputs class
        adui (str): AdvancedInputs class.
        cpacs_in (str): Path to the CPACS file

    Returns:
        ed (int): Updated ngineData class.
        ui (class): Updated UserInputs class
        adui (str): Updated AdvancedInputs class.

    """

    log.info("Starting data extraction from CPACS file")

    tixi = open_tixi(cpacs_in)

    create_branch(tixi, FUEL_XPATH, False)
    create_branch(tixi, GEOM_XPATH, False)
    create_branch(tixi, RANGE_XPATH, False)
    create_branch(tixi, PILOTS_XPATH, False)
    create_branch(tixi, CAB_CREW_XPATH, False)
    create_branch(tixi, PASS_XPATH, False)
    create_branch(tixi, ML_XPATH, False)
    create_branch(tixi, PROP_XPATH, False)

    # cpacs/vehicles
    MC_XPATH = MASSBREAKDOWN_XPATH + "/payload/mCargo/massDescription"

    create_branch(tixi, MC_XPATH, False)
    create_branch(tixi, F_XPATH, False)
    add_uid(tixi, F_XPATH, "kerosene")

    # Gathering data =========================================================
    # Geometry ===============================================================
    if not tixi.checkElement(GEOM_XPATH + "/description"):
        tixi.createElement(GEOM_XPATH, "description")
        tixi.updateTextElement(GEOM_XPATH + "/description",
                               "User " + "geometry input")

    ui.FLOORS_NB = get_value_or_default(tixi, GEOM_XPATH + "/floorsNb",
                                        ui.FLOORS_NB)
    adui.VRT_THICK = get_value_or_default(tixi, GEOM_XPATH + "/virtualThick",
                                          0.00014263)
    adui.VRT_STR_DENSITY = get_value_or_default(tixi,
                                                GEOM_XPATH + "/virtualDensity",
                                                2700.0)
    ui.H_LIM_CABIN = get_value_or_default(tixi, GEOM_XPATH + "/cabinHeight",
                                          2.3)

    # People =================================================================
    # Pilots user input data

    adui.PILOT_NB = get_value_or_default(tixi, PILOTS_XPATH + "/pilotNb", 2)
    adui.MASS_PILOT = get_value_or_default(tixi, PILOTS_XPATH + "/pilotMass",
                                           102.0)
    adui.MASS_CABIN_CREW = get_value_or_default(
        tixi, CAB_CREW_XPATH + "/cabinCrewMemberMass", 68.0)
    adui.MASS_PASS = get_value_or_default(tixi, PASS_XPATH + "/passMass",
                                          105.0)
    adui.PASS_BASE_DENSITY = get_value_or_default(tixi,
                                                  PASS_XPATH + "/passDensity",
                                                  1.66)
    adui.PASS_PER_TOILET = get_value_or_default(tixi,
                                                PASS_XPATH + "/passPerToilet",
                                                50)

    # what to to with this input
    if tixi.checkElement(PASS_XPATH + "/passNb"):
        temp = tixi.getIntegerElement(PASS_XPATH + "/passNb")
        if temp != ui.MAX_PASS and temp > 0:
            ui.MAX_PASS = temp

    # Fuel ===================================================================
    adui.FUEL_DENSITY = get_value_or_default(tixi, F_XPATH + "/density", 800)
    adui.RES_FUEL_PERC = get_value_or_default(tixi, F_XPATH + "/resFuelPerc",
                                              0.06)

    # Weight =================================================================
    # Mass limits data
    if not tixi.checkElement(ML_XPATH + "/description"):
        tixi.createElement(ML_XPATH, "description")
        tixi.updateTextElement(
            ML_XPATH + "/description",
            "Desired max fuel " + "volume [m^3] and payload mass [kg]")

    ui.MAX_PAYLOAD = get_value_or_default(tixi, ML_XPATH + "/maxPayload", 0.0)
    ui.MAX_FUEL_VOL = get_value_or_default(tixi, ML_XPATH + "/maxFuelVol", 0.0)
    ui.MASS_CARGO = get_value_or_default(tixi, MC_XPATH + "/massCargo", 0.0)
    # If the cargo mass is defined in the UserInputs class will be added
    # in the CPACS file after the analysis.

    # Flight =================================================================

    ed.TSFC_CRUISE = get_value_or_default(tixi, PROP_XPATH + "/tSFC", 0.5)

    # TODO: These data should be taken from aeroMaps...
    if not tixi.checkElement(RANGE_XPATH + "/lDRatio"):
        tixi.createElement(RANGE_XPATH, "lDRatio")
        tixi.updateDoubleElement(RANGE_XPATH + "/lDRatio", ui.LD, "%g")
    else:
        temp = tixi.getIntegerElement(RANGE_XPATH + "/lDRatio")
        if temp != ui.LD and temp > 0:
            ui.LD = temp

    if not tixi.checkElement(RANGE_XPATH + "/cruiseSpeed"):
        tixi.createElement(RANGE_XPATH, "cruiseSpeed")
        tixi.updateDoubleElement(RANGE_XPATH + "/cruiseSpeed", ui.CRUISE_SPEED,
                                 "%g")
    else:
        temp = tixi.getIntegerElement(RANGE_XPATH + "/cruiseSpeed")
        if temp != ui.CRUISE_SPEED and temp > 0:
            ui.CRUISE_SPEED = temp

    # TODO: see how to enter input for Engines
    if not tixi.checkElement(PROP_XPATH + "/userEngineOption"):
        tixi.createElement(PROP_XPATH, "userEngineOption")
        if ui.USER_ENGINES:
            tixi.updateTextElement(PROP_XPATH + "/userEngineOption", "True")
        else:
            tixi.updateTextElement(PROP_XPATH + "/userEngineOption", "False")
    else:
        temp = tixi.getTextElement(PROP_XPATH + "/userEngineOption")
        if temp == "False":
            ui.USER_ENGINES = False
        else:
            ui.USER_ENGINES = True

    if not tixi.checkElement(PROP_XPATH + "/singleHydraulics"):
        tixi.createElement(PROP_XPATH, "singleHydraulics")
        if adui.SINGLE_HYDRAULICS:
            tixi.updateTextElement(PROP_XPATH + "/singleHydraulics", "True")
        else:
            tixi.updateTextElement(PROP_XPATH + "/singleHydraulics", "False")
    else:
        temp = tixi.getTextElement(PROP_XPATH + "/singleHydraulics")
        if temp == "False":
            adui.SINGLE_HYDRAULICS = False
        else:
            adui.SINGLE_HYDRAULICS = True

    log.info("Data from CPACS file succesfully extracted")

    tixi.save(cpacs_in)

    return (ed, ui, adui)
예제 #6
0
def add_mesh_parameters(sumo_file_path, refine_level=0.0):
    """Function to add mesh parameter options in SUMO geometry (.smx file)

    Function 'add_mesh_parameters' is used to add meshing paramers in the SUMO
    geometry (.smx file) to get finer meshes. The only user input parameter is
    the refinement level which allows to generate finer meshes. 0 correspond
    to the default (close to values obtain with SUMO GUI). Then, increasing
    refinement level of 1 corespond to approximately two time more cells in
    the mesh. You can alos use float number (e.g. refine_level=2.4).

    Source :
        * sumo source code

    Args:
        sumo_file_path (str): Path to the SUMO geometry (.smx)
        cpacs_out_path (str): Path to the output CPACS file

    """

    refine_ratio = 0.6  # to get approx. double mesh cell when +1 on "refine_level"
    refine_factor = refine_ratio**refine_level
    log.info("Refinement factor is {}".format(refine_factor))

    # Open SUMO (.smx) with tixi library
    sumo = open_tixi(sumo_file_path)
    ROOT_XPATH = "/Assembly"

    # Get all Body (fuselage) and apply mesh parameters
    if sumo.checkElement(ROOT_XPATH):
        body_cnt = sumo.getNamedChildrenCount(ROOT_XPATH, "BodySkeleton")
        log.info(str(body_cnt) + " body has been found.")
    else:
        body_cnt = 0
        log.warning("No Fuselage has been found in this SUMO file!")

    for i_body in range(body_cnt):
        body_xpath = ROOT_XPATH + "/BodySkeleton[" + str(i_body + 1) + "]"

        circ_list = []
        min_radius = 10e6

        # Go throught every Boby frame (fuselage sections)
        frame_cnt = sumo.getNamedChildrenCount(body_xpath, "BodyFrame")
        for i_sec in range(frame_cnt):
            frame_xpath = body_xpath + "/BodyFrame[" + str(i_sec + 1) + "]"

            # Estimate circumference and add to the list
            height = sumo.getDoubleAttribute(frame_xpath, "height")
            width = sumo.getDoubleAttribute(frame_xpath, "width")
            circ = 2 * math.pi * math.sqrt((height**2 + width**2) / 2)
            circ_list.append(circ)

            # Get overall min radius (semi-minor axi for elipse)
            min_radius = min(min_radius, height, width)

        mean_circ = sum(circ_list) / len(circ_list)

        # Calculate mesh parameters from inputs and geometry
        maxlen = (0.08 * mean_circ) * refine_factor
        minlen = (
            min(0.1 * maxlen, min_radius / 4) * refine_factor
        )  # in SUMO, it is min_radius/2, but sometimes it leads to meshing errors

        # Add mesh parameters in the XML file (.smx)
        meshcrit_xpath = body_xpath + "/MeshCriterion"
        if not sumo.checkElement(meshcrit_xpath):
            sumo.addTextElement(body_xpath, "MeshCriterion", "")

        sumo.addTextAttribute(meshcrit_xpath, "defaults", "false")
        sumo.addTextAttribute(meshcrit_xpath, "maxlen", str(maxlen))
        sumo.addTextAttribute(meshcrit_xpath, "minlen", str(minlen))
        sumo.addTextAttribute(meshcrit_xpath, "maxphi", "30")
        sumo.addTextAttribute(meshcrit_xpath, "maxstretch", "6")
        sumo.addTextAttribute(meshcrit_xpath, "nvmax", "1073741824")
        sumo.addTextAttribute(meshcrit_xpath, "xcoarse", "false")

        # Chage fusage caps
        cap_cnt = sumo.getNamedChildrenCount(body_xpath, "Cap")

        for i_cap in range(cap_cnt):
            cap_xpath = body_xpath + "/Cap[1]"
            sumo.removeElement(cap_xpath)

        sumo.addTextElementAtIndex(body_xpath, "Cap", "", 1)
        cap1_xpath = body_xpath + "/Cap[1]"
        sumo.addTextAttribute(cap1_xpath, "height", "0")
        sumo.addTextAttribute(cap1_xpath, "shape", "LongCap")
        sumo.addTextAttribute(cap1_xpath, "side", "south")

        cap2_xpath = body_xpath + "/Cap[2]"
        sumo.addTextElementAtIndex(body_xpath, "Cap", "", 2)
        sumo.addTextAttribute(cap2_xpath, "height", "0")
        sumo.addTextAttribute(cap2_xpath, "shape", "LongCap")
        sumo.addTextAttribute(cap2_xpath, "side", "north")

    # Go through every Wing and apply mesh parameters
    if sumo.checkElement(ROOT_XPATH):
        wing_cnt = sumo.getNamedChildrenCount(ROOT_XPATH, "WingSkeleton")
        log.info(str(wing_cnt) + " wing(s) has been found.")
    else:
        wing_cnt = 0
        log.warning("No wing has been found in this CPACS file!")

    for i_wing in range(wing_cnt):
        wing_xpath = ROOT_XPATH + "/WingSkeleton[" + str(i_wing + 1) + "]"

        chord_list = []

        # Go throught every WingSection
        section_cnt = sumo.getNamedChildrenCount(wing_xpath, "WingSection")
        for i_sec in range(section_cnt):
            section_xpath = wing_xpath + "/WingSection[" + str(i_sec + 1) + "]"

            chord_length = sumo.getDoubleAttribute(section_xpath, "chord")
            chord_list.append(chord_length)

        # In SUMO refChord is calculated from Area and Span, but this is not
        # trivial to get those value for each wing from the .smx file
        ref_chord = sum(chord_list) / len(chord_list)

        # Calculate mesh parameter from inputs and geometry
        maxlen = (0.15 * ref_chord) * refine_factor
        minlen = (
            0.08 * maxlen
        ) * refine_factor  # in sumo it is 0.08*maxlen or 0.7*min leading edge radius...?

        if refine_level > 1:
            lerfactor = 1 / (2.0 + 0.5 * (refine_level - 1))
            terfactor = 1 / (2.0 + 0.5 * (refine_level - 1))
        else:
            # correspond to the default value in SUMO
            lerfactor = 1 / 2.0
            terfactor = 1 / 2.0

        # Add mesh parameters in the XML file (.smx)
        meshcrit_xpath = wing_xpath + "/WingCriterion"
        if not sumo.checkElement(meshcrit_xpath):
            sumo.addTextElement(wing_xpath, "WingCriterion", "")

        sumo.addTextAttribute(meshcrit_xpath, "defaults", "false")
        sumo.addTextAttribute(meshcrit_xpath, "maxlen", str(maxlen))
        sumo.addTextAttribute(meshcrit_xpath, "minlen", str(minlen))
        sumo.addTextAttribute(meshcrit_xpath, "lerfactor", str(lerfactor))
        sumo.addTextAttribute(meshcrit_xpath, "terfactor", str(terfactor))
        sumo.addTextAttribute(meshcrit_xpath, "maxphi", "30")
        sumo.addTextAttribute(meshcrit_xpath, "maxstretch", "6")
        sumo.addTextAttribute(meshcrit_xpath, "nvmax", "1073741824")
        sumo.addTextAttribute(meshcrit_xpath, "xcoarse", "false")

    sumo.save(sumo_file_path)
예제 #7
0
def wing_inertia(subd_c, SPACING, fuse, center_of_gravity, mass_seg_i, awg,
                 cpacs_in):
    """The function evaluates the inertia of the wings using the lumped
       masses method.

       INPUT
       (float) subd_c   --Arg.:  Number of subdivisions along the perimeter
                                 on each surface, total number of points for
                                 each section subd_c * 2
       (float) SPACING  --Arg.: Maximum distance between wing nodes along
                                the span [m].
       (float) fuse     --Arg.: Number of fuselages.
       (float_array) center_of_gravity --Arg.: x,y,z coordinates of the CoG.
       (float_array) mass_seg_i        --Arg.: Mass of each segment of each
                                               component of the aircraft.
       (class) awg      --Arg.: AircraftWingGeometry class.
        ##======= Class is defined in the InputClasses folder =======##
       (char) cpacs_in --Arg.: Cpacs xml file location.

       OUTPUT
       (float) swx --Out.: Lumped nodes x-coordinate [m].
       (float) swy --Out.: Lumped nodes y-coordinate [m].
       (float) swz --Out.: Lumped nodes z-coordinate [m].
       (float) Ixx --Out.: Moment of inertia respect to the x-axis [kgm^2].
       (float) Iyy --Out.: Moment of inertia respect to the y-axis [kgm^].
       (float) Izz --Out.: Moment of inertia respect to the z-axis [kgm^2].

    """
    tixi = open_tixi(cpacs_in)
    tigl = open_tigl(tixi)

    log.info("-------------------------------------------------------------")
    log.info("------ Evaluating wing nodes for lumped masses inertia ------")
    log.info("-------------------------------------------------------------")

    Ixx = 0
    Iyy = 0
    Izz = 0
    Ixy = 0
    Iyz = 0
    Ixz = 0
    swx = []
    swy = []
    swz = []
    a = 0
    for w in range(1, awg.w_nb + 1):
        DEN = 0.0
        for d in range(int(subd_c + 2)):
            DEN = DEN + d
        zeta = 1.0 / DEN
        for i in awg.w_seg_sec[:, w - 1, 2]:
            if i == 0.0:
                break
            wx = []
            wy = []
            wz = []
            # Number of subdivisions along the longitudinal axis
            subd_l = math.ceil(
                (awg.wing_seg_length[int(i) - 1][w + a - 1] / SPACING))
            if subd_l == 0:
                subd_l = 1
            eta = 1.0 / subd_l
            et = 0.0
            (xc, yc, zc) = awg.wing_center_seg_point[int(i) - 1][w + a - 1][:]
            for j in range(int(subd_l) + 1):
                et = j * eta
                (xle, yle, zle) = tigl.wingGetLowerPoint(w, int(i), et, 0.0)
                (xle2, yle2, zle2) = tigl.wingGetLowerPoint(w, int(i), et, 1.0)
                if xle < xle2:
                    ZLE = 0.0
                    ze = 0.0
                else:
                    ZLE = 1.0
                    ze = 1.0
                wx.extend((xle, xle2))
                wy.extend((yle, yle2))
                wz.extend((zle, zle2))
                swx.extend((xle, xle2))
                swy.extend((yle, yle2))
                swz.extend((zle, zle2))
                for k in range(int(subd_c) + 1):
                    if ZLE == 0.0:
                        ze += float(k) * zeta
                    elif ZLE == 1.0:
                        ze -= float(k) * zeta
                    (xl, yl, zl) = tigl.wingGetLowerPoint(w, int(i), et, ze)
                    (xu, yu, zu) = tigl.wingGetUpperPoint(w, int(i), et, ze)
                    wx.extend((xl, xu))
                    wy.extend((yl, yu))
                    wz.extend((zl, zu))
                    swx.extend((xl, xu))
                    swy.extend((yl, yu))
                    swz.extend((zl, zu))
            M = mass_seg_i[int(i) - 1, fuse + w + a - 1] / np.max(np.shape(wx))
            wcx = wx - (np.zeros((np.shape(wx))) + center_of_gravity[0])
            wcy = wy - (np.zeros((np.shape(wy))) + center_of_gravity[1])
            wcz = wz - (np.zeros((np.shape(wz))) + center_of_gravity[2])
            Ixx += np.sum(M * np.add(wcy**2, wcz**2))
            Iyy += np.sum(M * np.add(wcx**2, wcz**2))
            Izz += np.sum(M * np.add(wcx**2, wcy**2))
            Ixy += np.sum(M * wcx * wcy)
            Iyz += np.sum(M * wcy * wcz)
            Ixz += np.sum(M * wcx * wcz)
            if awg.wing_sym[int(w) - 1] != 0:
                if awg.wing_sym[int(w) - 1] == 1:  # x-y plane
                    symy = 1 + np.zeros(np.shape(wy))
                    symx = 1 + np.zeros(np.shape(wx))
                    symz = -1 + np.zeros(np.shape(wz))
                elif awg.wing_sym[int(w) - 1] == 2:  # x-z plane
                    symy = -1 + np.zeros(np.shape(wy))
                    symx = 1 + np.zeros(np.shape(wx))
                    symz = 1 + np.zeros(np.shape(wz))
                elif awg.wing_sym[int(w) - 1] == 3:  # y-z plane
                    symy = 1 + np.zeros(np.shape(wy))
                    symx = -1 + np.zeros(np.shape(wx))
                    symz = 1 + np.zeros(np.shape(wz))
                wx_t = wx * symx
                wy_t = wy * symy
                wz_t = wz * symz
                [swx.append(x) for x in wx_t]
                [swy.append(y) for y in wy_t]
                [swz.append(z) for z in wz_t]
                M = mass_seg_i[int(i) - 1, fuse + w + a - 1] / np.max(
                    np.shape(wx_t))
                wcx_t = wx_t - (np.zeros(
                    (np.shape(wx_t))) + center_of_gravity[0])
                wcy_t = wy_t - (np.zeros(
                    (np.shape(wy_t))) + center_of_gravity[1])
                wcz_t = wz_t - (np.zeros(
                    (np.shape(wz_t))) + center_of_gravity[2])
                Ixx += np.sum(M * np.add(wcy_t**2, wcz_t**2))
                Iyy += np.sum(M * np.add(wcx_t**2, wcz_t**2))
                Izz += np.sum(M * np.add(wcx_t**2, wcy_t**2))
                Ixy += np.sum(M * wcx_t * wcy_t)
                Iyz += np.sum(M * wcy_t * wcz_t)
                Ixz += np.sum(M * wcx_t * wcz_t)
        if awg.wing_sym[int(w) - 1] != 0:
            a += 1

    return (swx, swy, swz, Ixx, Iyy, Izz, Ixy, Iyz, Ixz)
예제 #8
0
def main(cpacs_in_path, cpacs_out_path):

    log.info("Running PyTornado...")

    # ===== Delete old working directories =====
    settings_from_CPACS = get_pytornado_settings_from_CPACS(cpacs_in_path)
    if settings_from_CPACS is not None:
        if settings_from_CPACS.get("deleteOldWKDIRs", False):
            wkdirs = glob(os.path.join(DIR_MODULE, "wkdir_*"))
            for wkdir in wkdirs:
                shutil.rmtree(wkdir, ignore_errors=True)

    # ===== Paths =====
    dir_pyt_wkdir = os.path.join(DIR_MODULE, "wkdir_temp")

    dir_pyt_aircraft = os.path.join(dir_pyt_wkdir, "aircraft")
    dir_pyt_settings = os.path.join(dir_pyt_wkdir, "settings")
    dir_pyt_results = os.path.join(dir_pyt_wkdir, "_results")
    file_pyt_aircraft = os.path.join(dir_pyt_aircraft, "ToolInput.xml")
    file_pyt_settings = os.path.join(dir_pyt_settings, "cpacs_run.json")

    # ===== Make directories =====
    Path(dir_pyt_wkdir).mkdir(parents=True, exist_ok=True)
    Path(dir_pyt_aircraft).mkdir(parents=True, exist_ok=True)
    Path(dir_pyt_settings).mkdir(parents=True, exist_ok=True)
    Path(dir_pyt_results).mkdir(parents=True, exist_ok=True)

    # ===== Setup =====
    shutil.copy(src=cpacs_in_path, dst=file_pyt_aircraft)
    mi.check_cpacs_input_requirements(cpacs_in_path)

    # ===== Get PyTornado settings =====
    cpacs_settings = get_pytornado_settings(cpacs_in_path)
    with open(file_pyt_settings, "w") as fp:
        dump_pretty_json(cpacs_settings, fp)

    # ===== PyTornado analysis =====
    pytornado = import_pytornado("pytornado.stdfun.run")
    # pytornado.standard_run(args=pytornado.StdRunArgs(run=file_pyt_settings, verbose=True))
    results = pytornado.standard_run(
        args=pytornado.StdRunArgs(run=file_pyt_settings, verbose=True))

    # ===== Extract load =====
    tixi = open_tixi(cpacs_in_path)
    extract_loads_xpath = "/cpacs/toolspecific/pytornado/save_results/extractLoads"
    extract_loads = get_value_or_default(tixi, extract_loads_xpath, False)

    if extract_loads:
        _get_load_fields(results, dir_pyt_results)

    # ===== Clean up =====
    shutil.copy(src=file_pyt_aircraft, dst=cpacs_out_path)

    # ===== Copy files in the wkflow results directory =====
    # TODO: use dirs_exist_ok=True option when  python >=3.8 and remove "tmp"
    dst_pyt_wkdir = Path(get_results_directory("PyTornado"), "tmp")
    if os.path.isdir(dst_pyt_wkdir):
        shutil.rmtree(dst_pyt_wkdir)
    shutil.copytree(src=dir_pyt_wkdir, dst=dst_pyt_wkdir)
    shutil.rmtree(dir_pyt_wkdir, ignore_errors=True)

    log.info("PyTornado analysis completed")
예제 #9
0
def get_data(mw, ri, cpacs_in):
    """ The function extracts from the xml file the required input data,
        the code will use the default value when they are missing.

        INPUT
        (class) mw       --Arg.: MassesWeight class updated
        (class) ri       --Arg.: RangeInput class updated
        ##======= Classes are defined in the Input_classes folder =======##

        (char) opt       --Arg.: Cpacs or input option
        (char) cpacs_in  --Arg.: Relative location of the xml file in the
                                 ToolInput folder (cpacs option) or
                                 relative location of the temp. xml file in
                                 the ToolOutput folder (input option).

        OUTPUT
        (class) mw       --Out.: MassesWeight class updated.
        (class) ri       --Out.: RangeInput class updated.
        (file) cpacs_in  --Out.: Updated cpasc file.
    """
    log.info("CPACS file path check")

    # path definition ========================================================
    # Opening CPACS file
    tixi = open_tixi(cpacs_in)

    TSPEC_PATH = "/cpacs/toolspecific/CEASIOMpy"
    W_PATH = TSPEC_PATH + "/weight"
    C_PATH = W_PATH + "/crew"
    P_PATH = C_PATH + "/pilots"
    CC_PATH = C_PATH + "/cabinCrewMembers"
    PASS_PATH = W_PATH + "/passengers"
    FMP_PATH = PASS_PATH + "/fuelMassMaxpass/mass"
    PROP_PATH = TSPEC_PATH + "/propulsion"
    RANGE_PATH = TSPEC_PATH + "/ranges"

    MASS_PATH = "/cpacs/vehicles/aircraft/model/analyses/massBreakdown"
    DM_PATH = MASS_PATH + "/designMasses"
    MTOM_PATH = DM_PATH + "/mTOM/mass"
    F_PATH = MASS_PATH + "/fuel/massDescription/mass"
    OEM_PATH = MASS_PATH + "/mOEM/massDescription/mass"
    PAY_PATH = MASS_PATH + "/payload/massDescription/mass"

    F1_PATH = "/cpacs/vehicles/fuels/fuel"
    F2_PATH = TSPEC_PATH + "/fuels"

    TSFC_PATH = PROP_PATH + "/tSFC"
    create_branch(tixi, TSFC_PATH, False)
    create_branch(tixi, RANGE_PATH, False)
    create_branch(tixi, P_PATH, False)
    create_branch(tixi, F1_PATH, False)
    create_branch(tixi, F2_PATH, False)
    add_uid(tixi, F1_PATH, "kerosene")

    # Compulsory path checks =================================================

    if not tixi.checkElement(TSPEC_PATH):
        raise Exception("Missing required toolspecific path.")
    elif not tixi.checkElement(CC_PATH + "/cabinCrewMemberNb"):
        raise Exception("Missing required cabinCrewMemberNb path.")
    elif not tixi.checkElement(MASS_PATH):
        raise Exception("Missing required massBreakdown path.")
    elif not tixi.checkElement(DM_PATH):
        raise Exception("Missing required designMasses path.")
    elif not tixi.checkElement(MTOM_PATH):
        raise Exception("Missing required mTOM/mass path.")
    elif not tixi.checkElement(F_PATH):
        raise Exception("Missing required fuel/massDescription/mass path.")
    elif not tixi.checkElement(FMP_PATH):
        raise Exception("Missing required fuelMassMaxpass/mass path.")
    elif not tixi.checkElement(OEM_PATH):
        raise Exception("Missing required mOEM/massDescription/mass path.")
    elif not tixi.checkElement(PAY_PATH):
        raise Exception("Missing required payload/massDescription/mass path.")
    else:
        log.info("All path correctly defined in the toolinput.xml file, " +
                 "beginning data extracction.")

    # Gathering data =========================================================
    #  TOOLSPECIFIC ----------------------------------------------------------
    if not tixi.checkElement(RANGE_PATH + "/lDRatio"):
        tixi.createElement(RANGE_PATH, "lDRatio")
        tixi.updateDoubleElement(RANGE_PATH + "/lDRatio", ri.LD, "%g")
    else:
        temp = tixi.getIntegerElement(RANGE_PATH + "/lDRatio")
        if temp != ri.LD and temp > 0:
            ri.LD = temp

    if not tixi.checkElement(RANGE_PATH + "/cruiseSpeed"):
        tixi.createElement(RANGE_PATH, "cruiseSpeed")
        tixi.updateDoubleElement(RANGE_PATH + "/cruiseSpeed", ri.CRUISE_SPEED,
                                 "%g")
    else:
        temp = tixi.getIntegerElement(RANGE_PATH + "/cruiseSpeed")
        if temp != ri.CRUISE_SPEED and temp > 0:
            ri.CRUISE_SPEED = temp

    if not tixi.checkElement(RANGE_PATH + "/loiterTime"):
        tixi.createElement(RANGE_PATH, "loiterTime")
        tixi.updateDoubleElement(RANGE_PATH + "/loiterTime", ri.LOITER_TIME,
                                 "%g")
    else:
        temp = tixi.getIntegerElement(RANGE_PATH + "/loiterTime")
        if temp != ri.LOITER_TIME and temp > 0:
            ri.LOITER_TIME = temp

    if not tixi.checkElement(TSPEC_PATH + "/geometry/winglet"):
        tixi.createElement(TSPEC_PATH + "/geometry", "winglet")
        tixi.updateIntegerElement(TSPEC_PATH + "/geometry/winglet", ri.WINGLET,
                                  "%i")
    else:
        temp = tixi.getIntegerElement(TSPEC_PATH + "/geometry/winglet")
        if temp != ri.WINGLET:
            ri.WINGLET = temp

    if not tixi.checkElement(P_PATH + "/pilotNb"):
        tixi.createElement(P_PATH, "pilotNb")
        tixi.updateIntegerElement(P_PATH + "/pilotNb", ri.pilot_nb, "%i")
    else:
        temp = tixi.getIntegerElement(P_PATH + "/pilotNb")
        if temp != ri.pilot_nb and temp > 0:
            ri.pilot_nb = temp

    # Pilots user input data
    if not tixi.checkElement(P_PATH + "/pilotMass"):
        tixi.createElement(P_PATH, "pilotMass")
        tixi.updateDoubleElement(P_PATH + "/pilotMass", ri.MASS_PILOT, "%g")
    else:
        temp = tixi.getDoubleElement(P_PATH + "/pilotMass")
        if temp != ri.MASS_PILOT and temp > 0:
            ri.MASS_PILOT = temp

    # Cabin crew user input data
    if not tixi.checkElement(CC_PATH + "/cabinCrewMemberMass"):
        tixi.createElement(CC_PATH, "cabinCrewMemberMass")
        tixi.updateDoubleElement(CC_PATH + "/cabinCrewMemberMass",
                                 ri.MASS_CABIN_CREW, "%g")
    else:
        temp = tixi.getDoubleElement(CC_PATH + "/cabinCrewMemberMass")
        if temp != ri.MASS_CABIN_CREW and temp > 0:
            ri.MASS_CABIN_CREW = temp

    # Passengers input
    if not tixi.checkElement(PASS_PATH + "/passMass"):
        tixi.createElement(PASS_PATH, "passMass")
        tixi.updateDoubleElement(PASS_PATH + "/passMass", ri.MASS_PASS, "%g")
    else:
        temp = tixi.getDoubleElement(PASS_PATH + "/passMass")
        if temp != ri.MASS_PASS and temp > 0:
            ri.MASS_PASS = temp

    # Propulsion and Fuel

    if not tixi.checkElement(PROP_PATH + "/turboprop"):
        create_branch(tixi, PROP_PATH, False)
        tixi.createElement(PROP_PATH, "turboprop")
        if ri.TURBOPROP:
            tixi.updateTextElement(PROP_PATH + "/turboprop", "True")
        else:
            tixi.updateTextElement(PROP_PATH + "/turboprop", "False")
    else:
        temp = tixi.getTextElement(PROP_PATH + "/turboprop")
        if temp == "False":
            ri.TURBOPROP = False
        else:
            ri.TURBOPROP = True

    if not tixi.checkElement(F2_PATH + "/resFuelPerc"):
        tixi.createElement(F2_PATH, "resFuelPerc")
        tixi.updateDoubleElement(F2_PATH + "/resFuelPerc", ri.RES_FUEL_PERC,
                                 "%g")
    else:
        temp = tixi.getDoubleElement(F2_PATH + "/resFuelPerc")
        if temp != ri.RES_FUEL_PERC and temp > 0:
            ri.RES_FUEL_PERC = temp

    if not tixi.checkElement(TSFC_PATH + "/tsfcCruise"):
        tixi.createElement(TSFC_PATH, "tsfcCruise")
        tixi.updateDoubleElement(TSFC_PATH + "/tsfcCruise", ri.TSFC_CRUISE,
                                 "%g")
    else:
        temp = tixi.getDoubleElement(TSFC_PATH + "/tsfcCruise")
        if temp != ri.TSFC_CRUISE and temp > 0:
            ri.TSFC_CRUISE = temp

    if not tixi.checkElement(TSFC_PATH + "/tsfcLoiter"):
        tixi.createElement(TSFC_PATH, "tsfcLoiter")
        tixi.updateDoubleElement(TSFC_PATH + "/tsfcLoiter", ri.TSFC_LOITER,
                                 "%g")
    else:
        temp = tixi.getDoubleElement(TSFC_PATH + "/tsfcLoiter")
        if temp != ri.TSFC_LOITER and temp > 0:
            ri.TSFC_LOITER = temp

    #  REQUIRED DATA =========================================================
    # Cabin Crew
    ri.cabin_crew_nb = tixi.getIntegerElement(CC_PATH + "/cabinCrewMemberNb")

    # Fuel
    mw.mass_fuel_maxpass = tixi.getDoubleElement(FMP_PATH)

    #  REQUIRED MASSBREAKDOWN DATA ===========================================
    mw.maximum_take_off_mass = tixi.getDoubleElement(MTOM_PATH)
    mw.operating_empty_mass = tixi.getDoubleElement(OEM_PATH)
    mw.mass_payload = tixi.getDoubleElement(PAY_PATH)
    mw.mass_fuel_max = tixi.getDoubleElement(F_PATH)

    log.info("Data from CPACS file succesfully extracted")

    # Saving and closing the cpacs file ======================================
    tixi.save(cpacs_in)

    return (mw, ri)
예제 #10
0
def check_cpacs_input_requirements(cpacs_file,
                                   *,
                                   submod_name=None,
                                   submodule_level=1,
                                   cpacs_inout=None):
    """Check if the input CPACS file contains the required nodes

    Note:
        * The __specs__ file will be located based on the calling module
        * In most cases this function should be called simply as

        ==> check_cpacs_input_requirements(cpacs_file)

    Args:
        cpacs_file (str): Path to the CPACS file to check
        submod_name (str): Name of the submod_name (if None, determined from caller)
        submodule_level (int): Levels up where the CEASIOMpy submodule is located
        cpacs_inout (obj): CPACSInOut() instance

    Raises:
        CPACSRequirementError: If one or more paths are required by calling
                               module but not available in CPACS file
    """

    # log = get_logger(module_file_name.split('.')[0])

    if not isinstance(submodule_level, int) and submodule_level < 1:
        ValueError("'submodule_level' must be a positive integer")

    # If 'cpacs_inout' not provided by caller, we try to determine it
    if cpacs_inout is None:
        if submod_name is None:
            # Get the path of the caller submodule
            frm = inspect.stack()[1]
            mod = inspect.getmodule(frm[0])
            caller_module_path = os.path.dirname(os.path.abspath(mod.__file__))

            # Get the CEASIOM_XPATH submodule name
            parent_path, submod_name = os.path.split(caller_module_path)
            for _ in range(1, submodule_level):
                parent_path, submod_name = os.path.split(parent_path)

        # Load the submodule specifications
        specs_module = get_specs_for_module(submod_name, raise_error=True)
        cpacs_inout = specs_module.cpacs_inout

    tixi = open_tixi(cpacs_file)
    missing_nodes = []
    for entry in cpacs_inout.inputs:

        if entry.default_value is not None:
            continue
        if tixi.checkElement(entry.xpath) is False:
            missing_nodes.append(entry.xpath)

    if missing_nodes:
        missing_str = ""
        for missing in missing_nodes:
            missing_str += "==> " + missing + "\n"

        msg = f"CPACS path required but does not exist\n{missing_str}"
        # log.error(msg)
        raise CPACSRequirementError(msg)
예제 #11
0
def convert_cpacs_to_sumo(cpacs_path, cpacs_out_path):
    """Function to convert a CPACS file geometry into a SUMO file geometry.

    Function 'convert_cpacs_to_sumo' open an input cpacs file with TIXI handle
    and via two main loop, one for fuselage(s), one for wing(s) it convert
    every element (as much as possible) in the SUMO (.smx) format, which is
    also an xml file. Due to some differences between both format, some CPACS
    definition could lead to issues. The output sumo file is saved in the
    folder /ToolOutput

    Source:
        * CPACS documentation: https://www.cpacs.de/pages/documentation.html

    Args:
        cpacs_path (str): Path to the CPACS file

    Returns:
        sumo_output_path (str): Path to the SUMO file

    """

    EMPTY_SMX = MODULE_DIR + "/files/sumo_empty.smx"

    tixi = open_tixi(cpacs_path)
    sumo = open_tixi(EMPTY_SMX)

    # Fuslage(s) ---------------------------------------------------------------

    if tixi.checkElement(FUSELAGES_XPATH):
        fus_cnt = tixi.getNamedChildrenCount(FUSELAGES_XPATH, "fuselage")
        log.info(str(fus_cnt) + " fuselage has been found.")
    else:
        fus_cnt = 0
        log.warning("No fuselage has been found in this CPACS file!")

    for i_fus in range(fus_cnt):
        fus_xpath = FUSELAGES_XPATH + "/fuselage[" + str(i_fus + 1) + "]"
        fus_uid = tixi.getTextAttribute(fus_xpath, "uID")
        fus_transf = Transformation()
        fus_transf.get_cpacs_transf(tixi, fus_xpath)

        # Create new body (SUMO)
        sumo.createElementAtIndex("/Assembly", "BodySkeleton", i_fus + 1)
        body_xpath = "/Assembly/BodySkeleton[" + str(i_fus + 1) + "]"

        sumo.addTextAttribute(body_xpath, "akimatg", "false")
        sumo.addTextAttribute(body_xpath, "name", fus_uid)

        body_tansf = Transformation()
        body_tansf.translation = fus_transf.translation

        # Convert angles
        body_tansf.rotation = euler2fix(fus_transf.rotation)

        # Add body rotation
        body_rot_str = (str(math.radians(body_tansf.rotation.x)) + " " +
                        str(math.radians(body_tansf.rotation.y)) + " " +
                        str(math.radians(body_tansf.rotation.z)))
        sumo.addTextAttribute(body_xpath, "rotation", body_rot_str)

        # Add body origin
        body_ori_str = (str(body_tansf.translation.x) + " " +
                        str(body_tansf.translation.y) + " " +
                        str(body_tansf.translation.z))
        sumo.addTextAttribute(body_xpath, "origin", body_ori_str)

        # Positionings
        if tixi.checkElement(fus_xpath + "/positionings"):
            pos_cnt = tixi.getNamedChildrenCount(fus_xpath + "/positionings",
                                                 "positioning")
            log.info(str(fus_cnt) + ' "Positionning" has been found : ')

            pos_x_list = []
            pos_y_list = []
            pos_z_list = []
            from_sec_list = []
            to_sec_list = []

            for i_pos in range(pos_cnt):
                pos_xpath = fus_xpath + "/positionings/positioning[" + str(
                    i_pos + 1) + "]"

                length = tixi.getDoubleElement(pos_xpath + "/length")
                sweep_deg = tixi.getDoubleElement(pos_xpath + "/sweepAngle")
                sweep = math.radians(sweep_deg)
                dihedral_deg = tixi.getDoubleElement(pos_xpath +
                                                     "/dihedralAngle")
                dihedral = math.radians(dihedral_deg)

                # Get the corresponding translation of each positionning
                pos_x_list.append(length * math.sin(sweep))
                pos_y_list.append(length * math.cos(dihedral) *
                                  math.cos(sweep))
                pos_z_list.append(length * math.sin(dihedral) *
                                  math.cos(sweep))

                # Get which section are connected by the positionning
                if tixi.checkElement(pos_xpath + "/fromSectionUID"):
                    from_sec = tixi.getTextElement(pos_xpath +
                                                   "/fromSectionUID")
                else:
                    from_sec = ""
                from_sec_list.append(from_sec)

                if tixi.checkElement(pos_xpath + "/toSectionUID"):
                    to_sec = tixi.getTextElement(pos_xpath + "/toSectionUID")
                else:
                    to_sec = ""
                to_sec_list.append(to_sec)

            # Re-loop though the positionning to re-order them
            for j_pos in range(pos_cnt):
                if from_sec_list[j_pos] == "":
                    prev_pos_x = 0
                    prev_pos_y = 0
                    prev_pos_z = 0

                elif from_sec_list[j_pos] == to_sec_list[j_pos - 1]:
                    prev_pos_x = pos_x_list[j_pos - 1]
                    prev_pos_y = pos_y_list[j_pos - 1]
                    prev_pos_z = pos_z_list[j_pos - 1]

                else:
                    index_prev = to_sec_list.index(from_sec_list[j_pos])
                    prev_pos_x = pos_x_list[index_prev]
                    prev_pos_y = pos_y_list[index_prev]
                    prev_pos_z = pos_z_list[index_prev]

                pos_x_list[j_pos] += prev_pos_x
                pos_y_list[j_pos] += prev_pos_y
                pos_z_list[j_pos] += prev_pos_z

        else:
            log.warning('No "positionings" have been found!')
            pos_cnt = 0

        # Sections
        sec_cnt = tixi.getNamedChildrenCount(fus_xpath + "/sections",
                                             "section")
        log.info("    -" + str(sec_cnt) + " fuselage sections have been found")

        if pos_cnt == 0:
            pos_x_list = [0.0] * sec_cnt
            pos_y_list = [0.0] * sec_cnt
            pos_z_list = [0.0] * sec_cnt

        for i_sec in range(sec_cnt):
            sec_xpath = fus_xpath + "/sections/section[" + str(i_sec + 1) + "]"
            sec_uid = tixi.getTextAttribute(sec_xpath, "uID")

            sec_transf = Transformation()
            sec_transf.get_cpacs_transf(tixi, sec_xpath)

            if sec_transf.rotation.x or sec_transf.rotation.y or sec_transf.rotation.z:

                log.warning('Sections "' + sec_uid + '" is rotated, it is \
                            not possible to take that into acount in SUMO !')

            # Elements
            elem_cnt = tixi.getNamedChildrenCount(sec_xpath + "/elements",
                                                  "element")

            if elem_cnt > 1:
                log.warning("Sections " + sec_uid + "  contains multiple \
                             element, it could be an issue for the conversion \
                             to SUMO!")

            for i_elem in range(elem_cnt):
                elem_xpath = sec_xpath + "/elements/element[" + str(i_elem +
                                                                    1) + "]"
                elem_uid = tixi.getTextAttribute(elem_xpath, "uID")

                elem_transf = Transformation()
                elem_transf.get_cpacs_transf(tixi, elem_xpath)

                if elem_transf.rotation.x or elem_transf.rotation.y or elem_transf.rotation.z:
                    log.warning('Element "' + elem_uid + '" is rotated, it \
                                 is not possible to take that into acount in \
                                 SUMO !')

                # Fuselage profiles
                prof_uid = tixi.getTextElement(elem_xpath + "/profileUID")
                prof_vect_x, prof_vect_y, prof_vect_z = get_profile_coord(
                    tixi, prof_uid)

                prof_size_y = (max(prof_vect_y) - min(prof_vect_y)) / 2
                prof_size_z = (max(prof_vect_z) - min(prof_vect_z)) / 2

                prof_vect_y[:] = [y / prof_size_y for y in prof_vect_y]
                prof_vect_z[:] = [z / prof_size_z for z in prof_vect_z]

                prof_min_y = min(prof_vect_y)
                prof_min_z = min(prof_vect_z)

                prof_vect_y[:] = [y - 1 - prof_min_y for y in prof_vect_y]
                prof_vect_z[:] = [z - 1 - prof_min_z for z in prof_vect_z]

                # Could be a problem if they are less positionings than secions
                # TODO: solve that!
                pos_y_list[i_sec] += (
                    (1 + prof_min_y) * prof_size_y) * elem_transf.scaling.y
                pos_z_list[i_sec] += (
                    (1 + prof_min_z) * prof_size_z) * elem_transf.scaling.z

                # #To Plot a particular section
                # if i_sec==5:
                #     plt.plot(prof_vect_z, prof_vect_y,'x')
                #     plt.xlabel('y')
                #     plt.ylabel('z')
                #     plt.grid(True)
                #     plt.show

                # Put value in SUMO format
                body_frm_center_x = (elem_transf.translation.x +
                                     sec_transf.translation.x +
                                     pos_x_list[i_sec]) * fus_transf.scaling.x
                body_frm_center_y = (
                    elem_transf.translation.y * sec_transf.scaling.y +
                    sec_transf.translation.y +
                    pos_y_list[i_sec]) * fus_transf.scaling.y
                body_frm_center_z = (
                    elem_transf.translation.z * sec_transf.scaling.z +
                    sec_transf.translation.z +
                    pos_z_list[i_sec]) * fus_transf.scaling.z

                body_frm_height = (prof_size_z * 2 * elem_transf.scaling.z *
                                   sec_transf.scaling.z * fus_transf.scaling.z)

                if body_frm_height < 0.01:
                    body_frm_height = 0.01
                body_frm_width = (prof_size_y * 2 * elem_transf.scaling.y *
                                  sec_transf.scaling.y * fus_transf.scaling.y)
                if body_frm_width < 0.01:
                    body_frm_width = 0.01

                # Convert the profile points in the SMX format
                prof_str = ""
                teta_list = []
                teta_half = []
                prof_vect_y_half = []
                prof_vect_z_half = []
                check_max = 0
                check_min = 0

                # Use polar angle to keep point in the correct order
                for i, item in enumerate(prof_vect_y):
                    teta_list.append(math.atan2(prof_vect_z[i],
                                                prof_vect_y[i]))

                for t, teta in enumerate(teta_list):
                    HALF_PI = math.pi / 2
                    EPSILON = 0.04

                    if abs(teta) <= HALF_PI - EPSILON:
                        teta_half.append(teta)
                        prof_vect_y_half.append(prof_vect_y[t])
                        prof_vect_z_half.append(prof_vect_z[t])
                    elif abs(teta) < HALF_PI + EPSILON:
                        # Check if not the last element of the list
                        if not t == len(teta_list) - 1:
                            next_val = prof_vect_z[t + 1]
                            # Check if it is better to keep next point
                            if not abs(next_val) > abs(prof_vect_z[t]):
                                if prof_vect_z[t] > 0 and not check_max:
                                    teta_half.append(teta)
                                    # Force y=0, to get symmetrical profile
                                    prof_vect_y_half.append(0)
                                    prof_vect_z_half.append(prof_vect_z[t])
                                    check_max = 1
                                elif prof_vect_z[t] < 0 and not check_min:
                                    teta_half.append(teta)
                                    # Force y=0, to get symmetrical profile
                                    prof_vect_y_half.append(0)
                                    prof_vect_z_half.append(prof_vect_z[t])
                                    check_min = 1

                # Sort points by teta value, to fit the SUMO profile format
                teta_half, prof_vect_z_half, prof_vect_y_half = (
                    list(t) for t in zip(*sorted(
                        zip(teta_half, prof_vect_z_half, prof_vect_y_half))))

                # Write profile as a string and add y=0 point at the begining
                # and at the end to ensure symmmetry
                if not check_min:
                    prof_str += str(0) + " " + str(prof_vect_z_half[0]) + " "
                for i, item in enumerate(prof_vect_z_half):
                    prof_str += (str(round(prof_vect_y_half[i], 4)) + " " +
                                 str(round(prof_vect_z_half[i], 4)) + " ")
                if not check_max:
                    prof_str += str(0) + " " + str(prof_vect_z_half[i]) + " "

                # Write the SUMO file
                sumo.addTextElementAtIndex(body_xpath, "BodyFrame", prof_str,
                                           i_sec + 1)
                frame_xpath = body_xpath + "/BodyFrame[" + str(i_sec + 1) + "]"

                body_center_str = (str(body_frm_center_x) + " " +
                                   str(body_frm_center_y) + " " +
                                   str(body_frm_center_z))

                sumo.addTextAttribute(frame_xpath, "center", body_center_str)
                sumo.addTextAttribute(frame_xpath, "height",
                                      str(body_frm_height))
                sumo.addTextAttribute(frame_xpath, "width",
                                      str(body_frm_width))
                sumo.addTextAttribute(frame_xpath, "name", sec_uid)

        # Fuselage symmetry (mirror copy)
        if tixi.checkAttribute(fus_xpath, "symmetry"):
            if tixi.getTextAttribute(fus_xpath, "symmetry") == "x-z-plane":
                sumo_mirror_copy(sumo, body_xpath, fus_uid, False)

    # To remove the default BodySkeleton
    if fus_cnt == 0:
        sumo.removeElement("/Assembly/BodySkeleton")
    else:
        sumo.removeElement("/Assembly/BodySkeleton[" + str(fus_cnt + 1) + "]")

    # Wing(s) ------------------------------------------------------------------

    if tixi.checkElement(WINGS_XPATH):
        wing_cnt = tixi.getNamedChildrenCount(WINGS_XPATH, "wing")
        log.info(str(wing_cnt) + " wings has been found.")
    else:
        wing_cnt = 0
        log.warning("No wings has been found in this CPACS file!")

    for i_wing in range(wing_cnt):
        wing_xpath = WINGS_XPATH + "/wing[" + str(i_wing + 1) + "]"
        wing_uid = tixi.getTextAttribute(wing_xpath, "uID")
        wing_transf = Transformation()
        wing_transf.get_cpacs_transf(tixi, wing_xpath)

        # Create new wing (SUMO)
        sumo.createElementAtIndex("/Assembly", "WingSkeleton", i_wing + 1)
        wg_sk_xpath = "/Assembly/WingSkeleton[" + str(i_wing + 1) + "]"

        sumo.addTextAttribute(wg_sk_xpath, "akimatg", "false")
        sumo.addTextAttribute(wg_sk_xpath, "name", wing_uid)

        # Create a class for the transformation of the WingSkeleton
        wg_sk_tansf = Transformation()

        # Convert WingSkeleton rotation and add it to SUMO
        wg_sk_tansf.rotation = euler2fix(wing_transf.rotation)
        wg_sk_rot_str = (str(math.radians(wg_sk_tansf.rotation.x)) + " " +
                         str(math.radians(wg_sk_tansf.rotation.y)) + " " +
                         str(math.radians(wg_sk_tansf.rotation.z)))
        sumo.addTextAttribute(wg_sk_xpath, "rotation", wg_sk_rot_str)

        # Add WingSkeleton origin
        wg_sk_tansf.translation = wing_transf.translation
        wg_sk_ori_str = (str(wg_sk_tansf.translation.x) + " " +
                         str(wg_sk_tansf.translation.y) + " " +
                         str(wg_sk_tansf.translation.z))
        sumo.addTextAttribute(wg_sk_xpath, "origin", wg_sk_ori_str)

        if tixi.checkAttribute(wing_xpath, "symmetry"):
            if tixi.getTextAttribute(wing_xpath, "symmetry") == "x-z-plane":
                sumo.addTextAttribute(wg_sk_xpath, "flags",
                                      "autosym,detectwinglet")
            else:
                sumo.addTextAttribute(wg_sk_xpath, "flags", "detectwinglet")

        # Positionings
        if tixi.checkElement(wing_xpath + "/positionings"):
            pos_cnt = tixi.getNamedChildrenCount(wing_xpath + "/positionings",
                                                 "positioning")
            log.info(str(wing_cnt) + ' "positionning" has been found : ')

            pos_x_list = []
            pos_y_list = []
            pos_z_list = []
            from_sec_list = []
            to_sec_list = []

            for i_pos in range(pos_cnt):
                pos_xpath = wing_xpath + "/positionings/positioning[" + str(
                    i_pos + 1) + "]"

                length = tixi.getDoubleElement(pos_xpath + "/length")
                sweep_deg = tixi.getDoubleElement(pos_xpath + "/sweepAngle")
                sweep = math.radians(sweep_deg)
                dihedral_deg = tixi.getDoubleElement(pos_xpath +
                                                     "/dihedralAngle")
                dihedral = math.radians(dihedral_deg)

                # Get the corresponding translation of each positionning
                pos_x_list.append(length * math.sin(sweep))
                pos_y_list.append(length * math.cos(dihedral) *
                                  math.cos(sweep))
                pos_z_list.append(length * math.sin(dihedral) *
                                  math.cos(sweep))

                # Get which section are connected by the positionning
                if tixi.checkElement(pos_xpath + "/fromSectionUID"):
                    from_sec = tixi.getTextElement(pos_xpath +
                                                   "/fromSectionUID")
                else:
                    from_sec = ""
                from_sec_list.append(from_sec)

                if tixi.checkElement(pos_xpath + "/toSectionUID"):
                    to_sec = tixi.getTextElement(pos_xpath + "/toSectionUID")
                else:
                    to_sec = ""
                to_sec_list.append(to_sec)

            # Re-loop though the positionning to re-order them
            for j_pos in range(pos_cnt):
                if from_sec_list[j_pos] == "":
                    prev_pos_x = 0
                    prev_pos_y = 0
                    prev_pos_z = 0
                elif from_sec_list[j_pos] == to_sec_list[j_pos - 1]:
                    prev_pos_x = pos_x_list[j_pos - 1]
                    prev_pos_y = pos_y_list[j_pos - 1]
                    prev_pos_z = pos_z_list[j_pos - 1]
                else:
                    index_prev = to_sec_list.index(from_sec_list[j_pos])
                    prev_pos_x = pos_x_list[index_prev]
                    prev_pos_y = pos_y_list[index_prev]
                    prev_pos_z = pos_z_list[index_prev]

                pos_x_list[j_pos] += prev_pos_x
                pos_y_list[j_pos] += prev_pos_y
                pos_z_list[j_pos] += prev_pos_z

        else:
            log.warning('No "positionings" have been found!')
            pos_cnt = 0

        # Sections
        sec_cnt = tixi.getNamedChildrenCount(wing_xpath + "/sections",
                                             "section")
        log.info("    -" + str(sec_cnt) + " wing sections have been found")
        wing_sec_index = 1

        if pos_cnt == 0:
            pos_x_list = [0.0] * sec_cnt
            pos_y_list = [0.0] * sec_cnt
            pos_z_list = [0.0] * sec_cnt

        for i_sec in reversed(range(sec_cnt)):
            sec_xpath = wing_xpath + "/sections/section[" + str(i_sec +
                                                                1) + "]"
            sec_uid = tixi.getTextAttribute(sec_xpath, "uID")
            sec_transf = Transformation()
            sec_transf.get_cpacs_transf(tixi, sec_xpath)

            # Elements
            elem_cnt = tixi.getNamedChildrenCount(sec_xpath + "/elements",
                                                  "element")

            if elem_cnt > 1:
                log.warning("Sections " + sec_uid + "  contains multiple \
                             element, it could be an issue for the conversion \
                             to SUMO!")

            for i_elem in range(elem_cnt):
                elem_xpath = sec_xpath + "/elements/element[" + str(i_elem +
                                                                    1) + "]"
                elem_uid = tixi.getTextAttribute(elem_xpath, "uID")
                elem_transf = Transformation()
                elem_transf.get_cpacs_transf(tixi, elem_xpath)

                # Get wing profile (airfoil)
                prof_uid = tixi.getTextElement(elem_xpath + "/airfoilUID")
                prof_vect_x, prof_vect_y, prof_vect_z = get_profile_coord(
                    tixi, prof_uid)

                # Apply scaling
                for i, item in enumerate(prof_vect_x):
                    prof_vect_x[i] = (item * elem_transf.scaling.x *
                                      sec_transf.scaling.x *
                                      wing_transf.scaling.x)
                for i, item in enumerate(prof_vect_y):
                    prof_vect_y[i] = (item * elem_transf.scaling.y *
                                      sec_transf.scaling.y *
                                      wing_transf.scaling.y)
                for i, item in enumerate(prof_vect_z):
                    prof_vect_z[i] = (item * elem_transf.scaling.z *
                                      sec_transf.scaling.z *
                                      wing_transf.scaling.z)

                # Plot setions (for tests)
                # if (i_sec>8 and i_sec<=10):
                #     plt.plot(prof_vect_x, prof_vect_z,'x')
                #     plt.xlabel('x')
                #     plt.ylabel('z')
                #     plt.grid(True)
                #     plt.show()

                prof_size_x = max(prof_vect_x) - min(prof_vect_x)
                prof_size_y = max(prof_vect_y) - min(prof_vect_y)
                prof_size_z = max(prof_vect_z) - min(prof_vect_z)

                if prof_size_y == 0:
                    prof_vect_x[:] = [x / prof_size_x for x in prof_vect_x]
                    prof_vect_z[:] = [z / prof_size_x for z in prof_vect_z]
                    # Is it correct to divide by prof_size_x ????

                    wg_sec_chord = prof_size_x
                else:
                    log.error("An airfoil profile is not define correctly")

                # SUMO variable for WingSection
                wg_sec_center_x = (elem_transf.translation.x +
                                   sec_transf.translation.x +
                                   pos_x_list[i_sec]) * wing_transf.scaling.x
                wg_sec_center_y = (
                    elem_transf.translation.y * sec_transf.scaling.y +
                    sec_transf.translation.y +
                    pos_y_list[i_sec]) * wing_transf.scaling.y
                wg_sec_center_z = (
                    elem_transf.translation.z * sec_transf.scaling.z +
                    sec_transf.translation.z +
                    pos_z_list[i_sec]) * wing_transf.scaling.z

                # Add roation from element and sections
                # Adding the two angles: Maybe not work in every case!!!
                add_rotation = SimpleNamespace()
                add_rotation.x = elem_transf.rotation.x + sec_transf.rotation.x
                add_rotation.y = elem_transf.rotation.y + sec_transf.rotation.y
                add_rotation.z = elem_transf.rotation.z + sec_transf.rotation.z

                # Get Section rotation for SUMO
                wg_sec_rot = euler2fix(add_rotation)
                wg_sec_dihed = math.radians(wg_sec_rot.x)
                wg_sec_twist = math.radians(wg_sec_rot.y)
                wg_sec_yaw = math.radians(wg_sec_rot.z)

                # Convert point list into string
                prof_str = ""

                # Airfoil points order : shoud be from TE (1 0) to LE (0 0)
                # then TE(1 0), but not reverse way.

                # to avoid double zero, not accepted by SUMO
                for i, item in enumerate(prof_vect_x):
                    # if not (prof_vect_x[i] == prof_vect_x[i-1] or \
                    #         round(prof_vect_z[i],4) == round(prof_vect_z[i-1],4)):
                    if round(prof_vect_z[i], 4) != round(
                            prof_vect_z[i - 1], 4):
                        prof_str += (str(round(prof_vect_x[i], 4)) + " " +
                                     str(round(prof_vect_z[i], 4)) + " ")

                sumo.addTextElementAtIndex(wg_sk_xpath, "WingSection",
                                           prof_str, wing_sec_index)
                wg_sec_xpath = wg_sk_xpath + "/WingSection[" + str(
                    wing_sec_index) + "]"
                sumo.addTextAttribute(wg_sec_xpath, "airfoil", prof_uid)
                sumo.addTextAttribute(wg_sec_xpath, "name", sec_uid)
                wg_sec_center_str = (str(wg_sec_center_x) + " " +
                                     str(wg_sec_center_y) + " " +
                                     str(wg_sec_center_z))
                sumo.addTextAttribute(wg_sec_xpath, "center",
                                      wg_sec_center_str)
                sumo.addTextAttribute(wg_sec_xpath, "chord", str(wg_sec_chord))
                sumo.addTextAttribute(wg_sec_xpath, "dihedral",
                                      str(wg_sec_dihed))
                sumo.addTextAttribute(wg_sec_xpath, "twist", str(wg_sec_twist))
                sumo.addTextAttribute(wg_sec_xpath, "yaw", str(wg_sec_yaw))
                sumo.addTextAttribute(wg_sec_xpath, "napprox", "-1")
                sumo.addTextAttribute(wg_sec_xpath, "reversed", "false")
                sumo.addTextAttribute(wg_sec_xpath, "vbreak", "false")

                wing_sec_index += 1

        # Add Wing caps
        add_wing_cap(sumo, wg_sk_xpath)

    # Engyine pylon(s) ---------------------------------------------------------

    inc_pylon_xpath = "/cpacs/toolspecific/CEASIOMpy/engine/includePylon"
    include_pylon = get_value_or_default(tixi, inc_pylon_xpath, False)

    if tixi.checkElement(PYLONS_XPATH) and include_pylon:
        pylon_cnt = tixi.getNamedChildrenCount(PYLONS_XPATH, "enginePylon")
        log.info(str(pylon_cnt) + " pylons has been found.")
    else:
        pylon_cnt = 0
        log.warning("No pylon has been found in this CPACS file!")

    for i_pylon in range(pylon_cnt):
        pylon_xpath = PYLONS_XPATH + "/enginePylon[" + str(i_pylon + 1) + "]"
        pylon_uid = tixi.getTextAttribute(pylon_xpath, "uID")
        pylon_transf = Transformation()
        pylon_transf.get_cpacs_transf(tixi, pylon_xpath)

        # Create new wing (SUMO) Pylons will be modeled as a wings
        sumo.createElementAtIndex("/Assembly", "WingSkeleton", i_pylon + 1)
        wg_sk_xpath = "/Assembly/WingSkeleton[" + str(i_pylon + 1) + "]"

        sumo.addTextAttribute(wg_sk_xpath, "akimatg", "false")
        sumo.addTextAttribute(wg_sk_xpath, "name", pylon_uid)

        # Create a class for the transformation of the WingSkeleton
        wg_sk_tansf = Transformation()

        # Convert WingSkeleton rotation and add it to SUMO
        wg_sk_tansf.rotation = euler2fix(pylon_transf.rotation)

        wg_sk_rot_str = sumo_str_format(
            math.radians(wg_sk_tansf.rotation.x),
            math.radians(wg_sk_tansf.rotation.y),
            math.radians(wg_sk_tansf.rotation.z),
        )
        sumo.addTextAttribute(wg_sk_xpath, "rotation", wg_sk_rot_str)

        # Add WingSkeleton origin
        wg_sk_tansf.translation = pylon_transf.translation

        sumo.addTextAttribute(
            wg_sk_xpath,
            "origin",
            sumo_str_format(wg_sk_tansf.translation.x,
                            wg_sk_tansf.translation.y,
                            wg_sk_tansf.translation.z),
        )
        sumo.addTextAttribute(wg_sk_xpath, "flags", "detectwinglet")

        # Positionings
        if tixi.checkElement(pylon_xpath + "/positionings"):
            pos_cnt = tixi.getNamedChildrenCount(pylon_xpath + "/positionings",
                                                 "positioning")
            log.info(str(pylon_cnt) + ' "positionning" has been found : ')

            pos_x_list = []
            pos_y_list = []
            pos_z_list = []
            from_sec_list = []
            to_sec_list = []

            for i_pos in range(pos_cnt):
                pos_xpath = pylon_xpath + "/positionings/positioning[" + str(
                    i_pos + 1) + "]"

                length = tixi.getDoubleElement(pos_xpath + "/length")
                sweep_deg = tixi.getDoubleElement(pos_xpath + "/sweepAngle")
                sweep = math.radians(sweep_deg)
                dihedral_deg = tixi.getDoubleElement(pos_xpath +
                                                     "/dihedralAngle")
                dihedral = math.radians(dihedral_deg)

                # Get the corresponding translation of each positionning
                pos_x_list.append(length * math.sin(sweep))
                pos_y_list.append(length * math.cos(dihedral) *
                                  math.cos(sweep))
                pos_z_list.append(length * math.sin(dihedral) *
                                  math.cos(sweep))

                # Get which section are connected by the positionning
                if tixi.checkElement(pos_xpath + "/fromSectionUID"):
                    from_sec = tixi.getTextElement(pos_xpath +
                                                   "/fromSectionUID")
                else:
                    from_sec = ""
                from_sec_list.append(from_sec)

                if tixi.checkElement(pos_xpath + "/toSectionUID"):
                    to_sec = tixi.getTextElement(pos_xpath + "/toSectionUID")
                else:
                    to_sec = ""
                to_sec_list.append(to_sec)

            # Re-loop though the positionning to re-order them
            for j_pos in range(pos_cnt):
                if from_sec_list[j_pos] == "":
                    prev_pos_x = 0
                    prev_pos_y = 0
                    prev_pos_z = 0
                elif from_sec_list[j_pos] == to_sec_list[j_pos - 1]:
                    prev_pos_x = pos_x_list[j_pos - 1]
                    prev_pos_y = pos_y_list[j_pos - 1]
                    prev_pos_z = pos_z_list[j_pos - 1]
                else:
                    index_prev = to_sec_list.index(from_sec_list[j_pos])
                    prev_pos_x = pos_x_list[index_prev]
                    prev_pos_y = pos_y_list[index_prev]
                    prev_pos_z = pos_z_list[index_prev]

                pos_x_list[j_pos] += prev_pos_x
                pos_y_list[j_pos] += prev_pos_y
                pos_z_list[j_pos] += prev_pos_z

        else:
            log.warning('No "positionings" have been found!')
            pos_cnt = 0

        # Sections
        sec_cnt = tixi.getNamedChildrenCount(pylon_xpath + "/sections",
                                             "section")
        log.info("    -" + str(sec_cnt) + " wing sections have been found")
        wing_sec_index = 1

        if pos_cnt == 0:
            pos_x_list = [0.0] * sec_cnt
            pos_y_list = [0.0] * sec_cnt
            pos_z_list = [0.0] * sec_cnt

        check_reversed_wing = []

        for i_sec in range(sec_cnt):
            # for i_sec in reversed(range(sec_cnt)):
            sec_xpath = pylon_xpath + "/sections/section[" + str(i_sec +
                                                                 1) + "]"
            sec_uid = tixi.getTextAttribute(sec_xpath, "uID")
            sec_transf = Transformation()
            sec_transf.get_cpacs_transf(tixi, sec_xpath)

            # Elements
            elem_cnt = tixi.getNamedChildrenCount(sec_xpath + "/elements",
                                                  "element")

            if elem_cnt > 1:
                log.warning("Sections " + sec_uid + "  contains multiple \
                             element, it could be an issue for the conversion \
                             to SUMO!")

            for i_elem in range(elem_cnt):
                elem_xpath = sec_xpath + "/elements/element[" + str(i_elem +
                                                                    1) + "]"
                elem_uid = tixi.getTextAttribute(elem_xpath, "uID")
                elem_transf = Transformation()
                elem_transf.get_cpacs_transf(tixi, elem_xpath)

                # Get pylon profile (airfoil)
                prof_uid = tixi.getTextElement(elem_xpath + "/airfoilUID")
                prof_vect_x, prof_vect_y, prof_vect_z = get_profile_coord(
                    tixi, prof_uid)

                # Apply scaling
                for i, item in enumerate(prof_vect_x):
                    prof_vect_x[i] = (item * elem_transf.scaling.x *
                                      sec_transf.scaling.x *
                                      pylon_transf.scaling.x)
                for i, item in enumerate(prof_vect_y):
                    prof_vect_y[i] = (item * elem_transf.scaling.y *
                                      sec_transf.scaling.y *
                                      pylon_transf.scaling.y)
                for i, item in enumerate(prof_vect_z):
                    prof_vect_z[i] = (item * elem_transf.scaling.z *
                                      sec_transf.scaling.z *
                                      pylon_transf.scaling.z)

                prof_size_x = max(prof_vect_x) - min(prof_vect_x)
                prof_size_y = max(prof_vect_y) - min(prof_vect_y)
                prof_size_z = max(prof_vect_z) - min(prof_vect_z)

                if prof_size_y == 0:
                    prof_vect_x[:] = [x / prof_size_x for x in prof_vect_x]
                    prof_vect_z[:] = [z / prof_size_x for z in prof_vect_z]
                    # Is it correct to divide by prof_size_x ????

                    wg_sec_chord = prof_size_x
                else:
                    log.error("An airfoil profile is not define correctly")

                # SUMO variable for WingSection
                wg_sec_center_x = (elem_transf.translation.x +
                                   sec_transf.translation.x +
                                   pos_x_list[i_sec]) * pylon_transf.scaling.x
                wg_sec_center_y = (
                    elem_transf.translation.y * sec_transf.scaling.y +
                    sec_transf.translation.y +
                    pos_y_list[i_sec]) * pylon_transf.scaling.y
                wg_sec_center_z = (
                    elem_transf.translation.z * sec_transf.scaling.z +
                    sec_transf.translation.z +
                    pos_z_list[i_sec]) * pylon_transf.scaling.z

                check_reversed_wing.append(wg_sec_center_y)

                # Add roation from element and sections
                # Adding the two angles: Maybe not work in every case!!!
                add_rotation = SimpleNamespace()
                add_rotation.x = elem_transf.rotation.x + sec_transf.rotation.x
                add_rotation.y = elem_transf.rotation.y + sec_transf.rotation.y
                add_rotation.z = elem_transf.rotation.z + sec_transf.rotation.z

                # Get Section rotation for SUMO
                wg_sec_rot = euler2fix(add_rotation)
                wg_sec_dihed = math.radians(wg_sec_rot.x)
                wg_sec_twist = math.radians(wg_sec_rot.y)
                wg_sec_yaw = math.radians(wg_sec_rot.z)

                # Convert point list into string
                prof_str = ""

                # Airfoil points order : should be from TE (1 0) to LE (0 0)
                # then TE(1 0), but not reverse way.

                # to avoid double zero, not accepted by SUMO
                for i, item in enumerate(prof_vect_x):
                    # if not (prof_vect_x[i] == prof_vect_x[i-1] or \
                    #         round(prof_vect_z[i],4) == round(prof_vect_z[i-1],4)):
                    if round(prof_vect_z[i], 4) != round(
                            prof_vect_z[i - 1], 4):
                        prof_str += (str(round(prof_vect_x[i], 4)) + " " +
                                     str(round(prof_vect_z[i], 4)) + " ")

                sumo.addTextElementAtIndex(wg_sk_xpath, "WingSection",
                                           prof_str, wing_sec_index)
                wg_sec_xpath = wg_sk_xpath + "/WingSection[" + str(
                    wing_sec_index) + "]"
                sumo.addTextAttribute(wg_sec_xpath, "airfoil", prof_uid)
                sumo.addTextAttribute(wg_sec_xpath, "name", sec_uid)
                sumo.addTextAttribute(
                    wg_sec_xpath,
                    "center",
                    sumo_str_format(wg_sec_center_x, wg_sec_center_y,
                                    wg_sec_center_z),
                )
                sumo.addTextAttribute(wg_sec_xpath, "chord", str(wg_sec_chord))
                sumo.addTextAttribute(wg_sec_xpath, "dihedral",
                                      str(wg_sec_dihed))
                sumo.addTextAttribute(wg_sec_xpath, "twist", str(wg_sec_twist))
                sumo.addTextAttribute(wg_sec_xpath, "yaw", str(wg_sec_yaw))
                sumo.addTextAttribute(wg_sec_xpath, "napprox", "-1")
                sumo.addTextAttribute(wg_sec_xpath, "reversed", "false")
                sumo.addTextAttribute(wg_sec_xpath, "vbreak", "false")

                wing_sec_index += 1

        # Check if the wing section order must be inverted with reversed attribute
        if check_reversed_wing[0] < check_reversed_wing[1]:
            log.info("Wing section order will be reversed.")
            for i_sec in range(sec_cnt):
                wg_sec_xpath = wg_sk_xpath + "/WingSection[" + str(i_sec +
                                                                   1) + "]"
                sumo.removeAttribute(wg_sec_xpath, "reversed")
                sumo.addTextAttribute(wg_sec_xpath, "reversed", "true")

        # If symmetry, create a mirror copy of the Pylon
        if tixi.checkAttribute(pylon_xpath, "symmetry"):
            if tixi.getTextAttribute(pylon_xpath, "symmetry") == "x-z-plane":
                sumo_mirror_copy(sumo, wg_sk_xpath, pylon_uid, True)

        add_wing_cap(sumo, wg_sk_xpath)

    # Engine(s) ----------------------------------------------------------------

    inc_engine_xpath = "/cpacs/toolspecific/CEASIOMpy/engine/includeEngine"
    include_engine = get_value_or_default(tixi, inc_engine_xpath, False)

    if tixi.checkElement(ENGINES_XPATH) and include_engine:
        engine_cnt = tixi.getNamedChildrenCount(ENGINES_XPATH, "engine")
        log.info(str(engine_cnt) + " engines has been found.")
    else:
        engine_cnt = 0
        log.warning("No engine has been found in this CPACS file!")

    for i_engine in range(engine_cnt):
        engine_xpath = ENGINES_XPATH + "/engine[" + str(i_engine + 1) + "]"

        engine = Engine(tixi, engine_xpath)

        # Nacelle (sumo)
        xengtransl = engine.transf.translation.x
        yengtransl = engine.transf.translation.y
        zengtransl = engine.transf.translation.z

        engineparts = [
            engine.nacelle.fancowl, engine.nacelle.corecowl,
            engine.nacelle.centercowl
        ]

        for engpart in engineparts:

            if not engpart.isengpart:
                log.info("This engine part is not define.")
                continue

            if engpart.iscone:

                xcontours = engpart.pointlist.xlist
                ycontours = engpart.pointlist.ylist

                xengtransl += engpart.xoffset

                ysectransl = 0
                zsectransl = 0

            else:

                xlist = engpart.section.pointlist.xlist
                ylist = engpart.section.pointlist.ylist

                xscaling = engpart.section.transf.scaling.x
                zscaling = engpart.section.transf.scaling.z

                # Why scaling z for point in y??? CPACS mystery...
                xlist = [i * xscaling for i in xlist]
                ylist = [i * zscaling for i in ylist]

                # Nacelle parts contour points
                # In CPACS nacelles are define as the revolution of section, in SUMO they have to
                # be define as a body composed of section + a lip at the inlet

                # Find upper part of the profile
                xminidx = xlist.index(min(xlist))

                yavg1 = sum(ylist[0:xminidx]) / (xminidx)
                yavg2 = sum(ylist[xminidx:-1]) / (len(ylist) - xminidx)

                if yavg1 > yavg2:
                    xcontours = xlist[0:xminidx]
                    ycontours = ylist[0:xminidx]
                else:
                    xcontours = xlist[xminidx:-1]
                    ycontours = ylist[xminidx:-1]

                ysectransl = engpart.section.transf.translation.y
                zsectransl = engpart.section.transf.translation.z

            # # Plot
            # fig, ax = plt.subplots()
            # ax.plot(xlist, ylist,'x')
            # ax.plot(xcontours, ycontours,'or')
            # ax.set(xlabel='x', ylabel='y',title='Engine profile')
            # ax.grid()
            # plt.show()

            sumo.createElementAtIndex("/Assembly", "BodySkeleton", i_fus + 1)
            body_xpath = "/Assembly/BodySkeleton[" + str(i_fus + 1) + "]"

            sumo.addTextAttribute(body_xpath, "akimatg", "false")
            sumo.addTextAttribute(body_xpath, "name", engpart.uid)

            # Add body rotation and origin
            sumo.addTextAttribute(body_xpath, "rotation",
                                  sumo_str_format(0, 0, 0))
            sumo.addTextAttribute(
                body_xpath,
                "origin",
                sumo_str_format(xengtransl + ysectransl, yengtransl,
                                zengtransl),
            )

            # Add section
            for i_sec in range(len(xcontours)):

                namesec = "section_" + str(i_sec + 1)
                # Only circle profiles
                prof_str = " 0 -1 0.7071 -0.7071 1 0 0.7071 0.7071 0 1"
                sumo.addTextElementAtIndex(body_xpath, "BodyFrame", prof_str,
                                           i_sec + 1)
                frame_xpath = body_xpath + "/BodyFrame[" + str(i_sec + 1) + "]"

                diam = (ycontours[i_sec] + zsectransl) * 2
                if diam < 0.01:
                    diam = 0.01

                sumo.addTextAttribute(frame_xpath, "center",
                                      sumo_str_format(xcontours[i_sec], 0, 0))
                sumo.addTextAttribute(frame_xpath, "height", str(diam))
                sumo.addTextAttribute(frame_xpath, "width", str(diam))
                sumo.addTextAttribute(frame_xpath, "name", namesec)

            # Nacelle/engine options
            sumo_add_nacelle_lip(sumo, body_xpath)

            if not engpart.iscone:
                sumo_add_engine_bc(sumo, "Engine", engpart.uid)

            if engine.sym:

                sumo.createElementAtIndex("/Assembly", "BodySkeleton",
                                          i_fus + 1)
                body_xpath = "/Assembly/BodySkeleton[" + str(i_fus + 1) + "]"

                sumo.addTextAttribute(body_xpath, "akimatg", "false")
                sumo.addTextAttribute(body_xpath, "name", engpart.uid + "_sym")

                # Add body rotation and origin
                sumo.addTextAttribute(body_xpath, "rotation",
                                      sumo_str_format(0, 0, 0))
                sumo.addTextAttribute(
                    body_xpath,
                    "origin",
                    sumo_str_format(xengtransl + ysectransl, -yengtransl,
                                    zengtransl),
                )

                # Add section
                for i_sec in range(len(xcontours)):

                    namesec = "section_" + str(i_sec + 1)
                    # Only circle profiles
                    prof_str = " 0 -1 0.7071 -0.7071 1 0 0.7071 0.7071 0 1"
                    sumo.addTextElementAtIndex(body_xpath, "BodyFrame",
                                               prof_str, i_sec + 1)
                    frame_xpath = body_xpath + "/BodyFrame[" + str(i_sec +
                                                                   1) + "]"

                    diam = (ycontours[i_sec] + zsectransl) * 2
                    if diam < 0.01:
                        diam = 0.01

                    sumo.addTextAttribute(
                        frame_xpath, "center",
                        sumo_str_format(xcontours[i_sec], 0, 0))
                    sumo.addTextAttribute(frame_xpath, "height", str(diam))
                    sumo.addTextAttribute(frame_xpath, "width", str(diam))
                    sumo.addTextAttribute(frame_xpath, "name", namesec)

                # Nacelle/Enine options
                sumo_add_nacelle_lip(sumo, body_xpath)

                if not engpart.iscone:
                    sumo_add_engine_bc(sumo, "Engine_sym",
                                       engpart.uid + "_sym")

    # Get results directory
    results_dir = get_results_directory("CPACS2SUMO")
    sumo_file_path = Path(results_dir, "ToolOutput.smx")

    create_branch(tixi, SUMOFILE_XPATH)
    tixi.updateTextElement(SUMOFILE_XPATH, str(sumo_file_path))

    # Save CPACS and SMX file
    tixi.save(cpacs_out_path)
    sumo.save(str(sumo_file_path))
예제 #12
0
def wing_check_thickness(h_min, awg, cpacs_in, TP, FUEL_ON_CABIN=0):
    """The fuction subdivides the main wing into nodes and defines
        the fuel and cabin volumes.

    Args:
        h_min (float): Minimum height for the fuselage [m].
        awg (class): AircraftWingGeometry class look at
                     aircraft_geometry_class.py in the classes folder for explanation.
        cpacs_in (str): Path to the CPACS file.
        TP (boolean): True if the aircraft is a turboprop.
        FUEL_ON_CABIN (float): Percentage of the cabin volume used for fuel
                           storaging instead for passengers. (default 0%)

    Returns:
        wing_nodes (float-array): 3D array containing the nodes coordinates (x,y,z) [m,m,m].
        awg (class): AircraftWingGeometry class look at aircraft_geometry_class.py
                     in the classes folder for explanation.
    """

    log.info("-----------------------------------------------------------")
    log.info("----------- Evaluating fuselage and wing volume -----------")
    log.info("-----------------------------------------------------------")

    tixi = open_tixi(cpacs_in)
    tigl = open_tigl(tixi)

    SPACING = 0.1
    subd_c = 30  # Number of subdivisions along the perimeter on eachsurface,
    # total number of points for each section subd_c * 2
    DEN = 0.0
    w = awg.main_wing_index - 1
    wing_nodes = 0
    c = False

    for d in range(1, subd_c + 2):
        DEN += d
    zeta = 1.0 / DEN
    for i in awg.w_seg_sec[:, w, 2]:
        if i == 0.0:
            break
        w_temp = np.zeros((2, subd_c + 2, 3))
        # Number of subdivisions along the longitudinal axis
        subd_l = math.ceil((awg.wing_seg_length[int(i) - 1][w] / SPACING))
        if subd_l == 0:
            subd_l = 1
        eta = 1.0 / subd_l
        et = 0.0
        (xc, yc, zc) = awg.wing_center_seg_point[int(i) - 1][w][:]
        for j in range(0, int(subd_l) - 1):
            (xle, yle, zle) = tigl.wingGetLowerPoint(w + 1, int(i), et, 0.0)
            (xle2, yle2, zle2) = tigl.wingGetLowerPoint(w + 1, int(i), et, 1.0)
            if xle < xle2:
                ZLE = 0.0
                ze = 0.0
            else:
                ZLE = 1.0
                ze = 1.0
            for k in range(0, subd_c + 2):
                if ZLE == 0.0:
                    ze += float(k) * zeta
                elif ZLE == 1.0:
                    ze -= float(k) * zeta
                (xl, yl, zl) = tigl.wingGetLowerPoint(w + 1, int(i), et, ze)
                (xu, yu, zu) = tigl.wingGetUpperPoint(w + 1, int(i), et, ze)
                w_temp[0, k, :] = (xu, yu, zu)
                w_temp[1, k, :] = (xl, yl, zl)
            if c is False:
                wing_nodes = w_temp
                c = True
            else:
                wing_nodes = np.concatenate((wing_nodes, w_temp), axis=0)
            et = j * eta
    (rows, columns, pages) = wing_nodes.shape

    # wing_nodes 3D matrix: the even rows and the zero row correspond
    # to the upper profile of the wing, while all the odd rows correspond
    # to the lower profile. The columns contain the coordinates of each nodes.
    # The page 0,1 and 2 contain respectively the x,y and z coordinate.
    h_max = []
    h_mean = []
    y_sec = []

    for r in range(0, rows - 1, 2):
        h_max_temp = 0
        z_min = 9999
        z_max = 0
        h = []
        for c in range(0, columns):
            (xu, yu, zu) = wing_nodes[r, c, :]
            (xl, yl, zl) = wing_nodes[r + 1, c, :]
            h.append(abs(zu - zl))
            if abs(zu - zl) > h_max_temp:
                h_max_temp = abs(zu - zl)
            if r == 0:
                if zl < z_min:
                    (_, y13, z13) = (xl, yl, zl)
                    z_min = zl
                if zu > z_max:
                    (_, y14, z14) = (xu, yu, zu)
                    z_max = zu
            else:
                if zl < z_min:
                    (x23_t, y23_t, z23_t) = (xl, yl, zl)
                    z_min = zl
                if zu > z_max:
                    (x24_t, y24_t, z24_t) = (xu, yu, zu)
                    z_max = zu
        h_max.append(h_max_temp)
        h_mean.append(np.mean(h))
        y_sec.append(yl)
        if np.mean(h) >= h_min:
            # h_mean_cabin = np.mean(h_mean)
            awg.y_max_cabin = yl
            seg = r
            if r != 0:
                (_, y23, z23) = (x23_t, y23_t, z23_t)
                (_, y24, z24) = (x24_t, y24_t, z24_t)
        else:
            (x11, y11, z11) = wing_nodes[0, 0, :]
            (x12, y12, z12) = wing_nodes[0, -1, :]
            (x21, y21, z21) = wing_nodes[seg, 0, :]
            (x22, y22, z22) = wing_nodes[seg, -1, :]
            break

    for c in range(0, columns):
        (xu, yu, zu) = wing_nodes[0, c, :]
        (xl, yl, zl) = wing_nodes[1, c, :]
        if abs(zu - zl) >= h_min:
            xs1 = xu
            yse1 = yl
            break
    for c in range(0, columns):
        (xu, yu, zu) = wing_nodes[seg, c, :]
        (xl, yl, zl) = wing_nodes[seg + 1, c, :]
        if abs(zu - zl) >= h_min:
            xs2 = xu
            yse2 = yl
            break
    for c in range(columns - 1, -1, -1):
        (xu, yu, zu) = wing_nodes[0, c, :]
        (xl, yl, zl) = wing_nodes[1, c, :]
        if abs(zu - zl) >= h_min:
            xe1 = xu
            break
    for c in range(columns - 1, -1, -1):
        (xu, yu, zu) = wing_nodes[seg, c, :]
        (xl, yl, zl) = wing_nodes[seg + 1, c, :]
        if abs(zu - zl) >= h_min:
            xe2 = xu
            # ze2u = zu
            # ze2l = zl
            break

    awg.cabin_area = 0.5 * abs(xs1 * yse2 + xs2 * yse2 + xe2 * yse1 +
                               xe1 * yse1 - xs2 * yse1 - xe2 * yse2 -
                               xe1 * yse2 - xs1 * yse1)
    fuse_plt_area = 0.5 * abs(x11 * y21 + x21 * y22 + x22 * y12 + x12 * y11 -
                              x21 * y11 - x22 * y21 - x12 * y22 - x11 * y12)
    fuse_frontal_area = 0.5 * abs(y24 * z23 + y23 * z13 + y13 * z14 +
                                  y14 * z24 - z24 * y23 - z23 * y13 -
                                  z13 * y14 - z14 * y24)
    c1 = math.sqrt((x11 - x12)**2 + (y11 - y12)**2 + (z11 - z12)**2)
    c2 = math.sqrt((x21 - x22)**2 + (y21 - y22)**2 + (z21 - z22)**2)

    awg.cabin_span = abs(awg.y_max_cabin - y11)

    awg.fuse_vol = ((0.95 * fuse_frontal_area) * (fuse_plt_area /
                                                  (awg.cabin_span)) /
                    (math.sqrt(1 + (c2 / c1))))

    if awg.wing_sym[w - 1] != 0:
        awg.fuse_vol *= 2
        awg.cabin_area *= 2

    awg.cabin_vol = awg.cabin_area * h_min
    delta_vol = awg.fuse_vol - awg.cabin_vol
    awg.fuse_fuel_vol = (float(FUEL_ON_CABIN) / 100.0) * delta_vol
    if TP:
        t = 0.5
    else:
        t = 0.55
    awg.wing_fuel_vol = t * (awg.wing_vol[w] - awg.fuse_vol)
    awg.fuel_vol_tot = awg.fuse_fuel_vol + awg.wing_fuel_vol

    # log info display ------------------------------------------------------------
    log.info("--------------------- Main wing Volumes -------------------")
    log.info("Wing volume [m^3]: " + str(awg.wing_vol[w]))
    log.info("Cabin volume [m^3]: " + str(awg.cabin_vol))
    log.info("Volume of the wing as fuselage [m^3]: " + str(awg.fuse_vol))
    log.info("Volume of the remaining portion of the wing [m^3]: " +
             str(awg.wing_vol[w] - awg.fuse_vol))
    log.info("Fuel volume in the fuselage [m^3]: " + str(awg.fuse_fuel_vol))
    log.info("Fuel volume in the wing [m^3]: " + str(awg.wing_fuel_vol))
    log.info("Total fuel Volume [m^3]: " + str(awg.fuel_vol_tot))
    log.info("-----------------------------------------------------------")

    return (awg, wing_nodes)
예제 #13
0
def get_data(ui, bi, mw, ed, cpacs_in):
    """ The function extracts from the xml file the required input data,
        the code will use the default value when they are missing.

        INPUT
        (class) ui       --Arg.: UserInputs class.
        (class) bi       --Arg.: BalanceInputs class.
        (class) mw       --Arg.: MassesWeight class.
        (class) ed       --Arg.: EngineData class.
        ##======= Classes are defined in the InputClasses folder =======##

        (char) cpacs_in  --Arg.: Relative location of the xml file in the
                                 ToolInput folder (cpacs option) or
                                 relative location of the temp. xml file in
                                 the ToolOutput folder (input option).
        OUTPUT
        (class) mw       --Out.: MassesWeight class updated.
        (class) ed       --Out.: EngineData class updated.
        (file) cpacs_in  --Out.: Updated cpasc file.
    """
    log.info("CPACS file path check")

    # path definition ========================================================
    # Opening CPACS file
    tixi = open_tixi(cpacs_in)

    TSPEC_PATH = "/cpacs/toolspecific/CEASIOMpy"

    GEOM_PATH = TSPEC_PATH + "/geometry"
    FMP_PATH = TSPEC_PATH + "/weight/passengers/fuelMassMaxpass/mass"
    PROP_PATH = TSPEC_PATH + "/propulsion"

    MASS_PATH = "/cpacs/vehicles/aircraft/model/analyses/massBreakdown"
    MTOM_PATH = MASS_PATH + "/designMasses/mTOM/mass"
    F_PATH = MASS_PATH + "/fuel/massDescription/mass"
    OEM_PATH = MASS_PATH + "/mOEM/massDescription/mass"
    PAY_PATH = MASS_PATH + "/payload/massDescription/mass"

    EN_PATH = "/cpacs/vehicles/engines/engine1/analysis/mass/mass"

    BC_PATH = TSPEC_PATH + "/balance/userBalance"
    create_branch(tixi, BC_PATH, False)
    # Compulsory path checks =================================================

    if not tixi.checkElement(TSPEC_PATH):
        raise Exception("Missing required toolspecific path. Run " +
                        "Weight_unc_main.py," +
                        " in the 4Weight_unc_module folder.")
    elif not tixi.checkElement(MASS_PATH):
        raise Exception("Missing required massBreakdown path. Run " +
                        "Weight_unc_main.py," +
                        " in the 4Weight_unc_module folder.")
    elif not tixi.checkElement(MTOM_PATH):
        raise Exception("Missing required mTOM/mass path. Run " +
                        "Weight_unc_main.py," +
                        " in the 4Weight_unc_module folder.")
    elif not tixi.checkElement(FMP_PATH):
        raise Exception("Missing required fuelMassMaxpass/mass path. Run " +
                        "Weight_unc_main.py," +
                        " in the 4Weight_unc_module folder.")
    elif not tixi.checkElement(OEM_PATH):
        raise Exception("Missing required mOEM/massDescription/mass " +
                        "path. Run Weight_unc_main.py," +
                        " in the 4Weight_unc_module folder.")
    elif not tixi.checkElement(PAY_PATH):
        raise Exception("Missing required payload/massDescription/mass " +
                        "path. Run Weight_unc_main.py," +
                        " in the 4Weight_unc_module folder.")
    elif not tixi.checkElement(F_PATH):
        raise Exception("Missing required /fuel/massDescription/mass " +
                        "path. Run Weight_unc_main.py," +
                        " in the 4Weight_unc_module folder.")
    elif not tixi.checkElement(EN_PATH):
        raise Exception("Missing required /cpacs/vehicles/engines/engine1" +
                        "/analysis/mass path. Run Weight_unc_main.py," +
                        " in the 4Weight_unc_module folder.")
    else:
        log.info("All path correctly defined in the toolinput.xml file, " +
                 "beginning data extracction.")

    # Gathering data =========================================================
    # Geometry Data

    if not tixi.checkElement(GEOM_PATH + "/floorsNb"):
        tixi.createElement(GEOM_PATH, "floorsNb")
        tixi.updateDoubleElement(GEOM_PATH + "/floorsNb", ui.FLOORS_NB, "%g")
    else:
        temp = tixi.getDoubleElement(GEOM_PATH + "/floorsNb")
        if temp != ui.FLOORS_NB and temp > 0:
            ui.FLOORS_NB = temp

    if not tixi.checkElement(GEOM_PATH + "/cabinHeight"):
        tixi.createElement(GEOM_PATH, "cabinHeight")
        tixi.updateDoubleElement(GEOM_PATH + "/cabinHeight", ui.H_LIM_CABIN,
                                 "%g")
    else:
        temp = tixi.getDoubleElement(GEOM_PATH + "/cabinHeight")
        if temp != ui.H_LIM_CABIN and temp > 0:
            ui.H_LIM_CABIN = temp

    #  User Case Balance
    if not tixi.checkElement(BC_PATH + "/userCase"):
        tixi.createElement(BC_PATH, "userCase")
        if bi.USER_CASE:
            tixi.updateTextElement(BC_PATH + "/userCase", "True")
        else:
            tixi.updateTextElement(BC_PATH + "/userCase", "False")
    else:
        temp = tixi.getTextElement(BC_PATH + "/userCase")
        if temp == "False":
            bi.USER_CASE = False
        else:
            bi.USER_CASE = True

    if bi.USER_CASE:
        if tixi.checkElement(BC_PATH + "/fuelPercentage"):
            bi.F_PERC = tixi.getDoubleElement(BC_PATH + "/fuelPercentage")
        elif bi.F_PERC:
            tixi.createElement(BC_PATH, "fuelPercentage")
            tixi.updateDoubleElement(BC_PATH + "/fuelPercentage", bi.F_PERC,
                                     "%g")
        else:
            raise Exception("User balance option defined" +
                            " True but no fuel percentage data in the" +
                            " CPACS file or in th BalanceInput class.")
        if tixi.checkElement(BC_PATH + "/payloadPercentage"):
            bi.P_PERC = tixi.getDoubleElement(BC_PATH + "/payloadPercentage")
        elif bi.P_PERC:
            tixi.createElement(BC_PATH, "payloadPercentage")
            tixi.updateDoubleElement(BC_PATH + "/payloadPercentage", bi.P_PERC,
                                     "%g")
        else:
            raise Exception("User balance option defined" +
                            " True but no payload percentage data in" +
                            " the CPACS file or in th BalanceInput class.")
    #  Engines Data
    ed.en_mass = tixi.getDoubleElement(EN_PATH)

    if not tixi.checkElement(PROP_PATH + "/wingMountedEngine"):
        create_branch(tixi, PROP_PATH, False)
        tixi.createElement(PROP_PATH, "wingMountedEngine")
        if ed.WING_MOUNTED:
            tixi.updateTextElement(PROP_PATH + "/wingMountedEngine", "True")
        else:
            tixi.updateTextElement(PROP_PATH + "/wingMountedEngine", "False")
    else:
        temp = tixi.getTextElement(PROP_PATH + "/wingMountedEngine")
        if temp == "False":
            ed.WING_MOUNTED = False
        else:
            ed.WING_MOUNTED = True

    if not tixi.checkElement(PROP_PATH + "/userEnginePlacement"):
        tixi.createElement(PROP_PATH, "userEnginePlacement")
        if bi.USER_EN_PLACEMENT:
            tixi.updateTextElement(PROP_PATH + "/userEnginePlacement", "True")
        else:
            tixi.updateTextElement(PROP_PATH + "/userEnginePlacement", "False")
    else:
        temp = tixi.getTextElement(PROP_PATH + "/userEnginePlacement")
        if temp == "False":
            bi.USER_EN_PLACEMENT = False
        else:
            bi.USER_EN_PLACEMENT = True

    if not tixi.checkElement(PROP_PATH + "/engineNumber"):
        create_branch(tixi, PROP_PATH, False)
        tixi.createElement(PROP_PATH, "engineNumber")
        tixi.updateIntegerElement(PROP_PATH + "/engineNumber", ed.NE, "%i")
    else:
        ed.NE = tixi.getIntegerElement(PROP_PATH + "/engineNumber")

    #  User Engine Placement
    tp = []
    ed.EN_NAME = []
    if tixi.checkElement(EN_PATH):
        for e in range(0, ed.NE):
            EN_PATH = "/cpacs/vehicles/engines"
            if ed.NE > 1:
                EN_PATH += "/engine" + str(e + 1)
            else:
                EN_PATH += "/engine"
            if not tixi.checkElement(EN_PATH):
                raise Exception("Engine definition inclomplete, missing" +
                                " one or more engines in the cpacs file")
            if not tixi.checkElement(EN_PATH + "/name"):
                ed.EN_NAME.append("Engine_" + str(e + 1))
            else:
                ed.EN_NAME.append(tixi.getTextElement(EN_PATH + "/name"))
            ENA_PATH = EN_PATH + "/analysis/mass"
            if tixi.checkElement(ENA_PATH):
                ed.en_mass = tixi.getDoubleElement(ENA_PATH + "/mass")
                tp.append(ed.en_mass)
                if e > 0 and ed.en_mass != tp[e - 1]:
                    log.warning("The engines have different masses," +
                                "this can lead to an unbalanced aircraft")
            elif ed.en_mass:
                tixi.createElement(ENA_PATH, "mass")
                tixi.updateDoubleElement(ENA_PATH + "/mass", ed.en_mass, "%g")
            else:
                raise Exception("Engine definition inclomplete, missing" +
                                " engine mass in the cpacs file")

    s = np.shape(ed.EN_PLACEMENT)
    warn = False
    if not ed.NE:
        raise Exception("No engine defined for the aircraft")
    elif s[0] < ed.NE or s[1] < 3 or np.any(ed.EN_PLACEMENT) == False:
        warn = True
    else:
        log.info("EngineData class defined correctly.")

    s = ed.EN_PLACEMENT
    if bi.USER_EN_PLACEMENT:
        ed.EN_PLACEMENT = []
        for e in range(1, ed.NE + 1):
            if ed.NE > 1:
                ENLOC_PATH = "/cpacs/vehicles/engines/engine" + str(
                    e) + "/analysis/mass/location"
            else:
                ENLOC_PATH = "/cpacs/vehicles/engines/engine/analysis/mass/location"
            if not tixi.checkElement(ENLOC_PATH) and warn:
                raise Exception("User engine Placement option defined" +
                                " True but no engine placement data in the" +
                                " CPACS file.")
            if not tixi.checkElement(ENLOC_PATH) and not warn:
                create_branch(tixi, ENLOC_PATH, False)
                tixi.createElement(ENLOC_PATH, "x")
                tixi.createElement(ENLOC_PATH, "y")
                tixi.createElement(ENLOC_PATH, "z")
                tixi.updateDoubleElement(ENLOC_PATH + "/x", s[e - 1][0], "%g")
                tixi.updateDoubleElement(ENLOC_PATH + "/y", s[e - 1][1], "%g")
                tixi.updateDoubleElement(ENLOC_PATH + "/z", s[e - 1][2], "%g")
                ed.EN_PLACEMENT.append([s[e - 1][0], s[e - 1][1], s[e - 1][2]])
            else:
                x = tixi.getDoubleElement(ENLOC_PATH + "/x")
                y = tixi.getDoubleElement(ENLOC_PATH + "/y")
                z = tixi.getDoubleElement(ENLOC_PATH + "/z")
                ed.EN_PLACEMENT.append([x, y, z])
        ed.EN_PLACEMENT = np.array(ed.EN_PLACEMENT)

    # REQUIRED TOOLSPECIFIC DATA ============================================
    # Fuel
    mw.mass_fuel_maxpass = tixi.getDoubleElement(FMP_PATH)

    # REQUIRED MASSBREAKDOWN DATA ===========================================

    mw.maximum_take_off_mass = tixi.getDoubleElement(MTOM_PATH)
    mw.operating_empty_mass = tixi.getDoubleElement(OEM_PATH)
    mw.mass_payload = tixi.getDoubleElement(PAY_PATH)
    mw.mass_fuel_tot = tixi.getDoubleElement(F_PATH)

    log.info("Data from CPACS file succesfully extracted")

    # # Saving and closing the cpacs file ======================================
    tixi.save(cpacs_in)

    return (mw, ed)
예제 #14
0
def fuse_geom_eval(fus_nb, h_min, fuse_thick, F_FUEL, afg, cpacs_in):
    """Main function to evaluate the fuselage geometry

    Args:
        fus_nb (int): Number of fuselages.
        h_min (float): Minimum height for the fuselage [m].
        fuse_thick(float) : Thickness of the fuselage [mm].
        F_FUEL (float-array): Percentage of the total volume of the fuel tank
                              fuselage, used for fuel straging (set False if
                              fuselage is ment for payload/passengers).
        afg (class): AircraftGeometry class look at aircraft_geometry_class.py
                     in the classes folder for explanation.
        cpacs_in (str): Path to the CPACS file

    Returns:
        afg (class): Updated aircraft_geometry class
    """

    log.info("-----------------------------------------------------------")
    log.info("---------- Analysing fuselage geometry --------------------")
    log.info("-----------------------------------------------------------")

    # Opening tixi and tigl
    tixi = open_tixi(cpacs_in)
    tigl = open_tigl(tixi)

    # INITIALIZATION 1 ----------------------------------------------------------
    afg.fus_nb = fus_nb

    # Counting sections and segments----------------------------------------------

    for i in range(1, afg.fus_nb + 1):
        afg.fuse_sec_nb.append(tigl.fuselageGetSectionCount(i))
        afg.fuse_seg_nb.append(tigl.fuselageGetSegmentCount(i))
        afg.fuse_vol.append(tigl.fuselageGetVolume(i))
        afg.fuse_surface.append(tigl.fuselageGetSurfaceArea(i))

    # Checking segment and section connection and reordering them
    (afg.fuse_sec_nb, start_index, seg_sec,
     fuse_sec_index) = check_segment_connection(afg.fus_nb, afg.fuse_seg_nb,
                                                afg.fuse_sec_nb, tigl)
    afg.f_seg_sec = seg_sec

    # INITIALIZATION 2 -----------------------------------------------------------

    max_sec_nb = np.amax(afg.fuse_sec_nb)
    max_seg_nb = np.amax(afg.fuse_seg_nb)
    afg.fuse_sec_per = np.zeros((max_sec_nb, afg.fus_nb))
    afg.fuse_sec_width = np.zeros((max_sec_nb, afg.fus_nb))
    afg.fuse_sec_height = np.zeros((max_sec_nb, afg.fus_nb))
    afg.fuse_sec_rel_dist = np.zeros((max_sec_nb, afg.fus_nb))
    afg.fuse_seg_index = np.zeros((max_sec_nb, afg.fus_nb))
    afg.fuse_seg_length = np.zeros((max_seg_nb, afg.fus_nb))
    afg.fuse_center_section_point = np.zeros((max_sec_nb, afg.fus_nb, 3))
    afg.fuse_center_seg_point = np.zeros((max_sec_nb, afg.fus_nb, 3))
    afg.fuse_seg_vol = np.zeros((max_seg_nb, afg.fus_nb))
    x1 = np.zeros((max_sec_nb, afg.fus_nb))
    y1 = np.zeros((max_sec_nb, afg.fus_nb))
    z1 = np.zeros((max_sec_nb, afg.fus_nb))
    x2 = np.zeros((max_sec_nb, afg.fus_nb))
    y2 = np.zeros((max_sec_nb, afg.fus_nb))
    z2 = np.zeros((max_sec_nb, afg.fus_nb))

    # FUSELAGE ANALYSIS ----------------------------------------------------------

    # Aircraft total length ------------------------------------------------------
    afg.tot_length = tigl.configurationGetLength()

    # Evaluating fuselage: sections perimeter, segments volume and length ---
    for i in range(1, afg.fus_nb + 1):
        (afg.fuse_sec_rel_dist[:, i - 1],
         afg.fuse_seg_index[:, i - 1]) = rel_dist(
             i,
             afg.fuse_sec_nb[i - 1],
             afg.fuse_seg_nb[i - 1],
             tigl,
             seg_sec[:, i - 1, :],
             start_index[i - 1],
         )
        afg.fuse_length.append(round(afg.fuse_sec_rel_dist[-1, i - 1], 3))
        for j in range(1, afg.fuse_seg_nb[i - 1] + 1):
            k = int(afg.fuse_seg_index[j][i - 1])
            afg.fuse_sec_per[j][i - 1] = tigl.fuselageGetCircumference(
                i, k, 1.0)
            (fpx, fpy, fpz) = tigl.fuselageGetPoint(i, k, 1.0, 0.0)
            (fpx2, fpy2, fpz2) = tigl.fuselageGetPoint(i, k, 1.0, 0.5)
            afg.fuse_seg_vol[j - 1][i - 1] = tigl.fuselageGetSegmentVolume(
                i, k)
            afg.fuse_center_section_point[j][i - 1][0] = (fpx + fpx2) / 2
            afg.fuse_center_section_point[j][i - 1][1] = (fpy + fpy2) / 2
            afg.fuse_center_section_point[j][i - 1][2] = (fpz + fpz2) / 2
            hw1 = 0.0
            hw2 = 0.0
            for zeta in np.arange(0.0, 1.0, 0.001):
                (fpx, fpy, fpz) = tigl.fuselageGetPoint(i, k, 1.0, zeta)
                if abs(fpz -
                       afg.fuse_center_section_point[j][i - 1][2]) < 0.01:
                    if fpy > afg.fuse_center_section_point[j][
                            i - 1][1] and hw1 == 0.0:
                        hw1 = abs(fpy -
                                  afg.fuse_center_section_point[j][i - 1][1])
                        x1[j, i - 1] = fpx
                        y1[j, i - 1] = fpy
                        z1[j, i - 1] = fpz
                    elif fpy < afg.fuse_center_section_point[j][
                            i - 1][1] and hw2 == 0.0:
                        hw2 = abs(fpy -
                                  afg.fuse_center_section_point[j][i - 1][1])
                        x2[j, i - 1] = fpx
                        y2[j, i - 1] = fpy
                        z2[j, i - 1] = fpz
                        break
            afg.fuse_sec_width[j][i - 1] = hw1 + hw2
            hh1 = 0.0
            hh2 = 0.0
            for zeta in np.arange(0.0, 1.0, 0.001):
                (fpx, fpy, fpz) = tigl.fuselageGetPoint(i, k, 1.0, zeta)
                if abs(fpy -
                       afg.fuse_center_section_point[j][i - 1][1]) < 0.01:
                    if fpz > afg.fuse_center_section_point[j][
                            i - 1][2] and hh1 == 0.0:
                        hh1 = abs(fpz -
                                  afg.fuse_center_section_point[j][i - 1][2])
                    elif fpz < afg.fuse_center_section_point[j][
                            i - 1][2] and hh2 == 0.0:
                        hh2 = abs(fpz -
                                  afg.fuse_center_section_point[j][i - 1][2])
                        break
            afg.fuse_sec_height[j][i - 1] = hh1 + hh2
            (fslpx, fslpy, fslpz) = tigl.fuselageGetPoint(1, k, 0.0, 0.0)
            (fslpx2, fslpy2, fslpz2) = tigl.fuselageGetPoint(1, k, 1.0, 0.0)
            afg.fuse_seg_length[j - 1][i - 1] = abs(fslpx2 - fslpx)
        k = int(afg.fuse_seg_index[1][i - 1])
        afg.fuse_sec_per[0][i - 1] = tigl.fuselageGetCircumference(i, k, 0.0)
        (fpx, fpy, fpz) = tigl.fuselageGetPoint(i, k, 0.0, 0.0)
        (fpx2, fpy2, fpz2) = tigl.fuselageGetPoint(i, k, 0.0, 0.5)
        afg.fuse_center_section_point[0][i - 1][0] = (fpx + fpx2) / 2
        afg.fuse_center_section_point[0][i - 1][1] = (fpy + fpy2) / 2
        afg.fuse_center_section_point[0][i - 1][2] = (fpz + fpz2) / 2
        hw1 = 0
        hw2 = 0
        for zeta in np.arange(0.0, 1.0, 0.001):
            (fpx, fpy, fpz) = tigl.fuselageGetPoint(i, k, 0.0, zeta)
            if abs(fpz - afg.fuse_center_section_point[0][i - 1][2]) < 0.01:
                if fpy > afg.fuse_center_section_point[0][i -
                                                          1][1] and hw1 == 0:
                    hw1 = abs(fpy - afg.fuse_center_section_point[0][i - 1][1])
                    x1[0, i - 1] = fpx
                    y1[0, i - 1] = fpy
                    z1[0, i - 1] = fpz
                elif fpy < afg.fuse_center_section_point[0][i -
                                                            1][1] and hw2 == 0:
                    hw2 = abs(fpy - afg.fuse_center_section_point[0][i - 1][1])
                    x2[0, i - 1] = fpx
                    y2[0, i - 1] = fpy
                    z2[0, i - 1] = fpz
                    break
        afg.fuse_sec_width[0][i - 1] = hw1 + hw2
        hh1 = 0.0
        hh2 = 0.0
        for zeta in np.arange(0.0, 1.0, 0.001):
            (fpx, fpy, fpz) = tigl.fuselageGetPoint(i, k, 1.0, zeta)
            if abs(fpy - afg.fuse_center_section_point[0][i - 1][1]) < 0.01:
                if fpz > afg.fuse_center_section_point[0][i -
                                                          1][2] and hh1 == 0.0:
                    hh1 = abs(fpz - afg.fuse_center_section_point[0][i - 1][2])
                elif fpz < afg.fuse_center_section_point[0][
                        i - 1][2] and hh2 == 0.0:
                    hh2 = abs(fpz - afg.fuse_center_section_point[0][i - 1][2])
                    break
        afg.fuse_sec_height[0][i - 1] = hh1 + hh2
        afg.fuse_mean_width.append(
            round(np.mean(afg.fuse_sec_width[:, i - 1]), 3))

    # Evaluating the point at the center of each segment.
    for i in range(int(afg.fuse_nb)):
        for j in range(1, afg.fuse_seg_nb[i - 1] + 1):
            afg.fuse_center_seg_point[j - 1][
                i - 1][0] = (afg.fuse_center_section_point[j - 1][i - 1][0] +
                             afg.fuse_center_section_point[j][i - 1][0]) / 2
            afg.fuse_center_seg_point[j - 1][
                i - 1][1] = (afg.fuse_center_section_point[j - 1][i - 1][1] +
                             afg.fuse_center_section_point[j][i - 1][1]) / 2
            afg.fuse_center_seg_point[j - 1][
                i - 1][2] = (afg.fuse_center_section_point[j - 1][i - 1][2] +
                             afg.fuse_center_section_point[j][i - 1][2]) / 2

    # Evaluating cabin length and volume, nose length and tail_length ------------
    log.info("-----------------------------------------------------------")
    log.info("----------- Analysing cabin dimensions --------------------")
    log.info("-----------------------------------------------------------")
    corr = (1.3) + np.zeros((afg.fus_nb))
    c = False
    afg.cabin_nb = np.zeros((afg.fus_nb))
    afg.cabin_area = np.zeros((afg.fus_nb))
    afg.fuse_cabin_length = np.zeros((afg.fus_nb))
    afg.cabin_seg = np.zeros((max_seg_nb, afg.fus_nb))
    afg.cabin_length = np.zeros((afg.fus_nb))
    afg.fuse_cabin_vol = np.zeros((afg.fus_nb))
    afg.fuse_nose_length = np.zeros((afg.fus_nb))
    afg.fuse_tail_length = np.zeros((afg.fus_nb))
    afg.fuse_fuel_vol = np.zeros((afg.fus_nb))

    for i in range(1, afg.fus_nb + 1):
        ex = False
        cabin_seg = np.zeros((max_seg_nb, 1))
        cabin_nb = 0
        cabin_length = 0
        cabin_volume = 0
        nose_length = 0
        tail_length = 0
        if not F_FUEL[i - 1]:
            for j in range(1, afg.fuse_seg_nb[i - 1] + 1):
                if round(afg.fuse_sec_width[j][i - 1], 3) == round(
                        np.amax(afg.fuse_sec_width[:, i - 1]),
                        3) and (h_min <= afg.fuse_sec_height[j, i - 1]):
                    cabin_length += afg.fuse_seg_length[j - 1, i - 1]
                    cabin_volume += afg.fuse_seg_vol[j - 1, i - 1]
                    cabin_seg[j - 1] = 1
                    c = True
                elif not c:
                    nose_length += afg.fuse_seg_length[j - 1, i - 1]
            if cabin_length >= 0.65 * afg.fuse_length[i - 1]:
                # If the aircraft is designed with 1 or more sections with
                # maximum width and the sun of their length is greater the 65%
                # of the total length, the cabin will be considered only in those
                # sections
                tail_length = afg.fuse_length[i -
                                              1] - cabin_length - nose_length
                cabin_nb = 1
                ex = True
            while ex is False:
                c = False
                cabin_seg[:] = 0
                nose_length = 0
                tail_length = 0
                cabin_length = 0
                cabin_volume = 0
                for j in range(1, afg.fuse_seg_nb[i - 1] + 1):
                    if afg.fuse_sec_width[j][i - 1] >= (
                            corr[i - 1] * afg.fuse_mean_width[i - 1]) and (
                                h_min <= afg.fuse_sec_height[j, i - 1]):
                        cabin_length += afg.fuse_seg_length[j - 1, i - 1]
                        cabin_volume += afg.fuse_seg_vol[j - 1, i - 1]
                        cabin_seg[j - 1] = 1
                        c += 1
                    elif c > 1:
                        tail_length += afg.fuse_seg_length[j - 1, i - 1]
                    else:
                        nose_length += afg.fuse_seg_length[j - 1, i - 1]
                if corr[i - 1] > 0.0 and cabin_length < (
                        0.20 * afg.fuse_length[i - 1]):
                    corr[i - 1] -= 0.05
                else:
                    ex = True
            afg.fuse_nose_length[i - 1] = round(nose_length, 3)
            afg.fuse_fuel_vol[i - 1] = 0
            afg.fuse_tail_length[i - 1] = round(tail_length, 3)
            afg.fuse_cabin_length[i - 1] = round(cabin_length, 3)
            afg.fuse_cabin_vol[i - 1] = round(cabin_volume, 3)
            afg.cabin_nb[i - 1] = cabin_nb
            afg.cabin_seg[:, i - 1] = cabin_seg[:, 0]
            afg.fuse_cabin_length[i - 1] = round(cabin_length, 3)
            cabin_area = 0
            for j in range(0, afg.fuse_seg_nb[i - 1]):
                if afg.cabin_seg[j, i - 1] == 1:
                    (x11, y11, _) = (x1[j, i - 1], y1[j, i - 1], z1[j, i - 1])
                    (x12, y12, _) = (x1[j + 1, i - 1], y1[j + 1,
                                                          i - 1], z1[j + 1,
                                                                     i - 1])
                    (x21, y21, _) = (x2[j, i - 1], y2[j, i - 1], z2[j, i - 1])
                    (x22, y22, _) = (x2[j + 1, i - 1], y2[j + 1,
                                                          i - 1], z2[j + 1,
                                                                     i - 1])
                    cabin_area += 0.5 * abs(
                        x11 * y12 + x12 * y22 + x22 * y21 + x21 * y11 -
                        (y11 * x12 + y12 * x22 + y22 * x21 + y21 * x11))
                elif cabin_area > 0 and afg.cabin_seg[j, i - 1] == 0:
                    break
            thick_area = afg.fuse_cabin_length[i - 1] * (fuse_thick * 2.0)
            afg.cabin_area[i - 1] = round((cabin_area - thick_area), 3)
        else:
            afg.fuse_fuel_vol[i - 1] *= F_FUEL[i - 1] / 100.0
            afg.fuse_nose_length[i - 1] = 0
            afg.fuse_tail_length[i - 1] = 0
            afg.fuse_cabin_length[i - 1] = 0
            afg.fuse_cabin_vol[i - 1] = 0
            afg.cabin_area[i - 1] = 0

    tixi.save(cpacs_in)

    # log info display ------------------------------------------------------------
    log.info("-----------------------------------------------------------")
    log.info("---------- Fuselage Geometry Evaluations ------------------")
    log.info("---------- USEFUL INFO ----------------------------------\n" +
             "If fuselage number is greater than 1 the\n" +
             "informations of each obj are listed in an\n " +
             "array ordered progressively")
    log.info("-----------------------------------------------------------")
    log.info("---------- Fuselage Results -------------------------------")
    log.info("Number of fuselage [-]: " + str(afg.fus_nb))
    log.info("Number of fuselage sections [-]: " + str(afg.fuse_sec_nb))
    log.info("Number of fuselage segments [-]: " + str(afg.fuse_seg_nb))
    log.info("Cabin segments array [-]:\n" + str(cabin_seg))
    log.info("Fuse Length [m]:\n" + str(afg.fuse_length))
    log.info("Fuse nose Length [m]:\n" + str(afg.fuse_nose_length))
    log.info("Fuse cabin Length [m]:\n" + str(afg.fuse_cabin_length))
    log.info("Fuse tail Length [m]:\n" + str(afg.fuse_tail_length))
    log.info("Aircraft Length [m]: " + str(afg.tot_length))
    log.info("Perimeter of each section of each fuselage [m]: \n" +
             str(afg.fuse_sec_per))
    log.info("Relative distance of each section of each fuselage [m]: \n" +
             str(afg.fuse_sec_rel_dist))
    log.info("Length of each segment of each fuselage [m]: \n" +
             str(afg.fuse_seg_length))
    log.info("Mean fuselage width [m]: " + str(afg.fuse_mean_width))
    log.info("Width of each section of each fuselage [m]: \n" +
             str(afg.fuse_sec_width))
    log.info("Cabin area [m^2]:\n" + str(afg.cabin_area))
    log.info("Fuselage wetted surface [m^2]:\n" + str(afg.fuse_surface))
    log.info("Volume of all the segmetns of each fuselage [m^3]: \n" +
             str(afg.fuse_seg_vol))
    log.info("Volume of each cabin [m^3]:\n" + str(afg.fuse_cabin_vol))
    log.info("Volume of each fuselage [m^3]:\n" + str(afg.fuse_vol))
    log.info("Volume of fuel in each fuselage [m^3]:\n" +
             str(afg.fuse_fuel_vol))
    log.info("-----------------------------------------------------------")

    return afg
예제 #15
0
def cpacs_update(mw, out, cpacs_path, cpacs_out_path):
    """ The function updates the cpacs file after the Weight analysis.

    Args:
        mw (class) : MassesWeights class
        out (class) : WeightOutput class
        cpacs_path (str) : Path to the CPACS file
        cpacs_out_path (str) : Path to the output CPACS file

    """

    tixi = open_tixi(
        cpacs_out_path
    )  # (because it has been modifed somewre else, TODO: change that)

    # Path update
    if not tixi.checkElement(CREW_XPATH + "/cabinCrewMembers/cabinCrewMemberNb"):
        create_branch(tixi, CREW_XPATH + "/cabinCrewMembers/cabinCrewMemberNb")
    tixi.updateDoubleElement(
        CREW_XPATH + "/cabinCrewMembers/cabinCrewMemberNb", out.cabin_crew_nb, "%g"
    )

    if not tixi.checkElement(PASS_XPATH + "/passNb"):
        tixi.createElement(PASS_XPATH, "passNb")
    tixi.updateIntegerElement(PASS_XPATH + "/passNb", out.pass_nb, "%i")
    if not tixi.checkElement(PASS_XPATH + "/rowNb"):
        tixi.createElement(PASS_XPATH, "rowNb")
    tixi.updateIntegerElement(PASS_XPATH + "/rowNb", out.row_nb, "%i")
    if not tixi.checkElement(PASS_XPATH + "/aisleNb"):
        tixi.createElement(PASS_XPATH, "aisleNb")
    tixi.updateIntegerElement(PASS_XPATH + "/aisleNb", out.aisle_nb, "%i")
    if not tixi.checkElement(PASS_XPATH + "/toiletNb"):
        tixi.createElement(PASS_XPATH, "toiletNb")
    tixi.updateIntegerElement(PASS_XPATH + "/toiletNb", out.toilet_nb, "%i")
    if not tixi.checkElement(PASS_XPATH + "/abreastNb"):
        tixi.createElement(PASS_XPATH, "abreastNb")
    tixi.updateIntegerElement(PASS_XPATH + "/abreastNb", out.abreast_nb, "%i")

    if not tixi.checkElement(PASS_XPATH + "/fuelMassMaxpass"):
        tixi.createElement(PASS_XPATH, "fuelMassMaxpass")
    FMP_XPATH = PASS_XPATH + "/fuelMassMaxpass"
    if not tixi.checkElement(FMP_XPATH + "/description"):
        tixi.createElement(FMP_XPATH, "description")
    tixi.updateTextElement(
        FMP_XPATH + "/description", "Maximum amount of " + "fuel with maximum payload [kg]"
    )
    if not tixi.checkElement(FMP_XPATH + "/mass"):
        tixi.createElement(FMP_XPATH, "mass")
    tixi.updateDoubleElement(FMP_XPATH + "/mass", mw.mass_fuel_maxpass, "%g")

    # CPACS MASS BREAKDOWN UPDATE

    # Path creation
    if tixi.checkElement(MASSBREAKDOWN_XPATH):
        tixi.removeElement(MASSBREAKDOWN_XPATH)

    MD_XPATH = MASSBREAKDOWN_XPATH + "/designMasses"
    MTOM_XPATH = MD_XPATH + "/mTOM"
    MZFM_XPATH = MD_XPATH + "/mZFM"
    MF_XPATH = MASSBREAKDOWN_XPATH + "/fuel/massDescription"
    OEM_XPATH = MASSBREAKDOWN_XPATH + "/mOEM/massDescription"
    PAY_XPATH = MASSBREAKDOWN_XPATH + "/payload/massDescription"
    MC_XPATH = MASSBREAKDOWN_XPATH + "/payload/mCargo"
    OIM_XPATH = MASSBREAKDOWN_XPATH + "/mOEM/mOperatorItems/mCrewMembers/massDescription"

    create_branch(tixi, MTOM_XPATH + "/mass", False)
    create_branch(tixi, MZFM_XPATH + "/mass", False)
    create_branch(tixi, MF_XPATH + "/mass", False)
    create_branch(tixi, OEM_XPATH + "/mass", False)
    create_branch(tixi, PAY_XPATH + "/mass", False)
    create_branch(tixi, MC_XPATH, False)
    create_branch(tixi, OIM_XPATH + "/mass", False)

    # DESIGN MASSES
    add_uid(tixi, MTOM_XPATH, "MTOM")
    tixi.createElement(MTOM_XPATH, "name")
    tixi.updateTextElement(MTOM_XPATH + "/name", "Maximum take-off mass")
    tixi.createElement(MTOM_XPATH, "description")
    tixi.updateTextElement(
        MTOM_XPATH + "/description",
        "Maximum " + "take off mass [kg], CoG coordinate [m] and " + "moment of inertia.",
    )
    tixi.updateDoubleElement(MTOM_XPATH + "/mass", mw.maximum_take_off_mass, "%g")

    # MZFM
    add_uid(tixi, MZFM_XPATH, "MZFM")
    tixi.createElement(MZFM_XPATH, "name")
    tixi.updateTextElement(MZFM_XPATH + "/name", "Maximum zero fuel mass")
    tixi.createElement(MZFM_XPATH, "description")
    tixi.updateTextElement(
        MZFM_XPATH + "/description",
        "Maximum "
        + "zero fuel mass [kg] and corresponding CoG "
        + "coordinate [m], moment of inertia.",
    )
    tixi.updateDoubleElement(MZFM_XPATH + "/mass", mw.zero_fuel_mass, "%g")

    # FUEL MASS
    add_uid(tixi, MF_XPATH, "MFM")
    tixi.createElement(MF_XPATH, "name")
    tixi.updateTextElement(MF_XPATH + "/name", "Max fuel mass")
    tixi.createElement(MF_XPATH, "description")
    tixi.updateTextElement(MF_XPATH + "/description", "Maximum fuel mass [kg]")
    tixi.updateDoubleElement(MF_XPATH + "/mass", mw.mass_fuel_max, "%g")

    # OEM
    add_uid(tixi, OEM_XPATH, "OEM")
    tixi.createElement(OEM_XPATH, "name")
    tixi.updateTextElement(OEM_XPATH + "/name", "Operating empty mass")
    tixi.createElement(OEM_XPATH, "description")
    tixi.updateTextElement(
        OEM_XPATH + "/description", "Operating empty" + " mass [kg] and related inertia [kgm^2]."
    )
    tixi.updateDoubleElement(OEM_XPATH + "/mass", mw.operating_empty_mass, "%g")
    tixi.updateDoubleElement(OIM_XPATH + "/mass", mw.mass_crew, "%g")
    add_uid(tixi, OIM_XPATH, "massCrew")

    # PAYLOAD MASS AND FUEL WITH MAX PAYLOAD
    add_uid(tixi, PAY_XPATH, "MPM")
    tixi.createElement(PAY_XPATH, "name")
    tixi.updateTextElement(PAY_XPATH + "/name", "Max payload mass")
    tixi.createElement(PAY_XPATH, "description")
    tixi.updateTextElement(PAY_XPATH + "/description", "Maximum " + "payload mass [kg].")
    tixi.updateDoubleElement(PAY_XPATH + "/mass", mw.mass_payload, "%g")

    if mw.mass_cargo:
        tixi.createElement(MC_XPATH, "massCargo")
        tixi.updateDoubleElement(MC_XPATH + "/massCargo", mw.mass_cargo, "%g")

    tixi.save(cpacs_out_path)
예제 #16
0
def fuse_geom_eval(ag, cpacs_in):
    """Main function to evaluate the fuselage geometry.

    INPUT
    (class) ag    --Arg.: AircraftGeometry class.
    # ======= Class is defined in the InputClasses folder =======##
    (char) cpacs_in  -- Arg.: Cpacs xml file location
    OUTPUT
    (class) ag  --Out.: AircraftGeometry class updated .
    """

    # ===========================================================================##
    log.info("---------------------------------------------")
    log.info("-------- Analysing fuselage geometry --------")
    log.info("---------------------------------------------")

    # Opening tixi and tigl
    tixi = open_tixi(cpacs_in)
    tigl = open_tigl(tixi)

    #  ----------------------------------------------------------------------------
    #  COUNTING 1 -----------------------------------------------------------------
    #  Counting fuselage number ---------------------------------------------------
    #  ----------------------------------------------------------------------------

    fus_nb = tixi.getNamedChildrenCount(
        "/cpacs/vehicles/aircraft/model/fuselages", "fuselage")
    #  ----------------------------------------------------------------------------
    #  INITIALIZATION 1 -----------------------------------------------------------
    #  ----------------------------------------------------------------------------

    ag.fus_nb = fus_nb
    ag.fuse_nb = fus_nb
    i = ag.fus_nb

    #  ----------------------------------------------------------------------------
    #  COUNTING 2 -----------------------------------------------------------------
    #  Counting sections and segments----------------------------------------------
    #  ----------------------------------------------------------------------------

    double = 1
    ag.fuse_sym.append(tigl.fuselageGetSymmetry(i))
    if ag.fuse_sym[i - 1] != 0:
        ag.fuse_nb += 1
        double = 2
    ag.fuse_sec_nb.append(tigl.fuselageGetSectionCount(i))
    ag.fuse_seg_nb.append(tigl.fuselageGetSegmentCount(i))
    ag.fuse_vol.append(tigl.fuselageGetVolume(i) * double)

    #  Checking segment and section connection and reordering them
    (ag.fuse_sec_nb, start_index, seg_sec,
     fuse_sec_index) = check_segment_connection(fus_nb, ag.fuse_seg_nb,
                                                ag.fuse_sec_nb, tigl)

    #  ----------------------------------------------------------------------------
    #  INITIALIZATION 2 -----------------------------------------------------------
    #  ----------------------------------------------------------------------------

    max_sec_nb = np.amax(ag.fuse_sec_nb)
    max_seg_nb = np.amax(ag.fuse_seg_nb)
    ag.fuse_sec_circ = np.zeros((max_sec_nb, fus_nb))
    ag.fuse_sec_width = np.zeros((max_sec_nb, fus_nb))
    ag.fuse_sec_rel_dist = np.zeros((max_sec_nb, fus_nb))
    ag.fuse_seg_index = np.zeros((max_sec_nb, fus_nb))
    ag.fuse_seg_length = np.zeros((max_seg_nb, fus_nb))
    fuse_center_section_point = np.zeros((max_sec_nb, fus_nb, 3))
    ag.fuse_center_seg_point = np.zeros((max_seg_nb, ag.fuse_nb, 3))
    ag.fuse_center_sec_point = np.zeros((max_sec_nb, ag.fuse_nb, 3))
    ag.fuse_seg_vol = np.zeros((max_seg_nb, fus_nb))

    # ===========================================================================##
    #  ----------------------------------------------------------------------------
    #  FUSELAGE ANALYSIS ----------------------------------------------------------
    #  ----------------------------------------------------------------------------
    #  Aircraft total length ------------------------------------------------------

    ag.tot_length = tigl.configurationGetLength()

    #  Evaluating fuselage: sections circumference, segments volume and length ---
    (ag.fuse_sec_rel_dist[:, i - 1], ag.fuse_seg_index[:, i - 1]) = rel_dist(
        i,
        ag.fuse_sec_nb[i - 1],
        ag.fuse_seg_nb[i - 1],
        tigl,
        seg_sec[:, i - 1, :],
        start_index[i - 1],
    )
    ag.fuse_length.append(ag.fuse_sec_rel_dist[-1, i - 1])
    for j in range(1, ag.fuse_seg_nb[i - 1] + 1):
        k = int(ag.fuse_seg_index[j][i - 1])
        ag.fuse_sec_circ[j][i - 1] = tigl.fuselageGetCircumference(i, k, 1.0)
        (fpx, fpy, fpz) = tigl.fuselageGetPoint(i, k, 1.0, 0.0)
        (fpx2, fpy2, fpz2) = tigl.fuselageGetPoint(i, k, 1.0, 0.5)
        ag.fuse_seg_vol[j - 1][i - 1] = abs(tigl.fuselageGetSegmentVolume(
            i, k))
        fuse_center_section_point[j][i - 1][0] = (fpx + fpx2) / 2
        fuse_center_section_point[j][i - 1][1] = (fpy + fpy2) / 2
        fuse_center_section_point[j][i - 1][2] = (fpz + fpz2) / 2
        hw1 = 0
        hw2 = 0
        for zeta in np.arange(0.0, 1.0, 0.001):
            (fpx, fpy, fpz) = tigl.fuselageGetPoint(i, k, 1.0, zeta)
            if abs(fpz - fuse_center_section_point[j][i - 1][2]) < 0.01:
                if fpy > fuse_center_section_point[j][i - 1][1] and hw1 == 0:
                    hw1 = abs(fpy - fuse_center_section_point[j][i - 1][1])
                elif fpy < fuse_center_section_point[j][i - 1][1] and hw2 == 0:
                    hw2 = abs(fpy - fuse_center_section_point[j][i - 1][1])
                    break
        ag.fuse_sec_width[j][i - 1] = hw1 + hw2
        (fslpx, fslpy, fslpz) = tigl.fuselageGetPoint(1, k, 0.0, 0.0)
        (fslpx2, fslpy2, fslpz2) = tigl.fuselageGetPoint(1, k, 1.0, 0.0)
        ag.fuse_seg_length[j - 1][i - 1] = abs(fslpx2 - fslpx)
    k = int(ag.fuse_seg_index[1][i - 1])
    ag.fuse_sec_circ[0][i - 1] = tigl.fuselageGetCircumference(i, k, 0.0)
    (fpx, fpy, fpz) = tigl.fuselageGetPoint(i, k, 0.0, 0.0)
    (fpx2, fpy2, fpz2) = tigl.fuselageGetPoint(i, k, 0.0, 0.5)
    fuse_center_section_point[0][i - 1][0] = (fpx + fpx2) / 2
    fuse_center_section_point[0][i - 1][1] = (fpy + fpy2) / 2
    fuse_center_section_point[0][i - 1][2] = (fpz + fpz2) / 2
    hw1 = 0
    hw2 = 0
    for zeta in np.arange(0.0, 1.0, 0.001):
        (fpx, fpy, fpz) = tigl.fuselageGetPoint(i, k, 0.0, zeta)
        if abs(fpz - fuse_center_section_point[0][i - 1][2]) < 0.01:
            if fpy > fuse_center_section_point[0][i - 1][1] and hw1 == 0:
                hw1 = abs(fpy - fuse_center_section_point[0][i - 1][1])
            elif fpy < fuse_center_section_point[0][i - 1][1] and hw2 == 0:
                hw2 = abs(fpy - fuse_center_section_point[0][i - 1][1])
                break
    ag.fuse_sec_width[0][i - 1] = hw1 + hw2
    ag.fuse_mean_width.append(np.mean(ag.fuse_sec_width[:, i - 1]))

    #  Evaluating the point at the center of each segment, symmetry is considered

    a = 0
    cs = False
    for i in range(int(ag.fuse_nb)):
        if cs:
            cs = False
            continue
        for j in range(1, ag.fuse_seg_nb[i - a - 1] + 1):
            ag.fuse_center_seg_point[j - 1][
                i - 1][0] = (fuse_center_section_point[j - 1][i - a - 1][0] +
                             fuse_center_section_point[j][i - a - 1][0]) / 2
            ag.fuse_center_seg_point[j - 1][
                i - 1][1] = (fuse_center_section_point[j - 1][i - a - 1][1] +
                             fuse_center_section_point[j][i - a - 1][1]) / 2
            ag.fuse_center_seg_point[j - 1][
                i - 1][2] = (fuse_center_section_point[j - 1][i - a - 1][2] +
                             fuse_center_section_point[j][i - a - 1][2]) / 2
            ag.fuse_center_sec_point[j - 1][
                i - 1][:] = fuse_center_section_point[j - 1][i - a - 1][:]
        if ag.fuse_sym[i - 1 - a] != 0:
            if ag.fuse_sym[i - 1 - a] == 1:
                symy = 1
                symx = 1
                symz = -1
            if ag.fuse_sym[i - 1 - a] == 2:
                symy = -1
                symx = 1
                symz = 1
            if ag.fuse_sym[i - 1 - a] == 3:
                symy = 1
                symx = -1
                symz = 1
            ag.fuse_center_seg_point[:, i,
                                     0] = ag.fuse_center_seg_point[:, i - 1,
                                                                   0] * symx
            ag.fuse_center_seg_point[:, i,
                                     1] = ag.fuse_center_seg_point[:, i - 1,
                                                                   1] * symy
            ag.fuse_center_seg_point[:, i,
                                     2] = ag.fuse_center_seg_point[:, i - 1,
                                                                   2] * symz
            ag.fuse_center_sec_point[j - 1][i][:] = fuse_center_section_point[
                j - 1][i - a - 1][:]
            cs = True
            a += 1

    # Evaluating cabin length and volume, nose length and tail_length ------------
    ex = False
    corr = 1.25 + np.zeros((1, fus_nb))
    c = False
    cabin_nb = np.zeros((1, fus_nb))
    cabin_seg = np.zeros((max_seg_nb, fus_nb))
    cabin_length = 0
    cabin_volume = 0
    nose_length = 0
    tail_length = 0
    for j in range(1, ag.fuse_seg_nb[i - 1] + 1):
        if round(ag.fuse_sec_width[j][i - 1],
                 3) == round(np.amax(ag.fuse_sec_width[:, i - 1]), 3):
            cabin_length += ag.fuse_seg_length[j - 1, i - 1]
            cabin_volume += ag.fuse_seg_vol[j - 1, i - 1]
            cabin_seg[j - 1][i - 1] = 1
            c = True
        elif not c:
            nose_length += ag.fuse_seg_length[j - 1, i - 1]
    if cabin_length >= 0.65 * ag.fuse_length[i - 1]:
        # If the aircraft is designed with 1 or more sections with
        # maximum width and the sun of their length is greater the 65%
        # of the total length, the cabin will be considered only in those
        # sections
        tail_length = ag.fuse_length[i - 1] - cabin_length - nose_length
        cabin_nb[i - 1] = 1
        ex = True
    while ex is False:
        c = False
        cabin_seg = np.zeros((max_seg_nb, fus_nb))
        nose_length = 0
        tail_length = 0
        cabin_length = 0
        cabin_volume = 0
        for j in range(1, ag.fuse_seg_nb[i - 1] + 1):
            if ag.fuse_sec_width[j][i - 1] >= (corr[i - 1] *
                                               ag.fuse_mean_width[i - 1]):
                cabin_length += ag.fuse_seg_length[j - 1, i - 1]
                cabin_volume += ag.fuse_seg_vol[j - 1, i - 1]
                cabin_seg[j - 1][i - 1] = 1
                c = True
            elif c:
                tail_length += ag.fuse_seg_length[j - 1, i - 1]
            else:
                nose_length += ag.fuse_seg_length[j - 1, i - 1]
        if corr[i - 1] > 0.0 and cabin_length < (0.20 * ag.fuse_length[i - 1]):
            corr[i - 1] -= 0.05
        else:
            ex = True

    ag.fuse_nose_length.append(nose_length)
    ag.fuse_tail_length.append(tail_length)
    ag.fuse_cabin_length.append(cabin_length)
    ag.fuse_cabin_vol.append(cabin_volume)
    ag.f_seg_sec = seg_sec
    ag.cabin_nb = cabin_nb
    ag.cabin_seg = cabin_seg
    ag.fuse_mean_width = ag.fuse_mean_width[0]

    tixi.save(cpacs_in)

    # log info display ------------------------------------------------------------

    log.info("---------------------------------------------")
    log.info("---------- Geometry Evaluations -------------")
    log.info("---------- USEFUL INFO ----------------------\n"
             "If fuselage or wing number is greater than 1 the "
             "informations\nof each part is listed in an "
             "array ordered per column progressively")
    log.info("Symmetry output: 0 = no symmetry, 1 =  x-y, " +
             "2 = x-z, 3 = y-z planes")
    log.info("---------------------------------------------")
    log.info("---------- Fuselage Results -----------------")
    log.info("Number of fuselage [-]: " + str(ag.fuse_nb))
    log.info("Fuselage symmetry plane [-]: " + str(ag.fuse_sym))
    log.info("Number of fuselage sections (not counting symmetry) [-]: " +
             str(ag.fuse_sec_nb))
    log.info("Number of fuselage segments (not counting symmetry) [-]: " +
             str(ag.fuse_seg_nb))
    log.info("Fuse Length [m]: " + str(ag.fuse_length))
    log.info("Fuse nose Length [m]: " + str(ag.fuse_nose_length))
    log.info("Fuse cabin Length [m]: " + str(ag.fuse_cabin_length))
    log.info("Fuse tail Length [m]: " + str(ag.fuse_tail_length))
    log.info("Aircraft Length [m]: " + str(ag.tot_length))
    log.info("Mean fuselage width [m]: " + str(ag.fuse_mean_width))
    log.info("Volume of each cabin [m^3]: " + str(ag.fuse_cabin_vol))
    log.info("Volume of each fuselage [m^3]: " + str(ag.fuse_vol))
    log.info("---------------------------------------------")

    return ag
예제 #17
0
def cpacs_update(mass_pass, out, mw, out_xml):
    """ The function updates the cpacs file after the range analysis.

        INPUT
        (float) mass_pass         --Arg.: Passenger mass, countig also the
                                          extra mass.
        (class) out               --Arg.: RangeOutput class.
        ##======== Class is defined in the InputClasses folder =======##

        (class) mw                --Arg.: MassesWeights class.
        ##======= Class is defined in the InputClasses folder =======##
        (char) out_xml             --Arg.: Path of the output file.


        OUTPUT
        (file) cpacs.xml --Out.: Updated cpacs file.
    """
    tixi = open_tixi(out_xml)

    # PATH CHECKS ==========================================================

    CEASIOM_PATH = "/cpacs/toolspecific/CEASIOMpy"
    # Ranges
    RANGE_PATH = CEASIOM_PATH + "/ranges"
    R_DES_MAXP_PATH = RANGE_PATH + "/rangeMaxP/rangeDescription"
    R_DES_MAXF_PATH = RANGE_PATH + "/rangeMaxF/rangeDescription"
    R_DES_MAXIMUM_PATH = RANGE_PATH + "/rangeMaximum/rangeDescription"

    create_branch(tixi, R_DES_MAXP_PATH + "/range", False)
    create_branch(tixi, R_DES_MAXP_PATH + "/payload", False)
    create_branch(tixi, R_DES_MAXF_PATH + "/range", False)
    create_branch(tixi, R_DES_MAXF_PATH + "/payload", False)
    create_branch(tixi, R_DES_MAXIMUM_PATH + "/range", False)
    create_branch(tixi, R_DES_MAXIMUM_PATH + "/payload", False)

    # Fuel consumption
    FCONS_PATH = "/cpacs/toolspecific/CEASIOMpy/fuelConsumption"
    FDES_PATH = FCONS_PATH + "/description"
    FTO_PATH = FCONS_PATH + "/fuelForTakeOff"
    FC_PATH = FCONS_PATH + "/fuelForClimb"
    FCR_PATH = FCONS_PATH + "/fuelForCruise"
    FL_PATH = FCONS_PATH + "/fuelForLoiter"
    FLD_PATH = FCONS_PATH + "/fuelForLanding"
    FAL_PATH = FCONS_PATH + "/fuelRemained"

    create_branch(tixi, FDES_PATH, False)
    create_branch(tixi, FTO_PATH, False)
    create_branch(tixi, FC_PATH, False)
    create_branch(tixi, FCR_PATH, False)
    create_branch(tixi, FL_PATH, False)
    create_branch(tixi, FLD_PATH, False)
    create_branch(tixi, FAL_PATH, False)

    #  RANGES ===============================================================
    #  Max payload max range ------------------------------------------------
    add_uid(tixi, R_DES_MAXP_PATH,
            "Maximum_range_[km]" + "_with_maximum_payload_[kg]")
    tixi.updateDoubleElement(R_DES_MAXP_PATH + "/range", out.ranges[1], "%g")
    tixi.updateDoubleElement(R_DES_MAXP_PATH + "/payload", out.payloads[1],
                             "%g")

    #  Max fuel range with some payload -------------------------------------
    add_uid(tixi, R_DES_MAXF_PATH,
            "Range_[km]_with_" + "maximum_fuel_and_some_payload_[kg]")
    tixi.updateDoubleElement(R_DES_MAXF_PATH + "/range", out.ranges[2], "%g")
    tixi.updateDoubleElement(R_DES_MAXF_PATH + "/payload", out.payloads[2],
                             "%g")

    #  Maximum range, no payload and max fuel -------------------------------
    add_uid(tixi, R_DES_MAXIMUM_PATH,
            "Maximum_range_[km]_with_" + "max_fuel_and_no_payload_[kg]")
    tixi.updateDoubleElement(R_DES_MAXIMUM_PATH + "/range", out.ranges[3],
                             "%g")
    tixi.updateDoubleElement(R_DES_MAXIMUM_PATH + "/payload", out.payloads[3],
                             "%g")

    #  FUEL CONSUMPTION =====================================================
    add_uid(
        tixi, FDES_PATH,
        "Fuel required for each flight phase " + "[kg], with maximum payload.")

    tixi.updateDoubleElement(FTO_PATH, mw.mf_for_to, "%g")
    tixi.updateDoubleElement(FC_PATH, mw.mf_for_climb, "%g")
    tixi.updateDoubleElement(FCR_PATH, mw.mf_for_cruise, "%g")
    tixi.updateDoubleElement(FL_PATH, mw.mf_for_loiter, "%g")
    tixi.updateDoubleElement(FLD_PATH, mw.mf_for_landing, "%g")
    tixi.updateDoubleElement(FAL_PATH, mw.mf_after_land, "%g")

    #  Saving and closing the new cpacs file inside the ToolOutput folder ---
    tixi.save(out_xml)

    return out_xml
예제 #18
0
def get_data(mw, bi, cpacs_in):
    """ The function extracts from the XML file the required input data,
        the code will use the default value when they are missing.

        INPUT
        (class) mw       --Arg.: MassesWeight class.
        (class) bi       --Arg.: BalanceInput class.
        ##======= Classes are defined in the InputClasses folder =======##

        (char) cpacs_in  --Arg.: Relative location of the xml file in the
                                 ToolInput folder (cpacs option) or
                                 relative location of the temp. xml file in
                                 the ToolOutput folder (input option).

        OUTPUT
        (class) mw       --Out.: MassesWeight class updated.
        (class) bi       --Out.: RangeInput class updated.
        (file) cpacs_in  --Out.: Updated cpasc file.
    """
    log.info("CPACS file path check")

    # path definition ========================================================
    # Opening CPACS file
    tixi = open_tixi(cpacs_in)

    TSPEC_PATH = "/cpacs/toolspecific/CEASIOMpy"
    FMP_PATH = TSPEC_PATH + "/weight/passengers/fuelMassMaxpass/mass"
    PROP_PATH = TSPEC_PATH + "/propulsion"

    BC_PATH = TSPEC_PATH + "/balance/userBalance"
    create_branch(tixi, BC_PATH, False)
    MASS_PATH = "/cpacs/vehicles/aircraft/model/analyses/massBreakdown"
    MTOM_PATH = MASS_PATH + "/designMasses/mTOM/mass"
    F_PATH = MASS_PATH + "/fuel/massDescription/mass"
    OEM_PATH = MASS_PATH + "/mOEM/massDescription/mass"
    PAY_PATH = MASS_PATH + "/payload/massDescription/mass"

    # Compulsory path checks =================================================

    if not tixi.checkElement(TSPEC_PATH):
        raise Exception("Missing required toolspecific path. Run " +
                        "Weight_main.py, in the 1Weight_module folder.")
    elif not tixi.checkElement(MASS_PATH):
        raise Exception("Missing required massBreakdown path. Run " +
                        "Weight_main.py, in the 1Weight_module folder.")
    elif not tixi.checkElement(MTOM_PATH):
        raise Exception("Missing required mTOM/mass path. Run " +
                        "Weight_main.py, in the 1Weight_module folder.")
    elif not tixi.checkElement(FMP_PATH):
        raise Exception("Missing required fuelMassMaxpass/mass path. Run " +
                        "Weight_main.py, in the 1Weight_module folder.")
    elif not tixi.checkElement(OEM_PATH):
        raise Exception("Missing required mOEM/massDescription/mass " +
                        "path. Run " +
                        "Weight_main.py, in the 1Weight_module folder.")
    elif not tixi.checkElement(PAY_PATH):
        raise Exception("Missing required payload/massDescription/mass " +
                        "path. Run " +
                        "Weight_main.py, in the 1Weight_module folder.")
    elif not tixi.checkElement(F_PATH):
        raise Exception("Missing required /fuel/massDescription/mass " +
                        "path. Run " +
                        "Weight_main.py, in the 1Weight_module folder.")
    else:
        log.info("All path correctly defined in the toolinput.xml file, " +
                 "beginning data extracction.")

    # Gathering data =========================================================
    #  TOOLSPECIFIC ----------------------------------------------------------
    if not tixi.checkElement(PROP_PATH + "/wingMountedEngine"):
        create_branch(tixi, PROP_PATH, False)
        tixi.createElement(PROP_PATH, "wingMountedEngine")
        if bi.WING_MOUNTED:
            tixi.updateTextElement(PROP_PATH + "/wingMountedEngine", "True")
        else:
            tixi.updateTextElement(PROP_PATH + "/wingMountedEngine", "False")
    else:
        temp = tixi.getTextElement(PROP_PATH + "/wingMountedEngine")
        if temp == "False":
            bi.WING_MOUNTED = False
        else:
            bi.WING_MOUNTED = True

    #  User Case Balance
    if not tixi.checkElement(BC_PATH + "/userCase"):
        tixi.createElement(BC_PATH, "userCase")
        if bi.USER_CASE:
            tixi.updateTextElement(BC_PATH + "/userCase", "True")
            print("1")
        else:
            tixi.updateTextElement(BC_PATH + "/userCase", "False")
            print("2")
    else:
        temp = tixi.getTextElement(BC_PATH + "/userCase")
        if temp == "False":
            bi.USER_CASE = False
        else:
            bi.USER_CASE = True

    if bi.USER_CASE:
        if tixi.checkElement(BC_PATH + "/fuelPercentage"):
            bi.F_PERC = tixi.getDoubleElement(BC_PATH + "/fuelPercentage")
        elif bi.F_PERC:
            tixi.createElement(BC_PATH, "fuelPercentage")
            tixi.updateDoubleElement(BC_PATH + "/fuelPercentage", bi.F_PERC,
                                     "%g")
        else:
            raise Exception("User balance option defined" +
                            " True but no fuel percentage data in the" +
                            " CPACS file or in th BalanceInput class.")
        if tixi.checkElement(BC_PATH + "/payloadPercentage"):
            bi.P_PERC = tixi.getDoubleElement(BC_PATH + "/payloadPercentage")
        elif bi.P_PERC:
            tixi.createElement(BC_PATH, "payloadPercentage")
            tixi.updateDoubleElement(BC_PATH + "/payloadPercentage", bi.P_PERC,
                                     "%g")
        else:
            raise Exception("User balance option defined" +
                            " True but no payload percentage data in" +
                            " the CPACS file or in th BalanceInput class.")

    #  REQUIRED TOOLSPECIFIC DATA ============================================
    # Fuel
    mw.mass_fuel_maxpass = tixi.getDoubleElement(FMP_PATH)

    #  REQUIRED MASSBREAKDOWN DATA ===========================================

    mw.maximum_take_off_mass = tixi.getDoubleElement(MTOM_PATH)
    mw.operating_empty_mass = tixi.getDoubleElement(OEM_PATH)
    mw.mass_payload = tixi.getDoubleElement(PAY_PATH)
    mw.mass_fuel_max = tixi.getDoubleElement(F_PATH)

    log.info("Data from CPACS file succesfully extracted")

    # Saving and closing the cpacs file ======================================
    tixi.save(cpacs_in)

    return (mw, bi)
예제 #19
0
def wing_geom_eval(ag, cpacs_in):
    """Main function to evaluate the wings geometry

    ARGUMENTS
    (class) ag         --Arg.: AircraftGeometry class.
    # ======= Class are defined in the InputClasses folder =======##
    (char) cpacs_in    -- Arg.: Cpacs xml file location.

    RETURN
    (class) ag  --Out.: AircraftGeometry class updated.
    """

    # ===========================================================================##
    log.info("---------------------------------------------")
    log.info("---------- Analysing wing geometry ----------")
    log.info("---------------------------------------------")

    # Opening tixi and tigl
    tixi = open_tixi(cpacs_in)
    tigl = open_tigl(tixi)

    # ----------------------------------------------------------------------------
    #  COUNTING 1 -----------------------------------------------------------------
    #  Counting wing number without symmetry --------------------------------------
    # ----------------------------------------------------------------------------

    w_nb = tixi.getNamedChildrenCount(
        "/cpacs/vehicles/aircraft\
                                      /model/wings",
        "wing",
    )

    # ----------------------------------------------------------------------------
    #  INITIALIZATION 1 -----------------------------------------------------------
    # ----------------------------------------------------------------------------

    ag.w_nb = w_nb
    ag.wing_nb = w_nb
    wing_plt_area_xz = []
    wing_plt_area_yz = []
    wingUID = []

    # ----------------------------------------------------------------------------
    #  COUNTING 2 -----------------------------------------------------------------
    #  Counting sections and segments----------------------------------------------
    # ----------------------------------------------------------------------------

    b = 0
    for i in range(1, w_nb + 1):
        double = 1
        ag.wing_sym.append(tigl.wingGetSymmetry(i))
        if ag.wing_sym[i - 1] != 0:
            double = 2  # To consider the real amount of wing
            # when they are defined using symmetry
            ag.wing_nb += 1
        ag.wing_sec_nb.append(tigl.wingGetSectionCount(i))
        ag.wing_seg_nb.append(tigl.wingGetSegmentCount(i))
        ag.wing_vol.append(tigl.wingGetVolume(i) * double)
        # x-y plane
        ag.wing_plt_area.append(tigl.wingGetReferenceArea(i, 1) * double)
        # x-z plane
        wing_plt_area_xz.append(tigl.wingGetReferenceArea(i, 2) * double)
        # y-z plane
        wing_plt_area_yz.append(tigl.wingGetReferenceArea(i, 3) * double)
        ag.wing_tot_vol = ag.wing_tot_vol + ag.wing_vol[i - 1]
        wingUID.append(tigl.wingGetUID(i))
        ag.wing_span.append(tigl.wingGetSpan(wingUID[i - 1]))
        a = np.amax(ag.wing_span)
        # Evaluating the index that corresponds to the main wing
        if a > b:
            ag.main_wing_index = i
            b = a

    #  Checking segment and section connection and reordering them
    (ag.wing_sec_nb, start_index, seg_sec, wing_sec_index) = check_segment_connection(
        wing_plt_area_xz, wing_plt_area_yz, ag, tigl
    )

    # ----------------------------------------------------------------------------
    #  INITIALIZATION 2 -----------------------------------------------------------
    # ----------------------------------------------------------------------------

    max_wing_sec_nb = np.amax(ag.wing_sec_nb)
    max_wing_seg_nb = np.amax(ag.wing_seg_nb)
    wing_center_section_point = np.zeros((max_wing_sec_nb, w_nb, 3))
    ag.wing_center_seg_point = np.zeros((max_wing_seg_nb, ag.wing_nb, 3))
    ag.wing_seg_vol = np.zeros((max_wing_seg_nb, w_nb))
    ag.wing_fuel_seg_vol = np.zeros((max_wing_seg_nb, w_nb))
    ag.wing_fuel_vol = 0
    ag.wing_mac = np.zeros((4, w_nb))
    ag.wing_sec_thicknes = np.zeros((max_wing_sec_nb + 1, w_nb))

    # ===========================================================================##
    # ----------------------------------------------------------------------------
    #  WING ANALYSIS --------------------------------------------------------------
    # ----------------------------------------------------------------------------
    # Main wing plantform area

    ag.wing_plt_area_main = ag.wing_plt_area[ag.main_wing_index - 1]

    #  Wing: MAC,chords,thicknes,span,plantform area ------------------------------

    for i in range(1, w_nb + 1):
        mac = tigl.wingGetMAC(wingUID[i - 1])
        (wpx, wpy, wpz) = tigl.wingGetChordPoint(i, 1, 0.0, 0.0)
        (wpx2, wpy2, wpz2) = tigl.wingGetChordPoint(i, 1, 0.0, 1.0)
        ag.wing_max_chord.append(
            np.sqrt((wpx2 - wpx) ** 2 + (wpy2 - wpy) ** 2 + (wpz2 - wpz) ** 2)
        )
        (wpx, wpy, wpz) = tigl.wingGetChordPoint(i, ag.wing_seg_nb[i - 1], 1.0, 0.0)
        (wpx2, wpy2, wpz2) = tigl.wingGetChordPoint(i, ag.wing_seg_nb[i - 1], 1.0, 1.0)
        ag.wing_min_chord.append(
            np.sqrt((wpx2 - wpx) ** 2 + (wpy2 - wpy) ** 2 + (wpz2 - wpz) ** 2)
        )
        for k in range(1, 5):
            ag.wing_mac[k - 1][i - 1] = mac[k - 1]
        for jj in range(1, ag.wing_seg_nb[i - 1] + 1):
            j = int(seg_sec[jj - 1, i - 1, 2])
            cle = tigl.wingGetChordPoint(i, j, 0.0, 0.0)
            ag.wing_seg_vol[j - 1][i - 1] = tigl.wingGetSegmentVolume(i, j)
            lp = tigl.wingGetLowerPoint(i, j, 0.0, 0.0)
            up = tigl.wingGetUpperPoint(i, j, 0.0, 0.0)
            if np.all(cle == lp):
                L = 0.25
            else:
                L = 0.75
            if np.all(cle == up):
                U = 0.25
            else:
                U = 0.75
            (wplx, wply, wplz) = tigl.wingGetLowerPoint(i, j, 0.0, L)
            (wpux, wpuy, wpuz) = tigl.wingGetUpperPoint(i, j, 0.0, U)
            wing_center_section_point[j - 1][i - 1][0] = (wplx + wpux) / 2
            wing_center_section_point[j - 1][i - 1][1] = (wply + wpuy) / 2
            wing_center_section_point[j - 1][i - 1][2] = (wplz + wpuz) / 2
            ag.wing_sec_thicknes[j - 1][i - 1] = np.sqrt(
                (wpux - wplx) ** 2 + (wpuy - wply) ** 2 + (wpuz - wplz) ** 2
            )
        j = int(seg_sec[ag.wing_seg_nb[i - 1] - 1, i - 1, 2])
        (wplx, wply, wplz) = tigl.wingGetLowerPoint(i, ag.wing_seg_nb[i - 1], 1.0, L)
        (wpux, wpuy, wpuz) = tigl.wingGetUpperPoint(i, ag.wing_seg_nb[i - 1], 1.0, U)
        ag.wing_sec_thicknes[j][i - 1] = np.sqrt(
            (wpux - wplx) ** 2 + (wpuy - wply) ** 2 + (wpuz - wplz) ** 2
        )
        wing_center_section_point[ag.wing_seg_nb[i - 1]][i - 1][0] = (wplx + wpux) / 2
        wing_center_section_point[ag.wing_seg_nb[i - 1]][i - 1][1] = (wply + wpuy) / 2
        wing_center_section_point[ag.wing_seg_nb[i - 1]][i - 1][2] = (wplz + wpuz) / 2
        ag.wing_sec_mean_thick.append(
            np.mean(ag.wing_sec_thicknes[0 : ag.wing_seg_nb[i - 1] + 1, i - 1])
        )
        # Evaluating wing fuel tank volume in the main wings
        if abs(round(ag.wing_plt_area[i - 1], 3) - ag.wing_plt_area_main) < 0.001:
            tp_ratio = ag.wing_min_chord[i - 1] / ag.wing_max_chord[i - 1]
            ratio = round(tp_ratio * ag.wing_plt_area[i - 1] / 100, 1)
            if ratio >= 1.0:
                ag.wing_fuel_vol = round(ag.wing_vol[i - 1] * 0.8, 2)
            elif ratio >= 0.5:
                ag.wing_fuel_vol = round(ag.wing_vol[i - 1] * 0.72, 2)
            else:
                ag.wing_fuel_vol = round(ag.wing_vol[i - 1] * 0.5, 2)
            for j in seg_sec[:, i - 1, 2]:
                if j == 0.0:
                    break
                ag.wing_fuel_seg_vol[int(j) - 1][i - 1] = round(
                    (ag.wing_seg_vol[int(j) - 1][i - 1] / (sum(ag.wing_vol))) * ag.wing_fuel_vol, 2
                )
        if (
            ag.wing_plt_area[i - 1] > wing_plt_area_xz[i - 1]
            and ag.wing_plt_area[i - 1] > wing_plt_area_yz[i - 1]
        ):
            ag.is_horiz.append(True)
            if ag.wing_sym[i - 1] != 0:
                ag.is_horiz.append(True)
        else:
            ag.is_horiz.append(False)
            if ag.wing_sym[i - 1] != 0:
                ag.is_horiz.append(False)

    # Wing segment length evaluatin function
    ag = get_wing_segment_length(ag, wing_center_section_point)

    # Evaluating the point at the center of each segment, the center
    # is placed at 1/4 of the chord, symmetry is considered.

    a = 0
    c = False
    for i in range(1, int(ag.wing_nb) + 1):
        if c:
            c = False
            continue
        for jj in range(1, ag.wing_seg_nb[i - a - 1] + 1):
            j = int(seg_sec[jj - 1, i - a - 1, 2])
            ag.wing_center_seg_point[j - 1][i - 1][0] = (
                wing_center_section_point[j - 1][i - a - 1][0]
                + wing_center_section_point[j][i - a - 1][0]
            ) / 2
            ag.wing_center_seg_point[j - 1][i - 1][1] = (
                wing_center_section_point[j - 1][i - a - 1][1]
                + wing_center_section_point[j][i - a - 1][1]
            ) / 2
            ag.wing_center_seg_point[j - 1][i - 1][2] = (
                wing_center_section_point[j - 1][i - a - 1][2]
                + wing_center_section_point[j][i - a - 1][2]
            ) / 2
        if ag.wing_sym[i - 1 - a] != 0:
            if ag.wing_sym[i - 1 - a] == 1:
                symy = 1
                symx = 1
                symz = -1
            if ag.wing_sym[i - 1 - a] == 2:
                symy = -1
                symx = 1
                symz = 1
            if ag.wing_sym[i - 1 - a] == 3:
                symy = 1
                symx = -1
                symz = 1
            ag.wing_center_seg_point[:, i, 0] = ag.wing_center_seg_point[:, i - 1, 0] * symx
            ag.wing_center_seg_point[:, i, 1] = ag.wing_center_seg_point[:, i - 1, 1] * symy
            ag.wing_center_seg_point[:, i, 2] = ag.wing_center_seg_point[:, i - 1, 2] * symz
            c = True
            a += 1

    ag.w_seg_sec = seg_sec
    tixi.save(cpacs_in)

    # log info display ------------------------------------------------------------
    log.info("---------------------------------------------")
    log.info("--------------- Wing Results ----------------")
    log.info("Number of Wings [-]: " + str(ag.wing_nb))
    log.info("Wing symmetry plane [-]: " + str(ag.wing_sym))
    log.info("Number of wing sections (not counting symmetry) [-]: " + str(ag.wing_sec_nb))
    log.info("Number of wing segments (not counting symmetry) [-]: " + str(ag.wing_seg_nb))
    log.info("Wing Span [m]: " + str(ag.wing_span))
    log.info(
        "Wing MAC length [m]: "
        + str(
            ag.wing_mac[
                0,
            ]
        )
    )
    log.info(
        "Wing MAC x,y,z coordinate [m]: \n"
        + str(
            ag.wing_mac[
                1:4,
            ]
        )
    )
    log.info("Wings sections thicknes [m]: " + str(ag.wing_sec_thicknes))
    log.info("Wings sections mean thicknes [m]: " + str(ag.wing_sec_mean_thick))
    log.info("Wing segments length [m]: " + str(ag.wing_seg_length))
    log.info("Wing max chord length [m]: " + str(ag.wing_max_chord))
    log.info("Wing min chord length [m]: " + str(ag.wing_min_chord))
    log.info("Main wing plantform area [m^2]: " + str(ag.wing_plt_area_main))
    log.info("Wings plantform area [m^2]: " + str(ag.wing_plt_area))
    log.info("Volume of each wing [m^3]: " + str(ag.wing_vol))
    log.info("Total wing volume [m^3]: " + str(ag.wing_tot_vol))
    log.info("Wing volume for fuel storage [m^3]: " + str(ag.wing_fuel_vol))
    log.info("---------------------------------------------")

    return ag
예제 #20
0
def generate_config_deformed_mesh(cpacs_path, cpacs_out_path, skip_config=False, skip_su2=False):
    """Function to generate all deform meshes with SU2 from CPACS data

    Function 'generate_config_deformed_mesh' reads data in the CPACS file
    and generate all the corresponding directory and config file which allow to
    generate deformed meshes.

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

    """

    tixi = open_tixi(cpacs_path)

    wkdir = get_results_directory("SU2Run")

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

    if wkdir in su2_mesh_path:
        log.info("The Baseline SU2 mesh is already in the working directory.")
    else:
        mesh_dir = os.path.join(wkdir, "MESH")
        if not os.path.isdir(mesh_dir):
            os.mkdir(mesh_dir)
        ac_name = aircraft_name(tixi)
        su2_mesh_new_path = os.path.join(mesh_dir, ac_name + "_baseline.su2")
        shutil.copyfile(su2_mesh_path, su2_mesh_new_path)
        tixi.updateTextElement(su2_mesh_xpath, su2_mesh_new_path)

    if not skip_config:

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

        if not control_surf:
            log.warning(
                "The CPACS file indicate that Control surface deflection should not be calculated!"
            )
            # active_ted_list = []
        else:

            ted_df = get_ted_list(tixi)

            # TODO: option to calculate only TED selected in cpacs
            # if ...
            #     active_ted_xpath = SU2_XPATH + '/options/....'
            #     # check element
            #     active_ted_list = get_string_vector(tixi,active_ted_xpath)
            # else: calculate all TED adn all deflections from CPACS
            #     active_ted_list = ted_list

            for i, row in ted_df.iterrows():

                # Unwrap TED data from the dataframe
                ted_uid = row["ted_uid"]
                wing_uid = row["wing_uid"]
                sym_dir = row["sym_dir"]
                defl_list = row["defl_list"]

                generate_mesh_def_config(tixi, wkdir, ted_uid, wing_uid, sym_dir, defl_list)

    if not skip_su2:

        run_mesh_deformation(tixi, wkdir)

    tixi.save(cpacs_out_path)
예제 #21
0
def cpacs_mbd_update(out, mw, bi, ms_zpm, out_xml):
    """ The function updates the cpacs file after the Weight_and_Balance
        program.

        INPUT
        (float) mass_pass   --Arg.: Passenger mass, countig also the
                                          extra mass.
        (class) out         --Arg.: BalanceOutput class.
        (class) mw          --Arg.: MassesWeights class.
        (class) bi          --Arg.: BalanceInput class.
        ##======= Classes are defined in the InputClasses folder =======##
        (cahr) out_xml      --Arg.: Path of the output file.

        OUTPUT
        (file) cpacs.xml --Out.: Updated cpacs file.
    """

    tixi = open_tixi(out_xml)

    # CREATING PATH ==========================================================
    MB_PATH = "/cpacs/vehicles/aircraft/" + "model/analyses/massBreakdown"

    MD_PATH = MB_PATH + "/designMasses"
    MTOM_PATH = MD_PATH + "/mTOM"

    MZFM_PATH = MD_PATH + "/mZFM"

    OEM_PATH = MB_PATH + "/mOEM/massDescription"
    J_PATH = OEM_PATH + "/massInertia/J"
    CG_PATH = OEM_PATH + "/location/"

    create_branch(tixi, MTOM_PATH + "/location/x", False)
    create_branch(tixi, MTOM_PATH + "/location/y", False)
    create_branch(tixi, MTOM_PATH + "/location/z", False)
    create_branch(tixi, MTOM_PATH + "/massInertia/Jxx", False)
    create_branch(tixi, MTOM_PATH + "/massInertia/Jyy", False)
    create_branch(tixi, MTOM_PATH + "/massInertia/Jzz", False)
    create_branch(tixi, MTOM_PATH + "/massInertia/Jxy", False)
    create_branch(tixi, MTOM_PATH + "/massInertia/Jyz", False)
    create_branch(tixi, MTOM_PATH + "/massInertia/Jxz", False)

    create_branch(tixi, MZFM_PATH + "/location/x", False)
    create_branch(tixi, MZFM_PATH + "/location/y", False)
    create_branch(tixi, MZFM_PATH + "/location/z", False)
    create_branch(tixi, MZFM_PATH + "/massInertia/Jxx", False)
    create_branch(tixi, MZFM_PATH + "/massInertia/Jyy", False)
    create_branch(tixi, MZFM_PATH + "/massInertia/Jzz", False)
    create_branch(tixi, MZFM_PATH + "/massInertia/Jxy", False)
    create_branch(tixi, MZFM_PATH + "/massInertia/Jyz", False)
    create_branch(tixi, MZFM_PATH + "/massInertia/Jxz", False)

    create_branch(tixi, OEM_PATH + "/location/x", False)
    create_branch(tixi, OEM_PATH + "/location/y", False)
    create_branch(tixi, OEM_PATH + "/location/z", False)
    create_branch(tixi, OEM_PATH + "/massInertia/Jxx", False)
    create_branch(tixi, OEM_PATH + "/massInertia/Jyy", False)
    create_branch(tixi, OEM_PATH + "/massInertia/Jzz", False)
    create_branch(tixi, OEM_PATH + "/massInertia/Jxy", False)
    create_branch(tixi, OEM_PATH + "/massInertia/Jyz", False)
    create_branch(tixi, OEM_PATH + "/massInertia/Jxz", False)

    # DESIGN MASSES ==========================================================
    # MTOM -------------------------------------------------------------------
    tixi.uIDSetToXPath(MTOM_PATH + "/location", "MTOMloc")

    tixi.updateDoubleElement(MTOM_PATH + "/location" + "/x",
                             out.center_of_gravity[0], "%g")
    tixi.updateDoubleElement(MTOM_PATH + "/location" + "/y",
                             out.center_of_gravity[1], "%g")
    tixi.updateDoubleElement(MTOM_PATH + "/location" + "/z",
                             out.center_of_gravity[2], "%g")

    tixi.updateDoubleElement(MTOM_PATH + "/massInertia" + "/Jxx", out.Ixx_lump,
                             "%g")
    tixi.updateDoubleElement(MTOM_PATH + "/massInertia" + "/Jyy", out.Iyy_lump,
                             "%g")
    tixi.updateDoubleElement(MTOM_PATH + "/massInertia" + "/Jzz", out.Izz_lump,
                             "%g")
    tixi.updateDoubleElement(MTOM_PATH + "/massInertia" + "/Jxy", out.Ixy_lump,
                             "%g")
    tixi.updateDoubleElement(MTOM_PATH + "/massInertia" + "/Jyz", out.Iyz_lump,
                             "%g")
    tixi.updateDoubleElement(MTOM_PATH + "/massInertia" + "/Jxz", out.Ixz_lump,
                             "%g")

    # MZFM -------------------------------------------------------------------
    add_uid(tixi, MZFM_PATH + "/location", "MZFMloc")

    # updating path
    tixi.updateDoubleElement(MZFM_PATH + "/location" + "/x", out.cg_zpm[0],
                             "%g")
    tixi.updateDoubleElement(MZFM_PATH + "/location" + "/y", out.cg_zpm[1],
                             "%g")
    tixi.updateDoubleElement(MZFM_PATH + "/location" + "/z", out.cg_zpm[2],
                             "%g")

    tixi.updateDoubleElement(MZFM_PATH + "/massInertia" + "/Jxx",
                             out.Ixx_lump_zfm, "%g")
    tixi.updateDoubleElement(MZFM_PATH + "/massInertia" + "/Jyy",
                             out.Iyy_lump_zfm, "%g")
    tixi.updateDoubleElement(MZFM_PATH + "/massInertia" + "/Jzz",
                             out.Izz_lump_zfm, "%g")
    tixi.updateDoubleElement(MZFM_PATH + "/massInertia" + "/Jxy",
                             out.Ixy_lump_zfm, "%g")
    tixi.updateDoubleElement(MZFM_PATH + "/massInertia" + "/Jyz",
                             out.Iyz_lump_zfm, "%g")
    tixi.updateDoubleElement(MZFM_PATH + "/massInertia" + "/Jxz",
                             out.Ixz_lump_zfm, "%g")

    # OEM ====================================================================
    add_uid(tixi, OEM_PATH + "/location", "OEMloc")

    tixi.updateDoubleElement((CG_PATH + "x"), out.cg_oem[0], "%g")
    tixi.updateDoubleElement((CG_PATH + "y"), out.cg_oem[1], "%g")
    tixi.updateDoubleElement((CG_PATH + "z"), out.cg_oem[2], "%g")
    tixi.updateDoubleElement((J_PATH + "xx"), out.Ixx_lump_oem, "%g")
    tixi.updateDoubleElement((J_PATH + "yy"), out.Iyy_lump_oem, "%g")
    tixi.updateDoubleElement((J_PATH + "zz"), out.Izz_lump_oem, "%g")
    tixi.updateDoubleElement((J_PATH + "xy"), out.Ixy_lump_oem, "%g")
    tixi.updateDoubleElement((J_PATH + "yz"), out.Iyz_lump_oem, "%g")
    tixi.updateDoubleElement((J_PATH + "xz"), out.Ixz_lump_oem, "%g")

    # ZPM INERTIA ============================================================
    B_PATH = "/cpacs/toolspecific/CEASIOMpy/balance"
    ZPM_PATH = B_PATH + "/mZPM"
    create_branch(tixi, ZPM_PATH + "/name", False)
    tixi.updateTextElement(ZPM_PATH + "/name", "Maximum no payload mass")
    create_branch(tixi, ZPM_PATH + "/description", False)
    tixi.updateTextElement(
        ZPM_PATH + "/description",
        "Maximum " + "no payload mass [kg], CoG coordinate [m] and " +
        "moment of inertia.",
    )
    create_branch(tixi, ZPM_PATH + "/mass", False)
    tixi.updateDoubleElement(ZPM_PATH + "/mass", ms_zpm, "%g")

    create_branch(tixi, ZPM_PATH + "/location/x", False)
    create_branch(tixi, ZPM_PATH + "/location/y", False)
    create_branch(tixi, ZPM_PATH + "/location/z", False)
    create_branch(tixi, ZPM_PATH + "/massInertia/Jxx", False)
    create_branch(tixi, ZPM_PATH + "/massInertia/Jyy", False)
    create_branch(tixi, ZPM_PATH + "/massInertia/Jzz", False)
    create_branch(tixi, ZPM_PATH + "/massInertia/Jxy", False)
    create_branch(tixi, ZPM_PATH + "/massInertia/Jyz", False)
    create_branch(tixi, ZPM_PATH + "/massInertia/Jxz", False)

    LOC_PATH = ZPM_PATH + "/location"
    MOI_PATH = ZPM_PATH + "/massInertia"

    add_uid(tixi, ZPM_PATH, "MZPM")
    add_uid(tixi, LOC_PATH, "MZPMloc")
    tixi.updateDoubleElement((LOC_PATH + "/x"), out.cg_zpm[0], "%g")
    tixi.updateDoubleElement((LOC_PATH + "/y"), out.cg_zpm[1], "%g")
    tixi.updateDoubleElement((LOC_PATH + "/z"), out.cg_zpm[2], "%g")
    tixi.updateDoubleElement((MOI_PATH + "/Jxx"), out.Ixx_lump_zpm, "%g")
    tixi.updateDoubleElement((MOI_PATH + "/Jyy"), out.Iyy_lump_zpm, "%g")
    tixi.updateDoubleElement((MOI_PATH + "/Jzz"), out.Izz_lump_zpm, "%g")
    tixi.updateDoubleElement((MOI_PATH + "/Jxy"), out.Ixy_lump_zpm, "%g")
    tixi.updateDoubleElement((MOI_PATH + "/Jyz"), out.Iyz_lump_zpm, "%g")
    tixi.updateDoubleElement((MOI_PATH + "/Jxz"), out.Ixz_lump_zpm, "%g")

    # USER CASE ==============================================================
    if bi.USER_CASE:
        UC_PATH = "/cpacs/toolspecific/CEASIOMpy/balance/userBalance"
        LOC_PATH = UC_PATH + "/location"
        MOI_PATH = UC_PATH + "/massInertia"

        create_branch(tixi, LOC_PATH + "/x", False)
        create_branch(tixi, LOC_PATH + "/y", False)
        create_branch(tixi, LOC_PATH + "/z", False)
        create_branch(tixi, MOI_PATH + "/Jxx", False)
        create_branch(tixi, MOI_PATH + "/Jyy", False)
        create_branch(tixi, MOI_PATH + "/Jzz", False)
        create_branch(tixi, MOI_PATH + "/Jxy", False)
        create_branch(tixi, MOI_PATH + "/Jyz", False)
        create_branch(tixi, MOI_PATH + "/Jxz", False)

        add_uid(tixi, LOC_PATH, "USERCase")
        tixi.updateDoubleElement((LOC_PATH + "/x"), out.cg_user[0], "%g")
        tixi.updateDoubleElement((LOC_PATH + "/y"), out.cg_user[1], "%g")
        tixi.updateDoubleElement((LOC_PATH + "/z"), out.cg_user[2], "%g")
        tixi.updateDoubleElement((MOI_PATH + "/Jxx"), out.Ixx_lump_user, "%g")
        tixi.updateDoubleElement((MOI_PATH + "/Jyy"), out.Iyy_lump_user, "%g")
        tixi.updateDoubleElement((MOI_PATH + "/Jzz"), out.Izz_lump_user, "%g")
        tixi.updateDoubleElement((MOI_PATH + "/Jxy"), out.Ixy_lump_user, "%g")
        tixi.updateDoubleElement((MOI_PATH + "/Jyz"), out.Iyz_lump_user, "%g")
        tixi.updateDoubleElement((MOI_PATH + "/Jxz"), out.Ixz_lump_user, "%g")

    # Saving and closing the new cpacs file inside the ToolOutput folder -----
    tixi.save(out_xml)

    return ()
예제 #22
0
def geom_eval(w_nb, awg, cpacs_in):
    """Main function to evaluate the wings geometry.

    Args:
        w_nb (integer): Number of wings [-].
        awg (class): AircraftWingGeometry class look at aircraft_geometry_class.py
                     in the classes folder for explanation.
        cpacs_in (str): Path to the CPACS file

    Returns:
        awg: AircraftWingGeometry class updated.
    """

    log.info("-----------------------------------------------------------")
    log.info("---------- Analysing wing geometry ------------------------")
    log.info("-----------------------------------------------------------")

    # Opening tixi and tigl
    tixi = open_tixi(cpacs_in)
    tigl = open_tigl(tixi)

    # INITIALIZATION 1 ---------------------------------------------------------
    awg.w_nb = w_nb
    awg.wing_nb = w_nb

    wing_plt_area_xz = []
    wing_plt_area_yz = []
    wingUID = []

    # Counting sections and segments--------------------------------------------

    for i in range(1, awg.w_nb + 1):
        double = 1
        awg.wing_sym.append(tigl.wingGetSymmetry(i))
        if awg.wing_sym[i - 1] != 0:
            double = 2  # To consider the real amount of wing
            # when they are defined using symmetry
            awg.wing_nb += 1
        awg.wing_sec_nb.append(tigl.wingGetSectionCount(i))
        awg.wing_seg_nb.append(tigl.wingGetSegmentCount(i))
        awg.wing_vol.append(tigl.wingGetVolume(i) * double)
        # x-y plane
        awg.wing_plt_area.append(tigl.wingGetReferenceArea(i, 1) * double)
        # x-z plane
        wing_plt_area_xz.append(tigl.wingGetReferenceArea(i, 2) * double)
        # y-z plane
        wing_plt_area_yz.append(tigl.wingGetReferenceArea(i, 3) * double)
        if (awg.wing_plt_area[i - 1] > wing_plt_area_xz[i - 1]
                and awg.wing_plt_area[i - 1] > wing_plt_area_yz[i - 1]):
            awg.is_horiz.append(True)
            if awg.wing_sym[i - 1] != 0:
                awg.is_horiz.append(True)
        else:
            awg.is_horiz.append(False)
            if awg.wing_sym[i - 1] != 0:
                awg.is_horiz.append(False)
        awg.wing_tot_vol += awg.wing_vol[i - 1]

    # Checking segment and section connection and reordering them
    (awg.wing_sec_nb, start_index, seg_sec,
     wing_sec_index) = check_segment_connection(wing_plt_area_xz,
                                                wing_plt_area_yz, awg, tigl)

    # INITIALIZATION 2 ---------------------------------------------------------

    max_wing_sec_nb = np.amax(awg.wing_sec_nb)
    max_wing_seg_nb = np.amax(awg.wing_seg_nb)
    wing_center_section_point = np.zeros((max_wing_sec_nb, awg.w_nb, 3))
    awg.wing_center_seg_point = np.zeros((max_wing_seg_nb, awg.wing_nb, 3))
    awg.wing_seg_vol = np.zeros((max_wing_seg_nb, awg.w_nb))
    awg.wing_mac = np.zeros((4, awg.w_nb))
    awg.wing_sec_thicknes = np.zeros((max_wing_sec_nb, awg.w_nb))

    # WING ANALYSIS ------------------------------------------------------------
    # Wing: MAC,chords,thicknes,span,plantform area ----------------------------

    b = 0
    for i in range(1, awg.w_nb + 1):
        wingUID.append(tigl.wingGetUID(i))
        mac = tigl.wingGetMAC(wingUID[i - 1])
        (wpx, wpy, wpz) = tigl.wingGetChordPoint(i, 1, 0.0, 0.0)
        (wpx2, wpy2, wpz2) = tigl.wingGetChordPoint(i, 1, 0.0, 1.0)
        awg.wing_max_chord.append(
            np.sqrt((wpx2 - wpx)**2 + (wpy2 - wpy)**2 + (wpz2 - wpz)**2))
        (wpx, wpy, wpz) = tigl.wingGetChordPoint(i, awg.wing_seg_nb[i - 1],
                                                 1.0, 0.0)
        (wpx2, wpy2, wpz2) = tigl.wingGetChordPoint(i, awg.wing_seg_nb[i - 1],
                                                    1.0, 1.0)
        awg.wing_min_chord.append(
            np.sqrt((wpx2 - wpx)**2 + (wpy2 - wpy)**2 + (wpz2 - wpz)**2))
        for k in range(1, 5):
            awg.wing_mac[k - 1][i - 1] = mac[k - 1]
        for jj in range(1, awg.wing_seg_nb[i - 1] + 1):
            j = int(seg_sec[jj - 1, i - 1, 2])
            cle = tigl.wingGetChordPoint(i, j, 0.0, 0.0)
            awg.wing_seg_vol[j - 1][i - 1] = tigl.wingGetSegmentVolume(i, j)
            lp = tigl.wingGetLowerPoint(i, j, 0.0, 0.0)
            up = tigl.wingGetUpperPoint(i, j, 0.0, 0.0)
            if np.all(cle == lp):
                L = 0.25
            else:
                L = 0.75
            if np.all(cle == up):
                U = 0.25
            else:
                U = 0.75
            (wplx, wply, wplz) = tigl.wingGetLowerPoint(i, j, 0.0, L)
            (wpux, wpuy, wpuz) = tigl.wingGetUpperPoint(i, j, 0.0, U)
            wing_center_section_point[j - 1][i - 1][0] = (wplx + wpux) / 2
            wing_center_section_point[j - 1][i - 1][1] = (wply + wpuy) / 2
            wing_center_section_point[j - 1][i - 1][2] = (wplz + wpuz) / 2
            awg.wing_sec_thicknes[j - 1][i - 1] = np.sqrt((wpux - wplx)**2 +
                                                          (wpuy - wply)**2 +
                                                          (wpuz - wplz)**2)
        j = int(seg_sec[awg.wing_seg_nb[i - 1] - 1, i - 1, 2])
        (wplx, wply, wplz) = tigl.wingGetLowerPoint(i, awg.wing_seg_nb[i - 1],
                                                    1.0, L)
        (wpux, wpuy, wpuz) = tigl.wingGetUpperPoint(i, awg.wing_seg_nb[i - 1],
                                                    1.0, U)
        awg.wing_sec_thicknes[j][i - 1] = np.sqrt((wpux - wplx)**2 +
                                                  (wpuy - wply)**2 +
                                                  (wpuz - wplz)**2)
        wing_center_section_point[awg.wing_seg_nb[i -
                                                  1]][i -
                                                      1][0] = (wplx + wpux) / 2
        wing_center_section_point[awg.wing_seg_nb[i -
                                                  1]][i -
                                                      1][1] = (wply + wpuy) / 2
        wing_center_section_point[awg.wing_seg_nb[i -
                                                  1]][i -
                                                      1][2] = (wplz + wpuz) / 2
        awg.wing_sec_mean_thick.append(
            np.mean(awg.wing_sec_thicknes[0:awg.wing_seg_nb[i - 1] + 1,
                                          i - 1]))
        # Wing Span Evaluation, Considering symmetry
        awg.wing_span.append(round(tigl.wingGetSpan(wingUID[i - 1]), 3))
        a = np.amax(awg.wing_span)
        # Evaluating the index that corresponds to the main wing
        if a > b:
            awg.main_wing_index = i
            b = a

    # Main wing plantform area
    awg.wing_plt_area_main = awg.wing_plt_area[awg.main_wing_index - 1]
    # Wing segment length evaluatin function
    awg = getwingsegmentlength(awg, wing_center_section_point)
    awg.w_seg_sec = seg_sec

    # Wings wetted area
    for i in range(1, awg.w_nb + 1):
        a = str(wingUID[i - 1])
        s = tigl.wingGetSurfaceArea(i)
        if awg.wing_sym[i - 1] != 0:
            s *= 2
        if i == awg.main_wing_index:
            awg.main_wing_surface = s
        else:
            awg.tail_wings_surface.append(s)
        awg.total_wings_surface += s

    # Evaluating the point at the center of each segment, the center
    # is placed at 1/4 of the chord, symmetry is considered.
    a = 0
    c = False
    for i in range(1, int(awg.wing_nb) + 1):
        if c:
            c = False
            continue
        for jj in range(1, awg.wing_seg_nb[i - a - 1] + 1):
            j = int(seg_sec[jj - 1, i - a - 1, 2])
            awg.wing_center_seg_point[j - 1][
                i - 1][0] = (wing_center_section_point[j - 1][i - a - 1][0] +
                             wing_center_section_point[j][i - a - 1][0]) / 2
            awg.wing_center_seg_point[j - 1][
                i - 1][1] = (wing_center_section_point[j - 1][i - a - 1][1] +
                             wing_center_section_point[j][i - a - 1][1]) / 2
            awg.wing_center_seg_point[j - 1][
                i - 1][2] = (wing_center_section_point[j - 1][i - a - 1][2] +
                             wing_center_section_point[j][i - a - 1][2]) / 2
        if awg.wing_sym[i - 1 - a] != 0:
            if awg.wing_sym[i - 1 - a] == 1:
                symy = 1
                symx = 1
                symz = -1
            if awg.wing_sym[i - 1 - a] == 2:
                symy = -1
                symx = 1
                symz = 1
            if awg.wing_sym[i - 1 - a] == 3:
                symy = 1
                symx = -1
                symz = 1
            awg.wing_center_seg_point[:, i,
                                      0] = awg.wing_center_seg_point[:, i - 1,
                                                                     0] * symx
            awg.wing_center_seg_point[:, i,
                                      1] = awg.wing_center_seg_point[:, i - 1,
                                                                     1] * symy
            awg.wing_center_seg_point[:, i,
                                      2] = awg.wing_center_seg_point[:, i - 1,
                                                                     2] * symz
            c = True
            a += 1

    tixi.save(cpacs_in)

    # log info display ------------------------------------------------------------
    log.info("-----------------------------------------------------------")
    log.info("---------- Wing Results -----------------------------------")
    log.info("Number of Wings [-]: " + str(awg.wing_nb))
    log.info("Wing symmetry plane [-]: " + str(awg.wing_sym))
    log.info("Number of wing sections (not counting symmetry) [-]: " +
             str(awg.wing_sec_nb))
    log.info("Number of wing segments (not counting symmetry) [-]: " +
             str(awg.wing_seg_nb))
    log.info("Wing Span (counting symmetry)[m]: \n" + str(awg.wing_span))
    log.info("Wing MAC length [m]: " + str(awg.wing_mac[0, ]))
    log.info("Wing MAC x,y,z coordinate [m]: \n" + str(awg.wing_mac[1:4, ]))
    log.info("Wings sections thicknes [m]: \n" + str(awg.wing_sec_thicknes))
    log.info("Wings sections mean thicknes [m]: \n" +
             str(awg.wing_sec_mean_thick))
    log.info("Wing segments length [m]: \n" + str(awg.wing_seg_length))
    log.info("Wing max chord length [m]: \n" + str(awg.wing_max_chord))
    log.info("Wing min chord length [m]: \n" + str(awg.wing_min_chord))
    log.info("Main wing plantform area [m^2]: " + str(awg.wing_plt_area_main))
    log.info("Main wing wetted surface [m^2]: " + str(awg.main_wing_surface))
    log.info("Tail wings wetted surface [m^2]: \n" +
             str(awg.tail_wings_surface))
    log.info("Total wings wetted surface [m^2]: \n" +
             str(awg.total_wings_surface))
    log.info("Wings plantform area [m^2]: \n" + str(awg.wing_plt_area))
    log.info("Volume of each wing [m^3]: " + str(awg.wing_vol))
    log.info("Total wing volume [m^3]: " + str(awg.wing_tot_vol))
    log.info("-----------------------------------------------------------")

    return awg
예제 #23
0
def cpacs_weight_update(out, mw, ui, cpacs_out_path):
    """ The function that update the cpacs file after the Weight_unc_main
        program.

    Args:
        out (class): Output class.
        mw (class): Mass and weight class.
        ui (class): UserInputs class.
        cpacs_out_path (str): Path of the output file.

    """

    tixi = open_tixi(cpacs_out_path)

    # Path definition
    MB_PATH = "/cpacs/vehicles/aircraft/model/analyses/massBreakdown"

    if tixi.checkElement(MB_PATH):
        tixi.removeElement(MB_PATH)

    MD_PATH = MB_PATH + "/designMasses"
    MTOM_PATH = MD_PATH + "/mTOM"
    MZFM_PATH = MD_PATH + "/mZFM"
    MF_PATH = MB_PATH + "/fuel/massDescription"
    OEM_PATH = MB_PATH + "/mOEM/massDescription"
    PAY_PATH = MB_PATH + "/payload/massDescription"
    MC_PATH = MB_PATH + "/payload/mCargo"
    EM_PATH = MB_PATH + "/mOEM/mEM"
    OIM_PATH = MB_PATH + "/mOEM/mOperatorItems/mCrewMembers/massDescription"
    MSYS_PATH = EM_PATH + "/mSystems/massDescription/mass"
    MSTR_PATH = EM_PATH + "/mStructure/massDescription/mass"
    MEN_PATH = EM_PATH + "/mPowerUnits/massDescription/mass"

    create_branch(tixi, MTOM_PATH + "/mass", False)
    create_branch(tixi, MZFM_PATH + "/mass", False)
    create_branch(tixi, MF_PATH + "/mass", False)
    create_branch(tixi, OEM_PATH + "/mass", False)
    create_branch(tixi, PAY_PATH + "/mass", False)
    create_branch(tixi, MC_PATH, False)
    create_branch(tixi, OIM_PATH + "/mass", False)
    create_branch(tixi, EM_PATH, False)
    create_branch(tixi, MSYS_PATH, False)
    create_branch(tixi, MSTR_PATH, False)
    create_branch(tixi, MEN_PATH, False)

    # DESIGN MASSES
    add_uid(tixi, MTOM_PATH, "MTOM")
    tixi.createElement(MTOM_PATH, "name")
    tixi.updateTextElement(MTOM_PATH + "/name", "Maximum take-off mass")
    tixi.createElement(MTOM_PATH, "description")
    tixi.updateTextElement(
        MTOM_PATH + "/description",
        "Maximum " + "take off mass [kg], CoG coordinate [m] and " +
        "moment of inertia.",
    )
    tixi.updateDoubleElement(MTOM_PATH + "/mass", mw.maximum_take_off_mass,
                             "%g")

    # MZFM
    add_uid(tixi, MZFM_PATH, "MZFM")
    tixi.createElement(MZFM_PATH, "name")
    tixi.updateTextElement(MZFM_PATH + "/name", "Maximum zero fuel mass")
    tixi.createElement(MZFM_PATH, "description")
    tixi.updateTextElement(
        MZFM_PATH + "/description",
        "Maximum " + "zero fuel mass [kg] and corresponding CoG " +
        "coordinate [m], moment of inertia.",
    )
    tixi.updateDoubleElement(MZFM_PATH + "/mass", mw.zero_fuel_mass, "%g")

    # FUEL MASS
    add_uid(tixi, MF_PATH, "MFM")
    tixi.createElement(MF_PATH, "name")
    tixi.updateTextElement(MF_PATH + "/name", "Max fuel mass")
    tixi.createElement(MF_PATH, "description")
    tixi.updateTextElement(MF_PATH + "/description", "Maximum fuel mass [kg].")
    tixi.updateDoubleElement(MF_PATH + "/mass", mw.mass_fuel_max, "%g")

    # OEM
    add_uid(tixi, OEM_PATH, "OEM")
    tixi.createElement(OEM_PATH, "name")
    tixi.updateTextElement(OEM_PATH + "/name", "Operating empty mass")
    tixi.createElement(OEM_PATH, "description")
    tixi.updateTextElement(
        OEM_PATH + "/description",
        "Operating empty" + " mass [kg] and related inertia [kgm^2].")
    tixi.updateDoubleElement(OEM_PATH + "/mass", mw.operating_empty_mass, "%g")

    tixi.updateDoubleElement(OIM_PATH + "/mass", mw.mass_crew, "%g")
    add_uid(tixi, OIM_PATH, "massCrew")
    tixi.updateDoubleElement(MSYS_PATH, mw.mass_systems, "%g")
    add_uid(tixi, EM_PATH + "/mSystems/massDescription", "mSys")
    tixi.updateDoubleElement(MSTR_PATH, mw.mass_structure, "%g")
    add_uid(tixi, EM_PATH + "/mStructure/massDescription", "mStrt")
    tixi.updateDoubleElement(MEN_PATH, mw.mass_engines, "%g")
    add_uid(tixi, EM_PATH + "/mPowerUnits/massDescription", "mEng")

    # PAYLOAD MASS AND FUEL CARGO MASS =======================================
    add_uid(tixi, PAY_PATH, "MPM")
    tixi.createElement(PAY_PATH, "name")
    tixi.updateTextElement(PAY_PATH + "/name", "Max payload mass")
    tixi.createElement(PAY_PATH, "description")
    tixi.updateTextElement(PAY_PATH + "/description",
                           "Maximum payload mass [kg].")
    tixi.updateDoubleElement(PAY_PATH + "/mass", mw.mass_payload, "%g")
    tixi.createElement(MC_PATH, "massCargo")
    tixi.updateDoubleElement(MC_PATH + "/massCargo", ui.MASS_CARGO, "%g")

    tixi.save(cpacs_out_path)

    return ()
예제 #24
0
def fuselage_inertia(SPACING, center_of_gravity, mass_seg_i, afg, cpacs_in):
    """Thefunction evaluates the inertia of the fuselage using the lumped
       masses method.

       INPUT
       (float) SPACING  --Arg.: Maximum distance between fuselage nodes [m].
       (float_array) center_of_gravity --Arg.: x,y,z coordinates of the CoG.
       (float_array) mass_seg_i        --Arg.: Mass of each segment of each
                                               component of the aircraft.
       (class) afg      --Arg.: AircraftFuseGeometry class.
        ##======= Class is defined in the InputClasses folder =======##
       (char) cpacs_in --Arg.: Cpacs xml file location.

       OUTPUT
       (float) sfx --Out.: Lumped nodes x-coordinate [m].
       (float) sfy --Out.: Lumped nodes y-coordinate [m].
       (float) sfz --Out.: Lumped nodes z-coordinate [m].
       (float) Ixx --Out.: Moment of inertia respect to the x-axis [kgm^2].
       (float) Iyy --Out.: Moment of inertia respect to the y-axis [kgm^].
       (float) Izz --Out.: Moment of inertia respect to the z-axis [kgm^2].
    """

    tixi = open_tixi(cpacs_in)
    tigl = open_tigl(tixi)

    sfx = []
    sfy = []
    sfz = []
    Ixx = 0
    Iyy = 0
    Izz = 0
    Ixy = 0
    Iyz = 0
    Ixz = 0
    log.info("-------------------------------------------------------------")
    log.info("---- Evaluating fuselage nodes for lumped masses inertia ----")
    log.info("-------------------------------------------------------------")
    for f in range(1, afg.fus_nb + 1):
        for i in afg.f_seg_sec[:, f - 1, 2]:
            fx = []
            fy = []
            fz = []
            # Number of subdivisions along the longitudinal axis
            subd_l = math.ceil(
                (afg.fuse_seg_length[int(i) - 1][f - 1] / SPACING))
            # Number of subdivisions along the perimeter
            SUBD_C0 = math.ceil(
                (afg.fuse_sec_per[int(i) - 1][f - 1] / SPACING))
            # Number of subdivisions along the radial axis
            subd_r = math.ceil(
                ((afg.fuse_sec_width[int(i) - 1][f - 1] / 2) / SPACING))
            if subd_l == 0:
                subd_l = 1.0
            if SUBD_C0 == 0:
                SUBD_C0 = 1.0
            if subd_r == 0:
                subd_r = 1.0
            eta = 1.0 / (subd_l)
            zeta = 1.0 / (SUBD_C0)
            D0 = np.sqrt(np.arange(subd_r * SUBD_C0) / float(subd_r * SUBD_C0))
            D = np.array([t for t in (D0 - (D0[-1] - 0.98)) if not t < 0])
            (xc, yc, zc) = afg.fuse_center_section_point[int(i) - 1][f - 1][:]
            for j in range(int(subd_l) + 1):
                et = j * eta
                for k in range(int(SUBD_C0) + 1):
                    ze = k * zeta
                    (x0, y0, z0) = tigl.fuselageGetPoint(f, int(i), et, ze)
                    fx.append(x0)
                    fy.append(y0)
                    fz.append(z0)
                    sfx.append(x0)
                    sfy.append(y0)
                    sfz.append(z0)
                if subd_r > 0.0:
                    deltar = np.sqrt((y0 - yc)**2 + (z0 - zc)**2) * D
                    theta = np.pi * (3 - np.sqrt(5)) * np.arange(len(D))
                    x = np.zeros(np.shape(deltar)) + x0
                    y = yc + deltar * np.cos(theta)
                    z = zc + deltar * np.sin(theta)
                    fx.extend(x)
                    fy.extend(y)
                    fz.extend(z)
                    sfx.extend(x)
                    sfy.extend(y)
                    sfz.extend(z)
            M = mass_seg_i[int(i) - 1, f - 1] / np.max(np.shape(fx))
            fcx = fx - (np.zeros((np.shape(fx))) + center_of_gravity[0])
            fcy = fy - (np.zeros((np.shape(fx))) + center_of_gravity[1])
            fcz = fz - (np.zeros((np.shape(fx))) + center_of_gravity[2])
            Ixx += np.sum(M * np.add(fcy**2, fcz**2))
            Iyy += np.sum(M * np.add(fcx**2, fcz**2))
            Izz += np.sum(M * np.add(fcx**2, fcy**2))
            Ixy += np.sum(M * fcx * fcy)
            Iyz += np.sum(M * fcy * fcz)
            Ixz += np.sum(M * fcx * fcz)

    return (sfx, sfy, sfz, Ixx, Iyy, Izz, Ixy, Iyz, Ixz)
예제 #25
0
def create_SU2_mesh(cpacs_path, cpacs_out_path):
    """Function to create a simple SU2 mesh form an SUMO file (.smx)

    Function 'create_mesh' is used to generate an unstructured mesh with  SUMO
    (which integrage Tetgen for the volume mesh) using a SUMO (.smx) geometry
    file as input.
    Meshing option could be change manually (only in the script for now)

    Source :
        * sumo help, tetgen help (in the folder /doc)

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

    """

    tixi = open_tixi(cpacs_path)

    sumo_dir = get_results_directory("SUMOAutoMesh")
    su2_mesh_path = os.path.join(sumo_dir, "ToolOutput.su2")

    sumo_file_xpath = "/cpacs/toolspecific/CEASIOMpy/filesPath/sumoFilePath"
    sumo_file_path = get_value_or_default(tixi, sumo_file_xpath, "")
    if sumo_file_path == "":
        raise ValueError("No SUMO file to use to create a mesh")

    # Set mesh parameters
    log.info("Mesh parameter will be set")
    refine_level_xpath = "/cpacs/toolspecific/CEASIOMpy/mesh/sumoOptions/refinementLevel"
    refine_level = get_value_or_default(tixi, refine_level_xpath, 0.0)
    log.info("Refinement level is {}".format(refine_level))
    add_mesh_parameters(sumo_file_path, refine_level)

    # Check current Operating System
    current_os = platform.system()

    if current_os == "Darwin":
        log.info("Your OS is Mac\n\n")
        log.info(
            "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
        log.info("On MacOS the mesh has to be generated manually.")
        log.info("To create a SU2Mesh you have to :")
        log.info("Open the .smx geometry that you will find there:")
        log.info(sumo_file_path)
        log.info('Click on the button "Mesh"')
        log.info('Click on "Create Mesh"')
        log.info('Click on "Volume Mesh"')
        log.info('Click on "Run"')
        log.info('When the mesh generation is completed, click on "Close"')
        log.info('Go to the Menu "Mesh" -> "Save volume mesh..."')
        log.info('Chose "SU2 (*.su2)" as File Type"')
        log.info("Copy/Paste the following line as File Name")
        log.info(su2_mesh_path)
        log.info('Click on "Save"')
        log.info("You can now close SUMO, your workflow will continue.")
        log.info(
            "More information:"
            "https://ceasiompy.readthedocs.io/en/latest/user_guide/modules/SUMOAutoMesh/index.html"
        )
        log.info(
            "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n"
        )

        # For now, I did not find a way to run "sumo -batch" on Mac...
        # The command just open SUMO GUI, the mesh has to be generate and save manually
        with change_working_dir(sumo_dir):
            command = ["open", "/Applications/SUMO/dwfsumo.app/"]

        # /Applications/SUMO/dwfsumo.app/Contents/MacOS/dwfsumo
        # -batch output=su2 -tetgen-options=pq1.16VY ToolOutput.smx
        os.system(" ".join(command))
        input("Press ENTER to continue...")

    elif current_os == "Linux":
        log.info("Your OS is Linux")

        # Check if SUMO is installed
        soft_dict = get_install_path(["sumo"])

        # Run SUMO in batch
        output = "-output=su2"
        options = "-tetgen-options=pq1.16VY"  # See Tetgen help for more options
        # Command line to run: sumo -batch -output=su2 -tetgen-options=pq1.16VY ToolOutput.smx
        command = [
            soft_dict["sumo"], "-batch", output, options, sumo_file_path
        ]

        with change_working_dir(sumo_dir):
            os.system(" ".join(command))

    elif current_os == "Windows":
        log.info("Your OS is Windows")
        # TODO: develop this part

        log.warning("OS not supported yet by SUMOAutoMesh!")
        raise NotImplementedError("OS not supported yet!")

    else:
        raise OSError("OS not recognize!")

    # Copy the mesh in the MESH directory
    su2_mesh_name = aircraft_name(tixi) + "_baseline.su2"
    su2_mesh_new_path = os.path.join(sumo_dir, su2_mesh_name)
    shutil.copyfile(su2_mesh_path, su2_mesh_new_path)

    if os.path.isfile(su2_mesh_new_path):
        log.info("An SU2 Mesh has been correctly generated.")
        create_branch(tixi, SU2MESH_XPATH)
        tixi.updateTextElement(SU2MESH_XPATH, su2_mesh_new_path)
        os.remove(su2_mesh_path)

    else:
        raise ValueError("No SU2 Mesh file has been generated!")

    tixi.save(cpacs_out_path)