from dlubal.api import rfem
from dlubal.api import common
from math import inf
import numpy as np

#function for coupling distances
def generate_couplings(l):
    step_desired = 0.1            #0.1m desired distance of couplings
    n = max(1, round(l/step_desired))     #Number of Couplings

    step = l/n              #real distance of couplings

    x_couplings = [i*step for i in range(n+1)]

    x_couplings = list(dict.fromkeys(x_couplings)) #remove duplicates

    return x_couplings


#function for connector distances
def generate_connectors(l, distance1, distance2, distance3, offset_left, offset_right):
    x1 = l/4
    x2 = 3*l/4
    start_offset = offset_left*distance1
    end_offset = offset_right*distance3
    x_connectors = []

    def generate_zone(x_start, x_end, target_spacing):
        length = x_end - x_start
        # calculate number of distances
        n = max(1, round(length / target_spacing))
        return list(np.linspace(x_start, x_end, n + 1))

    # area 1
    x_connectors += generate_zone(start_offset, x1, distance1)
    # area 2
    x_connectors += generate_zone(x1, x2, distance2)[1:]
    # area 3
    x_connectors += generate_zone(x2, l-end_offset, distance3)[1:]

    spacings = [round(float(x_connectors[i+1] - x_connectors[i]), 5) for i in range(len(x_connectors)-1)]

    edge_left = round(x_connectors[0],5)
    edge_right = round(l - x_connectors[-1],5)

    spacings = [float(x_connectors[i+1] - x_connectors[i]) for i in range(len(x_connectors)-1)]

    return x_connectors, spacings, edge_left, edge_right, x1, x2

#wrapper for coupling distances
def couplings_multiple_spans(feldlängen):
    x_couplings_global = []
    x_offset = 0.0

    for l in feldlängen:
        x_local = generate_couplings(l)

        #shift local coords
        x_shifted = [x + x_offset for x in x_local]

        #avoid doublings
        if x_couplings_global:
            x_shifted = x_shifted[1:]

        x_couplings_global += x_shifted
        x_offset += l

    return x_couplings_global


#wrapper for connector distances
def connectors_multiple_spans(feldlängen, distance1, distance2, distance3, offset_left, offset_right):
    x_connectors_global = []
    x_offset = 0.0
    x_connector_results = []

    for l in feldlängen:
        x_local, spacings, edge_left, edge_right, x1, x2 = generate_connectors(l, distance1, distance2, distance3, offset_left, offset_right)

        x_connector_results.append({"Feld": len(x_connector_results)+1,
                                    "x": x_local,
                                    "Abstände": spacings,
                                    "Rand links": edge_left,
                                    "Rand rechts": edge_right,
                                    "x1": x1,
                                    "x2": x2})

        #shift local coords
        x_shifted = [x + x_offset for x in x_local]

        #avoid doublings
        if x_connectors_global:
            if abs(x_shifted[0] - x_offset) < 1e-6:
                x_shifted = x_shifted[1:]

        x_connectors_global += x_shifted
        x_offset += l

    return x_connectors_global, x_connector_results

#function for generation of materials and members
def generiere_Materialien_und_Querschnitte(Holzklasse, b_ug_cm, h_ug_cm, Betonklasse, b_og_cm, h_og_cm, K_ser):
    list_of_sections_materials = []

    b_ug=b_ug_cm/100.0          #convert to m
    h_ug=h_ug_cm/100.0
    b_og=b_og_cm/100.0
    h_og=h_og_cm/100.0

    #create materials
    material_id = 1

    material=rfem.structure_core.Material(no = material_id, name = Holzklasse)
    list_of_sections_materials.append(material)
    material_id += 1

    material=rfem.structure_core.Material(no = material_id, name = Betonklasse)
    list_of_sections_materials.append(material)
    material_id += 1

    #connectors, basic setting = ULS (2/3 K_ser)
    material = rfem.types_for_members.MemberDefinableStiffness(no = 1,
                                                                torsional_stiffness = 0.0,
                                                                bending_stiffness_y = 2/3*(K_ser*((h_og/2)**3 + (h_ug/2)**3)/3)*1e3,
                                                                bending_stiffness_z = 2/3*(K_ser*((h_og/2)**3 + (h_ug/2)**3)/3)*1e3,
                                                                axial_stiffness = 100,
                                                                shear_stiffness_y = 1.000e+16,
                                                                shear_stiffness_z = 1.000e+16,
                                                                specific_weight = 0.0,
                                                                section_area = 0.0,
                                                                rotation = 0.0,
                                                                thermal_expansion_alpha = 0.0,
                                                                thermal_expansion_width = 0.0,
                                                                thermal_expansion_height = 0.0)
    list_of_sections_materials.append(material)
    material_id += 1

    #create cross-sections
    section_id=1

    section=rfem.structure_core.Section(no=section_id, material=1, name=f'R_M1 {b_ug}/{h_ug}')
    list_of_sections_materials.append(section)
    section_id+=1

    section=rfem.structure_core.Section(no=section_id, material=2, name=f'R_M1 {b_og}/{h_og}')
    list_of_sections_materials.append(section)
    section_id+=1

    #create hinge
    hinge = rfem.types_for_members.MemberHinge (no = 1,
                                                moment_release_my = 0.0, axial_release_n = inf,
                                                axial_release_vz = inf)
    list_of_sections_materials.append(hinge)

    return list_of_sections_materials

#function for model generation
def generiere_Stabwerksmodell(feldlängen, h_og_cm, h_ug_cm, VM_Abstand_1, VM_Abstand_2, VM_Abstand_3, offset_left, offset_right):
    list_of_objects = []
    feldgrenzen = np.cumsum(feldlängen)
    x_positions = [0.0] + list(feldgrenzen)

    line_id = 1

    h_ug=h_ug_cm/100.0          #convert to m
    h_og=h_og_cm/100.0

    node_cache = {}             #avoid doublings for nodes
    node_id = 1
    def get_or_create_node(x, y, z, line_id=None, start_id=None):
        nonlocal node_id
        if start_id is not None and node_id<start_id:
            node_id = start_id

        key = (round(x, 6), round(y, 6), round(z, 6))

        if key in node_cache:
            return node_cache[key]

        node = rfem.structure_core.Node(no=node_id,
            coordinate_1=x,
            coordinate_2=y,
            coordinate_3=z,
            on_line_reference_line=line_id)
        list_of_objects.append(node)
        node_cache[key] = node_id
        node_id += 1
        return node_cache[key]

    #x_coords of couplings
    x_couplings = couplings_multiple_spans(feldlängen)

    #x_coords of connectors
    distance1 = float(VM_Abstand_1)/100
    distance2 = float(VM_Abstand_2)/100
    distance3 = float(VM_Abstand_3)/100

    x_connectors, _ = connectors_multiple_spans(feldlängen, distance1, distance2, distance3, offset_left, offset_right)


    #timber nodes on span borders
    timber_nodes = []
    for x in x_positions:
        node = get_or_create_node(x, 0.0, 0.0,
                                  line_id = None,
                                  start_id = 1)
        timber_nodes.append(node)

    #concrete nodes on span borders
    concrete_nodes = []
    for x in x_positions:
        node = get_or_create_node(x, 0.0, (h_ug+h_og)/2,
                                  line_id = None,
                                  start_id = node_id)
        concrete_nodes.append(node)


    #create concrete and timber lines
    timber_lines = []
    for i in range(len(timber_nodes)-1):
        line = rfem.structure_core.Line(no = line_id, definition_nodes = [timber_nodes[i], timber_nodes[i+1]])
        list_of_objects.append(line)
        timber_lines.append(line_id)
        line_id += 1

    def find_timber_line(x):
        for i in range(len(x_positions)-1):
            if x_positions[i] <= x <= x_positions[i+1]:
                return timber_lines[i]
        return timber_lines[-1]

    concrete_lines = []
    for i in range(len(concrete_nodes)-1):
        line = rfem.structure_core.Line(no = line_id, definition_nodes = [concrete_nodes[i], concrete_nodes[i+1]])
        list_of_objects.append(line)
        concrete_lines.append(line_id)
        line_id+=1

    def find_concrete_line(x):
        for i in range(len(x_positions)-1):
            if x_positions[i] <= x <= x_positions[i+1]:
                return concrete_lines[i]
        return concrete_lines[-1]

    #create nodes for couplings
    node_id = 1000
    coupling_node_pairs = [(timber_nodes[0], concrete_nodes[0]),
                           (timber_nodes[-1], concrete_nodes[-1])]

    for x in x_couplings[1:-1]:
            line_id_timber = find_timber_line(x)
            coupling_node_timber = get_or_create_node(x, 0.0, 0.0, line_id = line_id_timber)

            line_id_concrete = find_concrete_line(x)
            coupling_node_concrete = get_or_create_node(x, 0.0, (h_ug+h_og)/2, line_id = line_id_concrete)

            coupling_node_pairs.append((coupling_node_timber, coupling_node_concrete))

    #create nodes for connectors
    node_id = 10000                 #separation
    connector_node_pairs = []        #list with sets of nodes
    for x in x_connectors:

        line_id_timber = find_timber_line(x)
        node_timber = get_or_create_node(x, 0.0, 0.0, line_id = line_id_timber)

        node_joint = get_or_create_node(x, 0.0, h_ug/2)

        line_id_concrete = find_concrete_line(x)
        node_concrete = get_or_create_node(x, 0.0, (h_ug + h_og)/2, line_id = line_id_concrete)

        connector_node_pairs.append((node_timber, node_joint, node_concrete))

    #create coupling lines
    line_id = 1000            #separation
    for ug_node, og_node in coupling_node_pairs:

         line = rfem.structure_core.Line(no = line_id, definition_nodes = [ug_node, og_node])
         list_of_objects.append(line)
         line_id+=1

    #create connector lines
    line_id = 10000         #separation
    connector_lines = []
    for node_timber, node_joint, node_concrete in connector_node_pairs:

         #lower part of screw
         line = rfem.structure_core.Line(no = line_id,
                                         definition_nodes = [node_timber, node_joint])
         list_of_objects.append(line)
         connector_lines.append(line_id)
         line_id += 1

         #upper part of screw
         line = rfem.structure_core.Line(no = line_id,
                                         definition_nodes = [node_joint, node_concrete])
         list_of_objects.append(line)
         connector_lines.append(line_id)
         line_id += 1


    #create concrete and timber members
    member_id = 1

    for line_id in timber_lines:
        member = rfem.structure_core.Member(no = member_id, line = line_id, section_start = 1)
        list_of_objects.append(member)
        member_id += 1

    concrete_members = []
    for line_id in concrete_lines:
        member = rfem.structure_core.Member(no = member_id, line = line_id, section_start = 2)
        list_of_objects.append(member)
        concrete_members.append(member_id)
        member_id += 1

    #create coupling members
    member_id = 1000
    line_id_start = 1000
    for i in range(len(x_couplings)):
        member = rfem.structure_core.Member(no=member_id, line=line_id_start+i, type=rfem.structure_core.Member.TYPE_COUPLING_HINGE_HINGE)
        list_of_objects.append(member)
        member_id += 1

    #create connector members
    member_id = 10000
    connector_members = []
    for i, line_id in enumerate(connector_lines):

        if i % 2 == 0:
            #lower part of screw
            member = rfem.structure_core.Member(no = member_id, line = line_id, type = rfem.structure_core.Member.TYPE_DEFINABLE_STIFFNESS,
                                                member_type_definable_stiffness = 1)

        else:
            #upper part of screw with hinge
            member = rfem.structure_core.Member(no = member_id, line = line_id, type = rfem.structure_core.Member.TYPE_DEFINABLE_STIFFNESS,
                                                member_type_definable_stiffness = 1, member_hinge_start = 1)

        list_of_objects.append (member)
        connector_members.append (member_id)
        member_id += 1

    #create supports
    left_support = rfem.types_for_nodes.NodalSupport(nodes = [timber_nodes[0]],
                                                     spring = common.Vector3d(x = inf, z = inf),
                                                     rotational_restraint = common.Vector3d(x = 0, z = 0))
    list_of_objects.append(left_support)

    for node_nr in timber_nodes [1:]:
        right_supports = rfem.types_for_nodes.NodalSupport(nodes = [node_nr],
                                                           spring = common.Vector3d(x = 0.0, z = inf),
                                                           rotational_restraint = common.Vector3d(x = 0, z = 0))
        list_of_objects.append(right_supports)

    return list_of_objects, concrete_members, connector_members

def kategoriezuordnung(kategorie):

    if kategorie == "ständig":
        return rfem.loading.LoadCase.ACTION_CATEGORY_PERMANENT_G

    elif "Kat. A" in kategorie:
        return rfem.loading.LoadCase.ACTION_CATEGORY_IMPOSED_LOADS_CATEGORY_A_DOMESTIC_RESIDENTIAL_AREAS_QI_A

    elif "Kat. B" in kategorie:
        return rfem.loading.LoadCase.ACTION_CATEGORY_IMPOSED_LOADS_CATEGORY_B_OFFICE_AREAS_QI_B

    elif "Kat. C" in kategorie:
        return rfem.loading.LoadCase.ACTION_CATEGORY_IMPOSED_LOADS_CATEGORY_C_CONGREGATION_AREAS_QI_C

    elif "Kat. D" in kategorie:
        return rfem.loading.LoadCase.ACTION_CATEGORY_IMPOSED_LOADS_CATEGORY_D_SHOPPING_AREAS_QI_D

    elif "Kat. E" in kategorie:
        return rfem.loading.LoadCase.ACTION_CATEGORY_IMPOSED_LOADS_CATEGORY_E_STORAGE_AREAS_QI_E

    elif "Kat. H" in kategorie:
        return rfem.loading.LoadCase.ACTION_CATEGORY_IMPOSED_LOADS_CATEGORY_H_ROOFS_QI_H

    elif "Windlasten" in kategorie:
        return rfem.loading.LoadCase.ACTION_CATEGORY_WIND_QW

    elif "Schnee H <= 1000m" in kategorie:
        return rfem.loading.LoadCase.ACTION_CATEGORY_SNOW_ICE_LOADS_H_LESS_OR_EQUAL_TO_1000_M_QS

    elif "Schnee H > 1000m" in kategorie:
        return rfem.loading.LoadCase.ACTION_CATEGORY_SNOW_ICE_LOADS_H_GREATER_THAN_1000_M_QS

    else:
        return rfem.loading.LoadCase.ACTION_CATEGORY_PERMANENT_G

def aktualisiere_LF1(rfem_app, loads):
    if not loads:
        return

    erste_kategorie = loads[0]["Kategorie"]

    lc1 = rfem.loading.LoadCase(no=1,
        name=erste_kategorie,
        analysis_type=rfem.loading.LoadCase.ANALYSIS_TYPE_STATIC_ANALYSIS,
        self_weight_active=False,
        action_category=kategoriezuordnung(erste_kategorie))

    rfem_app.update_object(lc1)

def ermittle_lastbereiche(load, feldlängen):
    bereich = load["Bereich"]

    position_ist_definiert = (
        load["Position / Start [m]"] is not None and
        not np.isnan(load["Position / Start [m]"])
    )
    länge_ist_definiert = (load["Länge der Last [m]"] is not None and
                           not np.isnan(load["Länge der Last [m]"]))

    if position_ist_definiert != länge_ist_definiert:
        raise ValueError("Position / Start und Länge der Last müssen gemeinsam angegeben werden.")

    feldgrenzen = np.cumsum(feldlängen)
    gesamtlänge = float(sum(feldlängen))

    if bereich == "Gesamtsystem":
        if position_ist_definiert:
            start_global = float(load["Position / Start [m]"])
            ende_global = start_global + float(load["Länge der Last [m]"])
        else:
            start_global = 0.0
            ende_global = gesamtlänge

    elif "Feld" in bereich:
        feld_index = int(bereich.split()[1]) - 1
        feld_start = 0.0 if feld_index == 0 else float(feldgrenzen[feld_index - 1])
        feldlänge = float(feldlängen[feld_index])

        if position_ist_definiert:
            start_global = feld_start + float(load["Position / Start [m]"])
            ende_global = start_global + float(load["Länge der Last [m]"])
        else:
            start_global = feld_start
            ende_global = feld_start + feldlänge
    else:
        raise ValueError("Ungültiger Bereich.")

    if ende_global <= start_global:
        raise ValueError("Ungültiger Lastbereich.")

    teilbereiche = []
    feld_start = 0.0

    for feld_index, feld_ende in enumerate(feldgrenzen):
        feld_ende = float(feld_ende)

        teil_start_global = max(start_global, feld_start)
        teil_ende_global = min(ende_global, feld_ende)

        if teil_start_global < teil_ende_global:
            teilbereiche.append({
                "feld_index": feld_index,
                "teil_start_global": teil_start_global,
                "teil_ende_global": teil_ende_global,
                "a_local": teil_start_global - feld_start,
                "b_local": teil_ende_global - feld_start,
                "gesamt_start_global": start_global,
                "gesamt_ende_global": ende_global
            })

        feld_start = feld_ende

    return teilbereiche

def generiere_Lasten(loads, concrete_members, feldlängen):
    list_of_loads = []

    if not loads:
        return list_of_loads

    kategorie_zu_LC = {}
    LC_id = 2
    load_id = 1

    erste_kategorie = loads[0]["Kategorie"]
    kategorie_zu_LC[erste_kategorie] = 1

    for load in loads:

        kategorie = load["Kategorie"]
        #create new LC for new category
        if kategorie not in kategorie_zu_LC:
            action_category = kategoriezuordnung(kategorie)

            load_case = rfem.loading.LoadCase(no = LC_id,
                                              name = kategorie,
                                              analysis_type = rfem.loading.LoadCase.ANALYSIS_TYPE_STATIC_ANALYSIS,
                                              self_weight_active = False,
                                              action_category = action_category)
            list_of_loads.append(load_case)
            kategorie_zu_LC[kategorie] = LC_id
            LC_id += 1

        if load["Lastart"] == "Gleichstreckenlast":

            q = float(load["Größe Kraft [kN/m bzw. kN]"])*1000      #kN/m -> N/m
            q = -abs(q)

            try:
                teilbereiche = ermittle_lastbereiche(load, feldlängen)
            except Exception as e:
                print(f"Fehler bei Gleichstreckenlast: {e}")
                continue

            for teil in teilbereiche:
                member_id = concrete_members[teil["feld_index"]]

                member_load = rfem.loads.MemberLoad(no = load_id,
                                                    load_case = kategorie_zu_LC[kategorie],
                                                    members = [member_id],
                                                    load_distribution = rfem.loads.MemberLoad.LOAD_DISTRIBUTION_TRAPEZOIDAL,
                                                    load_direction = rfem.loads.MemberLoad.LOAD_DIRECTION_GLOBAL_Z_OR_USER_DEFINED_W_PROJECTED_LENGTH,
                                                    magnitude_1 = q,
                                                    magnitude_2 = q,
                                                    distance_a_is_defined_as_relative = False,
                                                    distance_a_absolute = teil["a_local"],
                                                    distance_b_is_defined_as_relative = False,
                                                    distance_b_absolute = teil["b_local"])
                list_of_loads.append(member_load)
                load_id += 1

        elif load["Lastart"] == "Einzellast":

            if "Feld" not in load["Bereich"]:
                continue

            p = float(load["Größe Kraft [kN/m bzw. kN]"])*1000      #kN -> N
            p = -abs(p)

            try:
                index = int(load["Bereich"].split()[1]) - 1
                member_id = concrete_members[index]
                x = float(load["Position / Start [m]"])
            except Exception as e:
                print(f"Fehler bei Einzellast: {e}")
                continue

            member_load = rfem.loads.MemberLoad(no = load_id,
                                                load_case = kategorie_zu_LC[kategorie],
                                                members = [member_id],
                                                load_distribution = rfem.loads.MemberLoad.LOAD_DISTRIBUTION_CONCENTRATED_1,
                                                load_direction = rfem.loads.MemberLoad.LOAD_DIRECTION_GLOBAL_Z_OR_USER_DEFINED_W_PROJECTED_LENGTH,
                                                magnitude = p,
                                                distance_a_is_defined_as_relative = False,
                                                distance_a_absolute = x)
            list_of_loads.append(member_load)
            load_id += 1

        elif load["Lastart"] == "Trapezlast":

            try:
                q_start = float(load["Größe Kraft [kN/m bzw. kN]"]) * 1000
                q_ende = float(load["Endwert Trapezlast [kN/m]"]) * 1000

                q_start = -abs(q_start)
                q_ende = -abs(q_ende)

                teilbereiche = ermittle_lastbereiche(load, feldlängen)
                gesamt_start = teilbereiche[0]["gesamt_start_global"]
                gesamt_ende = teilbereiche[0]["gesamt_ende_global"]
                gesamtlaenge = gesamt_ende - gesamt_start

                if gesamtlaenge <= 0:
                    continue
            except Exception as e:
                print(f"Fehler bei Trapezlast: {e}")
                continue

            for teil in teilbereiche:
                member_id = concrete_members[teil["feld_index"]]

                anteil_start = (teil["teil_start_global"] - gesamt_start) / gesamtlaenge
                anteil_ende = (teil["teil_ende_global"] - gesamt_start) / gesamtlaenge

                q1 = q_start + (q_ende - q_start) * anteil_start
                q2 = q_start + (q_ende - q_start) * anteil_ende

                member_load = rfem.loads.MemberLoad(
                    no = load_id,
                    load_case = kategorie_zu_LC[kategorie],
                    members = [member_id],
                    load_distribution = rfem.loads.MemberLoad.LOAD_DISTRIBUTION_TRAPEZOIDAL,
                    load_direction = rfem.loads.MemberLoad.LOAD_DIRECTION_GLOBAL_Z_OR_USER_DEFINED_W_PROJECTED_LENGTH,
                    magnitude_1 = q1,
                    magnitude_2 = q2,
                    distance_a_is_defined_as_relative = False,
                    distance_a_absolute = teil["a_local"],
                    distance_b_is_defined_as_relative = False,
                    distance_b_absolute = teil["b_local"]
                )
                list_of_loads.append(member_load)
                load_id += 1

    return list_of_loads

def sls_modification(rfem_app, connector_members):
    #create SLS stiffnessmodifications
    sls_member_modification = rfem.types_for_members.MemberStiffnessModification(no=1,
                                                                                 user_defined_name_enabled=True,
                                                                                 name="GZG_Schraubensteifigkeit",
                                                                                 type=rfem.types_for_members.MemberStiffnessModification.TYPE_PARTIAL_STIFFNESSES_FACTORS,
                                                                                 factor_of_bending_y_stiffness=1.5,
                                                                                 factor_of_bending_z_stiffness=1.5)
    sls_structure_modification = rfem.structure_advanced.StructureModification(no=1,
                                                                               user_defined_name_enabled=True,
                                                                               name="GZG_Schraubenmodifikation",
                                                                               modify_stiffnesses_members=True,
                                                                               modify_stiffnesses_member_table=
                                                                                 rfem.structure_advanced.StructureModification.ModifyStiffnessesMemberTable(
                                                                                     rows=[rfem.structure_advanced.StructureModification.ModifyStiffnessesMemberTableRow(
                                                                                         no=1,
                                                                                         description="Schrauben GZG",
                                                                                         member_modification=1,
                                                                                         members=connector_members)]))

    rfem_app.create_object_list([sls_member_modification, sls_structure_modification])
    #create and get back automatic loadcombinations
    rfem_app.generate_combinations()
    load_combinations = rfem_app.get_object_list(objs=[rfem.loading.LoadCombination()])

    #modify SLS combinations
    combinations_to_update = []
    for co in load_combinations:
        if co.design_situation in [2, 3, 4]:
            co.structure_modification_enabled = True
            co.structure_modification = 1
            combinations_to_update.append(co)

    if combinations_to_update:
        rfem_app.update_object_list(combinations_to_update)

# looking for right port for RFEM
def connect_to_rfem():
    for port in range(9000,9010):
        try:
            app = rfem.Application(port=port)
            print(f"Verbunden über Port {port}")
            return app
        except:
            continue

    raise RuntimeError('Keine Verbindung zu RFEM möglich.'
    'Bitte prüfen Sie, ob RFEM gestartet ist, die API aktiviert ist und eine gültige Lizenz vorliegt.')


# Initialisiere und verbinde zu RFEM
def run_rfem_model(feldlängen, Holzklasse, b_ug_cm, h_ug_cm, Betonklasse, b_og_cm, h_og_cm, K_ser,
                   VM_Abstand_1, VM_Abstand_2, VM_Abstand_3, offset_left, offset_right, loads):
    rfem_app = connect_to_rfem()
    with rfem_app:

        #Close all Models & create new Model
        rfem_app.close_all_models(save_changes = False)
        rfem_app.create_model(name = 'Stabwerksmodell nach Rautenstrauch')

        #Adjust Base Data
        base_data: rfem.BaseData  = rfem_app.get_base_data()
        base_data.main.model_type = rfem.BaseData.Main.MODEL_TYPE_2D_XZ_PLANE_STRESS
        base_data.general_settings.global_axes_orientation = rfem.BaseData.GeneralSettings.GLOBAL_AXES_ORIENTATION_ZUP
        base_data.addons.load_wizards_active = True
        base_data.addons.combination_wizard_and_classification_active = True
        base_data.addons.timber_design_active = True
        base_data.combinations_settings.combination_wizard_active = True
        base_data.combinations_settings.automatically_generate_corresponding_load_combinations = True
        #base_data.standards.combination_wizard_standard_group = (rfem.BaseData.Standards.COMBINATION_WIZARD_EN_1990_BASE___TIMBER_STANDARD_GROUP)
        #base_data.standards.combination_wizard_standard = (rfem.BaseData.Standards.COMBINATION_WIZARD_NATIONAL_ANNEX_AND_EDITION_EN_1990_BASE_TIMBER_DIN_2012_08_STANDARD)
        base_data.standards.load_wizard_standard_group = (rfem.BaseData.Standards.LOAD_WIZARD_STANDARD_GROUP_EN_1991_STANDARD_GROUP)
        base_data.standards.load_wizard_standard = (rfem.BaseData.Standards.LOAD_WIZARD_NATIONAL_ANNEX_AND_EDITION_EN_1991_DIN_2019_04_STANDARD)
        base_data.standards.timber_design_standard_group = (rfem.BaseData.Standards.TIMBER_DESIGN_STANDARD_GROUP_EN_1995_STANDARD_GROUP)
        base_data.standards.timber_design_standard = (rfem.BaseData.Standards.TIMBER_DESIGN_NATIONAL_ANNEX_AND_EDITION_EN_1995_DIN_2014_07_STANDARD)
        rfem_app.set_base_data(base_data = base_data)

        #Define Objects in Code
        Querschnitte = generiere_Materialien_und_Querschnitte(Holzklasse, b_ug_cm, h_ug_cm, Betonklasse, b_og_cm, h_og_cm, K_ser)
        Stabwerksmodell, concrete_members, connector_members = generiere_Stabwerksmodell(feldlängen, h_og_cm, h_ug_cm, VM_Abstand_1, VM_Abstand_2, VM_Abstand_3, offset_left, offset_right)
        Lasten = generiere_Lasten(loads, concrete_members, feldlängen)

        #Create Objects in Model
        rfem_app.create_object_list(Querschnitte)
        rfem_app.create_object_list(Stabwerksmodell)

        aktualisiere_LF1(rfem_app, loads)
        rfem_app.create_object_list(Lasten)

        #Modify SLS
        sls_modification(rfem_app, connector_members)