Collar Beam Roof#

../../../../_images/collar_beam_roof.png

Modelling a hinged timber collar roof and reading its governing bending moment:

  • Build a 12.0 x 4.0 m C24 collar roof as a 2D XZ model, pinned at both rafter feet

  • Hinge the rafters at the ridge, and the 4.5 m collar beam at both of its ends

  • Place the collar connections as nodes on the rafters, given by relative distance

  • Apply three load cases: gravity on the rafters, gravity on the collar, and wind on one slope

  • Let the combination wizard build the ULS 6.10 design situation from them

  • Read the largest bending moment over all members and combinations

Keywords:
collar beam member hinge node on member design situation internal forces ULS
from dlubal.api import rfem, common
from math import inf, sqrt

# -------------------------------------------------------
# This example demonstrates how to model a collar beam
# roof: two inclined rafters connected by a hinge at the
# ridge and pinned at the base, with a horizontal collar
# beam hinged to both of them. Self-weight is neglected,
# and the maximum bending moment of the ULS combination
# is retrieved.
# -------------------------------------------------------


# Editable parameters (SI units)

CROSS_SECTION_WIDTH = 0.06         # b  [m]
CROSS_SECTION_HEIGHT = 0.18        # h  [m]

SYSTEM_LENGTH = 12.0               # L  [m]
SYSTEM_HEIGHT = 4.0                # H  [m]
HEIGHT_TO_COLLAR_BEAM = 2.5        # hu [m]

# Derived, not editable: both follow from the geometry above.
HEIGHT_FROM_COLLAR_BEAM = SYSTEM_HEIGHT - HEIGHT_TO_COLLAR_BEAM          # ho [m]
COLLAR_BEAM_LENGTH = SYSTEM_LENGTH * HEIGHT_FROM_COLLAR_BEAM / SYSTEM_HEIGHT  # b [m]

DISTRIBUTED_LOAD_RAFTER = 5000.0   # g  [N/m] = 5 kN/m
DISTRIBUTED_LOAD_COLLAR = 5000.0   # gk [N/m] = 5 kN/m
DISTRIBUTED_LOAD_WIND = 5000.0     # gw [N/m] = 5 kN/m



def define_structure() -> list:
    """Define and return structural model objects created only once."""
    half_span = SYSTEM_LENGTH / 2.0
    apex_x = half_span
    apex_z = -SYSTEM_HEIGHT

    left_rafter_rel = HEIGHT_TO_COLLAR_BEAM / SYSTEM_HEIGHT
    right_rafter_rel = HEIGHT_FROM_COLLAR_BEAM / SYSTEM_HEIGHT

    rafter_length = sqrt(half_span ** 2 + SYSTEM_HEIGHT ** 2)
    left_rafter_abs = left_rafter_rel * rafter_length
    right_rafter_abs = right_rafter_rel * rafter_length

    return [
        rfem.structure_core.Material(
            no=1,
            name="C24",
        ),
        rfem.structure_core.CrossSection(
            no=1,
            # Parametric, not TYPE_STANDARDIZED_TIMBER: that type ignores b and h
            # and always yields the first catalogue entry, a 24/60 board.
            type=rfem.structure_core.CrossSection.TYPE_PARAMETRIC_MASSIVE_I,
            material=1,
            b=CROSS_SECTION_WIDTH,
            h=CROSS_SECTION_HEIGHT,
            shear_stiffness_deactivated=True,
        ),

        # Main truss nodes
        rfem.structure_core.Node(no=1),
        rfem.structure_core.Node(no=2, coordinate_1=SYSTEM_LENGTH),
        rfem.structure_core.Node(no=3, coordinate_1=apex_x, coordinate_3=apex_z),

        # Collar beam nodes as points on rafters
        rfem.structure_core.Node(
            no=4,
            type=rfem.structure_core.Node.TYPE_ON_MEMBER,
            on_member_reference_member=1,
            distance_from_start_relative=left_rafter_rel,
            distance_from_start_absolute=left_rafter_abs,
        ),
        rfem.structure_core.Node(
            no=5,
            type=rfem.structure_core.Node.TYPE_ON_MEMBER,
            on_member_reference_member=2,
            distance_from_start_relative=right_rafter_rel,
            distance_from_start_absolute=right_rafter_abs,
        ),

        # Truss lines
        rfem.structure_core.Line(no=1, definition_nodes=[1, 3]),
        rfem.structure_core.Line(no=2, definition_nodes=[3, 2]),
        rfem.structure_core.Line(no=3, definition_nodes=[4, 5]),

        # Members
        rfem.structure_core.Member(no=1, line=1, cross_section_start=1, member_hinge_end=1),
        rfem.structure_core.Member(no=2, line=2, cross_section_start=1),
        rfem.structure_core.Member(no=3, line=3, cross_section_start=1, member_hinge_start=1, member_hinge_end=1),

        # Supports and hinge
        rfem.types_for_nodes.NodalSupport(
            no=1,
            user_defined_name_enabled=True,
            name="Pinned",
            nodes=[1, 2],
            spring=common.Vector3d(x=inf, y=0, z=inf),
        ),
        # Every release constant is given explicitly. An omitted constant is
        # zero, which RELEASES that component: leaving vy, mt and mz out reads
        # as rigid but stores them as released. Harmless in this 2D XZ model,
        # where those degrees of freedom are inactive, but it would disconnect
        # the members if the model type were ever changed to 3D.
        rfem.types_for_members.MemberHinge(
            no=1,
            axial_release_n=inf,
            axial_release_vy=inf,
            axial_release_vz=inf,
            moment_release_mt=inf,
            moment_release_my=0.0,
            moment_release_mz=inf,
        ),
    ]

def define_loading() -> list:
    """Define separate load cases acting on one shared collar-roof structure."""

    return [
        rfem.loading.LoadCase(
            no=1,
            name="Distributed load rafter (g)",
            static_analysis_settings=1,
            self_weight_active=False,
        ),
        rfem.loading.LoadCase(
            no=2,
            name="Distributed load collar beam (gk)",
            static_analysis_settings=1,
            self_weight_active=False,
        ),
        rfem.loading.LoadCase(
            no=3,
            name="Distributed load wind (gw)",
            static_analysis_settings=1,
            self_weight_active=False,
        ),
        rfem.loads.MemberLoad(
            no=1,
            members=[1, 2],
            load_case=1,
            load_direction=rfem.loads.MemberLoad.LOAD_DIRECTION_GLOBAL_Z_OR_USER_DEFINED_W_PROJECTED_LENGTH,
            magnitude=DISTRIBUTED_LOAD_RAFTER,
        ),
        rfem.loads.MemberLoad(
            no=2,
            members=[3],
            load_case=2,
            magnitude=DISTRIBUTED_LOAD_COLLAR,
        ),
        rfem.loads.MemberLoad(
            no=3,
            members=[1],
            load_case=3,
            coordinate_system=common.CoordinateSystemRepresentation(
                type=common.CoordinateSystemRepresentation.COORDINATE_SYSTEM_TYPE_LOCAL
            ),
            magnitude=DISTRIBUTED_LOAD_WIND,
        ),
        rfem.loading.StaticAnalysisSettings(
            no=1
        ),
        rfem.loading.CombinationWizard(
            no=1,
            static_analysis_settings=1
        ),
        rfem.loading.DesignSituation(
            no=1,
            name="ULS (STR/GEO) - Permanent and transient - Eq. 6.10",
            design_situation_type=rfem.loading.DesignSituation.DESIGN_SITUATION_TYPE_STR_PERMANENT_AND_TRANSIENT_6_10,
            combination_wizard=1,
        )
    ]


with rfem.Application() as rfem_app:

    app_info = rfem_app.get_application_info()
    print(f"\nApplication Info:\n{app_info}")

    # Modelling
    rfem_app.close_all_models(save_changes=False)
    rfem_app.create_model(name='collar_beam_roof')

    base_data = rfem_app.get_base_data()
    base_data.combinations_settings.combination_wizard_active = True
    base_data.main.model_type = rfem.BaseData.Main.MODEL_TYPE_2D_XZ_PLANE_STRESS
    rfem_app.set_base_data(base_data=base_data)

    rfem_app.delete_all_objects()
    rfem_app.create_object_list(define_structure() + define_loading())

    # Calculation
    calculation_info = rfem_app.calculate_all(skip_warnings=True)
    print(f"\nCalculation Info:\n{calculation_info}")

    # Results
    member_internal_forces_df = rfem_app.get_results(
        results_type=rfem.results.STATIC_ANALYSIS_MEMBERS_INTERNAL_FORCES,
    ).data.copy()

    idx_max_abs_m_y = member_internal_forces_df["m_y"].abs().idxmax()
    row = member_internal_forces_df.loc[idx_max_abs_m_y]

    print(f"Max |m_y|:\n{row}")