Nonlinear Nodal Support#
|
Modelling a support that cannot be pulled down, to check the impact on reactions and internal forces:
Keywords:
continuous beam nodal support support nonlinearity uplift support force member deformation |
from math import inf
from dlubal.api import common, rfem
# -------------------------------------------------------
# Two-span continuous beam on a nonlinear nodal support.
#
# A support that simply rests on its bearing cannot pull the
# beam down. This is modelled with the nodal support
# nonlinearity "failure if positive P_Z'", which deactivates
# the support as soon as its reaction turns into tension.
#
# The same model is calculated twice and compared:
# 1) linear support - the support may act in tension
# 2) uplift-releasing - the support fails in tension
#
# With the load on the first span only, the far support of a
# two-span beam is pulled upwards. Releasing it turns the
# system into a single span with a free overhang, so the
# reactions, the span moment, and the deflections all change.
#
# Assumptions:
# - single-line 2D beam in the global XZ plane
# - Z-up coordinate system
# - supports pinned in plane (rotation about Y released)
# - self-weight ignored, so results match hand calculation
# -------------------------------------------------------
# Editable parameters (SI units)
MODEL_NAME = "nonlinear_nodal_support"
SPAN = 6.0
MATERIAL = "S235"
CROSS_SECTION = "IPE 300"
SPAN_LOAD = -20_000.0 # N/m on the first span, global Z direction
def define_structure() -> list:
"""Define the beam geometry, material, cross-section, and the two inner supports."""
return [
rfem.structure_core.Material(
no=1,
name=MATERIAL,
),
rfem.structure_core.CrossSection(
no=1,
name=CROSS_SECTION,
material=1,
),
# Nodes
rfem.structure_core.Node(no=1, coordinate_1=0.0, coordinate_2=0.0, coordinate_3=0.0),
rfem.structure_core.Node(no=2, coordinate_1=SPAN, coordinate_2=0.0, coordinate_3=0.0),
rfem.structure_core.Node(no=3, coordinate_1=2.0 * SPAN, coordinate_2=0.0, coordinate_3=0.0),
# Lines
rfem.structure_core.Line(no=1, definition_nodes=[1, 2]),
rfem.structure_core.Line(no=2, definition_nodes=[2, 3]),
# Members: 1 loaded span, 2 unloaded span
rfem.structure_core.Member(no=1, line=1, cross_section_start=1),
rfem.structure_core.Member(no=2, line=2, cross_section_start=1),
# End support, held in all directions
rfem.types_for_nodes.NodalSupport(
no=1,
nodes=[1],
spring=common.Vector3d(x=inf, y=inf, z=inf),
rotational_restraint=common.Vector3d(x=inf, y=0.0, z=inf),
),
# Inner support, free to slide along the beam axis
rfem.types_for_nodes.NodalSupport(
no=2,
nodes=[2],
spring=common.Vector3d(x=0.0, y=inf, z=inf),
rotational_restraint=common.Vector3d(x=inf, y=0.0, z=inf),
),
]
def end_support(uplift_releasing: bool):
"""Create the far support, either linear or failing under tension."""
nonlinearity = (
rfem.types_for_nodes.NodalSupport.SPRING_Z_NONLINEARITY_FAILURE_IF_POSITIVE
if uplift_releasing
else rfem.types_for_nodes.NodalSupport.SPRING_Z_NONLINEARITY_NONE
)
return rfem.types_for_nodes.NodalSupport(
no=3,
nodes=[3],
spring=common.Vector3d(x=0.0, y=inf, z=inf),
rotational_restraint=common.Vector3d(x=inf, y=0.0, z=inf),
spring_z_nonlinearity=nonlinearity,
)
def define_loading() -> list:
"""Define the analysis settings, the load case, and the load on the first span."""
return [
rfem.loading.StaticAnalysisSettings(
no=1,
analysis_type=rfem.loading.StaticAnalysisSettings.ANALYSIS_TYPE_GEOMETRICALLY_LINEAR,
),
rfem.loading.LoadCase(
no=1,
name="Load on span 1",
action_category=rfem.loading.LoadCase.ACTION_CATEGORY_PERMANENT_G,
static_analysis_settings=1,
self_weight_active=False,
),
rfem.loads.MemberLoad(
no=1,
load_case=1,
members=[1],
load_type=rfem.loads.MemberLoad.LOAD_TYPE_FORCE,
load_distribution=rfem.loads.MemberLoad.LOAD_DISTRIBUTION_UNIFORM,
load_direction=rfem.loads.MemberLoad.LOAD_DIRECTION_GLOBAL_Z_OR_USER_DEFINED_W_TRUE_LENGTH,
magnitude=SPAN_LOAD,
),
]
def calculate_case(rfem_app, uplift_releasing: bool) -> dict:
"""Apply the requested end support, recalculate, and collect the comparison values."""
rfem_app.update_object(end_support(uplift_releasing))
calculation_info = rfem_app.calculate_all(skip_warnings=True)
if not calculation_info.succeeded:
raise RuntimeError("Calculation failed.")
support_forces = rfem_app.get_results(
results_type=rfem.results.STATIC_ANALYSIS_NODES_SUPPORT_FORCES,
).data
reactions = {int(row.node_no): float(row.p_z) for row in support_forces.itertuples()}
deformations = rfem_app.get_results(
results_type=rfem.results.STATIC_ANALYSIS_MEMBERS_GLOBAL_DEFORMATIONS,
).data
internal_forces = rfem_app.get_results(
results_type=rfem.results.STATIC_ANALYSIS_MEMBERS_INTERNAL_FORCES,
).data
return {
"reactions": reactions,
"deflection": deformations[deformations["member_no"] == 1]["u_z"].min(),
"uplift": float(deformations[deformations["node_no"] == 3]["u_z"].iloc[0]),
"moment": internal_forces[internal_forces["member_no"] == 1]["m_y"].max(),
}
def print_case(label: str, result: dict) -> None:
"""Print one calculated case as a single comparison row."""
reactions = result["reactions"]
print(
f"{label:<22}"
f"{reactions[1] / 1000:>10.2f}"
f"{reactions[2] / 1000:>10.2f}"
f"{reactions[3] / 1000:>10.2f}"
f"{result['deflection'] * 1000:>13.2f}"
f"{result['uplift'] * 1000:>13.2f}"
f"{result['moment'] / 1000:>13.2f}"
)
with rfem.Application() as rfem_app:
print(f"\nCreating model: {MODEL_NAME}")
rfem_app.close_all_models(save_changes=False)
rfem_app.create_model(name=MODEL_NAME)
base_data = rfem_app.get_base_data()
base_data.main.surfaces_active = False
base_data.general_settings.global_axes_orientation = (
rfem.BaseData.GeneralSettings.GLOBAL_AXES_ORIENTATION_ZUP
)
rfem_app.set_base_data(base_data=base_data)
rfem_app.delete_all_objects()
rfem_app.create_object_list(
define_structure()
+ [end_support(uplift_releasing=False)]
+ define_loading()
)
linear = calculate_case(rfem_app, uplift_releasing=False)
nonlinear = calculate_case(rfem_app, uplift_releasing=True)
print(
f"\n{'End support':<22}"
f"{'R1 [kN]':>10}{'R2 [kN]':>10}{'R3 [kN]':>10}"
f"{'u_z,1 [mm]':>13}{'u_z,3 [mm]':>13}{'M_y,1 [kNm]':>13}"
)
print("-" * 91)
print_case("linear", linear)
print_case("uplift-releasing", nonlinear)
print(
"\nA positive R3 means the linear support holds the beam down. "
"Once the support is allowed to fail in tension, R3 drops to zero, "
"the beam lifts off by u_z,3, and the loaded span carries the full "
f"single-span moment of {abs(SPAN_LOAD) * SPAN ** 2 / 8 / 1000:.2f} kNm."
)