Response Spectrum by Standard#

../../../../../_images/response_spectrum_analysis.png

Defining a response spectrum according to standard, adjusting the peak ground acceleration, and reading spectral member forces:

  • Create the fixed-column model and seismic mass combinations

  • Define modal analysis and import masses from generated combinations

  • Create a response spectrum according to standard

  • Adjust the standard parameter for peak ground acceleration

  • Run response spectrum analysis in the X direction

Keywords:
spectral analysis response spectrum standard peak ground acceleration member internal forces
from math import inf

from dlubal.api import rfem, common

# -------------------------------------------------------
# This example demonstrates response spectrum analysis with a response spectrum defined according to standard.
# It defines the same fixed-column model, modal mass import,
# spectral analysis settings, and result retrieval workflow.
# -------------------------------------------------------

def define_structure() -> list:
    """Define a single fixed column (IPE 550)."""

    return [

            # Material
            rfem.structure_core.Material(
                no=1,
                name='S235',
            ),

            # CrossSection
            rfem.structure_core.CrossSection(
                no=1,
                name='IPE 550',
                material=1,
            ),

            # Nodes
            rfem.structure_core.Node(
                no=1,
                coordinate_2=-2,
            ),
            rfem.structure_core.Node(
                no=2,
                coordinate_2=-2,
                coordinate_3=-4,
            ),

            # Line
            rfem.structure_core.Line(
                no=1,
                definition_nodes=[1, 2],
            ),

            # Member
            rfem.structure_core.Member(
                no=1,
                line=1,
                cross_section_start=1,
            ),

            # Support
            rfem.types_for_nodes.NodalSupport(
                no=1,
                user_defined_name_enabled=True,
                name="Fixed",
                nodes=[1],
                spring=common.Vector3d(x=inf, y=inf, z=inf),
                rotational_restraint=common.Vector3d(x=inf, y=inf, z=inf),
            )
    ]

def define_loading() -> list:
    """Define load cases, static analysis settings, and seismic mass combinations."""

    return [
        # Load Case | LC1
        rfem.loading.LoadCase(
            no=1,
            name="Static | Self-weight",
            static_analysis_settings=1,
        ),
        # Nodal Loads | LC1
        rfem.loads.NodalLoad(   # Force
            no=1,
            nodes=[2],
            force_magnitude=1000,
            load_direction=rfem.loads.NodalLoad.LOAD_DIRECTION_GLOBAL_Z_OR_USER_DEFINED_W_TRUE_LENGTH,
            load_case=1,
        ),
        rfem.loads.NodalLoad(   # Mass
            no=2,
            load_type=rfem.loads.NodalLoad.LOAD_TYPE_MASS,
            nodes=[2],
            individual_mass_components=True,
            mass=common.Vector3d(x=100, y=100, z=100),
            mass_moment_of_inertia=common.Vector3d(x=100, y=100, z=100),
            load_case=1,
        ),

        # Static Analysis Settings
        rfem.loading.StaticAnalysisSettings(
            no=1,
            analysis_type=rfem.loading.StaticAnalysisSettings.ANALYSIS_TYPE_GEOMETRICALLY_LINEAR,
            mass_conversion_enabled=True,
        ),
        rfem.loading.StaticAnalysisSettings(
            no=2,
            analysis_type=rfem.loading.StaticAnalysisSettings.ANALYSIS_TYPE_SECOND_ORDER_P_DELTA,
            mass_conversion_enabled=True,
            consider_favorable_effect_due_to_tension_in_members=True,
        ),

        # --- Combinatoric for Seismic Mass ---

        # Combination Wizard
        rfem.loading.CombinationWizard(
            no=1,
            generate_combinations=rfem.loading.CombinationWizard.GENERATE_COMBINATIONS_LOAD_COMBINATIONS,
            consider_imperfection_case=True,
            static_analysis_settings=2,
        ),
        rfem.loading.CombinationWizard(
            no=2,
            generate_combinations=rfem.loading.CombinationWizard.GENERATE_COMBINATIONS_RESULT_COMBINATIONS,
        ),

        # Design Situations
        rfem.loading.DesignSituation(
            no=1,
            name="Seismic/Mass Combination - psi-E,i",
            design_situation_type=rfem.loading.DesignSituation.DesignSituationType.DESIGN_SITUATION_TYPE_SEISMIC_MASS,
            combination_wizard=1,
        ),
        rfem.loading.DesignSituation(
            no=2,
            name="ULS (EQU) - Seismic",
            design_situation_type= rfem.loading.DesignSituation.DesignSituationType.DESIGN_SITUATION_TYPE_EQU_SEISMIC,
            combination_wizard=2,
        ),
    ]

def define_response_spectra() -> list:
    """Define response spectrum input data for this example."""

    return [
        rfem.dynamic_loads.ResponseSpectrum(
                        no=1,
                        definition_type=rfem.dynamic_loads.ResponseSpectrum.DEFINITION_TYPE_ACCORDING_TO_STANDARD,
                        user_defined_response_spectrum_step_enabled=False,
                        is_g_factor_mode=False,
                ),
    ]

def define_modal_analysis_settings() -> list:
    """Modal analysis settings and the corresponding modal load case."""

    return [
        # Modal Analysis Settings
        rfem.loading.ModalAnalysisSettings(
            no=1,
            name='User-defined | Modes=10',
            user_defined_name_enabled=True,
            acting_masses_about_axis_x_enabled=True,
            acting_masses_about_axis_y_enabled=True,
            acting_masses_about_axis_z_enabled=True,
            acting_masses_in_direction_z_enabled=True,
            activate_minimum_initial_prestress=False,
            solution_method=rfem.loading.ModalAnalysisSettings.SOLUTION_METHOD_LANCZOS,
            number_of_modes=10,
            minimum_initial_strain=0.00001,
        ),
        # Modal Load Cases
        rfem.loading.LoadCase(
            no=2,
            analysis_type=rfem.loading.LoadCase.ANALYSIS_TYPE_MODAL_ANALYSIS,
            name="Modal Analysis ALL 10",
            modal_analysis_settings=1,
        ),
    ]

def define_spectral_analysis_settings() -> list:
    """Spectral analysis settings (SRSS + Scaled Sum 30%) and the RSA load case."""

    return [

        # Spectral Analysis Settings
        rfem.loading.SpectralAnalysisSettings(
            no=1,
            user_defined_name_enabled=True,
            name='SRSS | Scaled Sum 30.00 %',
            assigned_to='LC 3',
            combination_rule_for_periodic_responses=rfem.loading.SpectralAnalysisSettings.COMBINATION_RULE_FOR_DIRECTIONAL_COMPONENTS_SRSS,
            use_equivalent_linear_combination=False,
            combination_rule_for_directional_components=rfem.loading.SpectralAnalysisSettings.COMBINATION_RULE_FOR_DIRECTIONAL_COMPONENTS_SCALED_SUM,
            combination_rule_for_directional_components_value=0.3,
        ),
        # Spectral Load Cases
        rfem.loading.LoadCase(
            no=3,
            analysis_type=rfem.loading.LoadCase.ANALYSIS_TYPE_RESPONSE_SPECTRUM_ANALYSIS,
            name="RSA Direction X",
            spectral_analysis_settings=1,
            response_spectrum_is_enabled_in_direction_x=True,
            response_spectrum_in_direction_x=1,
            response_spectrum_and_equivalent_load_consider_accidental_torsion=True,
            response_spectrum_and_equivalent_load_eccentricity_for_y_direction_relative=0.01,
            import_modal_analysis_from=2,
            response_spectrum_save_results_of_all_selected_modes=True,
        ),
    ]

# -------------------------------------------------------
# MAIN SCRIPT
# -------------------------------------------------------

with rfem.Application() as rfem_app:

    rfem_app.close_all_models(save_changes=False)
    rfem_app.create_model(name='Response Spectrum by Standard')

    # Set global model settings:
    base_data: rfem.BaseData = rfem_app.get_base_data()

    # Activate add-ons
    base_data.addons.modal_analysis_active = True
    base_data.addons.response_spectrum_analysis_active = True
    base_data.combinations_settings.combination_wizard_active = True
    base_data.combinations_settings.result_combinations_active = True
    base_data.combinations_settings.result_combinations_parentheses_active = True
    base_data.combinations_settings.result_combinations_consider_sub_results = True
    # Set standard
    base_data.standards.combination_wizard_standard = rfem.BaseData.Standards.COMBINATION_WIZARD_NATIONAL_ANNEX_AND_EDITION_EN_1990_DIN_2012_08_STANDARD
    base_data.standards.load_wizard_standard = rfem.BaseData.Standards.LOAD_WIZARD_NATIONAL_ANNEX_AND_EDITION_EN_1991_DIN_2019_04_STANDARD
    base_data.standards.dynamic_analysis_standard = rfem.BaseData.Standards.DYNAMIC_ANALYSIS_NATIONAL_ANNEX_AND_EDITION_EN_1998_1_DIN_2023_11_STANDARD
    # Adjust general settings
    base_data.general_settings.gravitational_acceleration = 9.81

    rfem_app.set_base_data(base_data=base_data)
    rfem_app.delete_all_objects()

    # Build model structure and loading
    rfem_app.create_object_list(
        define_structure() +
        define_loading()
    )

    # Define analysis settings
    rfem_app.create_object_list(
        define_modal_analysis_settings() +
        define_spectral_analysis_settings()
    )

    # Import masses from combinations to modal load cases
    rfem_app.generate_combinations()
    lc_list = rfem_app.get_object_list(
        objs=[rfem.loading.LoadCase()]
    )
    for lc in lc_list:
        if lc.analysis_type is rfem.loading.LoadCase.ANALYSIS_TYPE_MODAL_ANALYSIS:
            lc.import_masses_from.no = 1
            lc.import_masses_from.object_type = rfem.ObjectType.OBJECT_TYPE_LOAD_COMBINATION
    rfem_app.update_object_list(lc_list)

    # Create response spectrum data
    rfem_app.create_object_list(
        objs=define_response_spectra()
    )

    # Modify response spectrum by standard (peak ground acceleration)
    spectrum_by_standard = rfem_app.get_object(
        rfem.dynamic_loads.ResponseSpectrum(no=1)
    )
    common.set_values_by_key(
        tree=spectrum_by_standard.standard_parameters,
        key='a_gr',
        values=[0.459]
    )
    rfem_app.update_object(spectrum_by_standard)

    # Retrieve results
    rfem_app.calculate_all(skip_warnings=True)

    results = rfem_app.get_results(
        results_type=rfem.results.ResultsType.SPECTRAL_ANALYSIS_MEMBERS_INTERNAL_FORCES
    )
    print(f"\nSpectral Analysis | Members Internal Forces:\n{results.data}")