Stability Methods#
|
This example demonstrates the two stability design methods of the Steel Design add-on side by side according to EN 1993, and how to switch between their sub-options through the ULS configuration.
|
Note
The calculation method is not a setting of the ULS configuration. It follows from the stability object assigned to the member:
Assigned object |
Calculation method |
|---|---|
Equivalent member method, EN 1993-1-1, 6.3.1 to 6.3.3 |
|
2D general method, EN 1993-1-1, 6.3.4 (4 degrees of freedom) |
Assigning both objects to the same member is rejected by the add-on with the message More than one stability method assigned. This is why the model contains two identical members, one per method, instead of reassigning the stability object of a single member between design runs.
from dlubal.api import rfem, common
from math import inf
import pandas
# -------------------------------------------------------
# This example demonstrates the two stability design methods of the
# Steel Design add-on side by side, in accordance with EN 1993-1-1 and
# the French National Annex NF:2016-02.
#
# The calculation method is not a setting of the ULS configuration. It
# follows from the stability object assigned to the member:
#
# SteelEffectiveLengths -> equivalent member method, 6.3.1 to 6.3.3
# SteelBoundaryConditions -> 2D general method, 6.3.4 (4 DOF)
#
# Assigning both objects to the same member is rejected by the add-on
# with the message "More than one stability method assigned". The model
# therefore contains two identical members, one for each method, so that
# a single design run covers both of them.
#
# It includes:
# - two identical beam-columns under an axial compressive force and
# bending about both principal axes
# - the interaction factors kyy, kyz, kzy and kzz of 6.3.3(4)
# determined acc. to Method 1 (Annex A) and acc. to Method 2
# (Annex B), and their effect on the design ratio
# - the adapted method that extends the general method of 6.3.4 to
# double bending, without which a member carrying M_z is rejected
# - extraction of the interaction factors from the design check details
# -------------------------------------------------------
# The two members are geometrically and structurally identical. They differ
# only in the stability object assigned to them.
MEMBER_EQUIVALENT_MEMBER_METHOD = 1 # carries SteelEffectiveLengths
MEMBER_GENERAL_METHOD = 2 # carries SteelBoundaryConditions
# What to report for each member: the method its stability object selects, the
# design check types to look up, and the values to read from the design check
# details. A "ST" check type carries a design ratio, an "ER" type carries no
# ratio and states why a check could not be performed. Both outcomes of the
# general method are expected results of the two design runs below: without the
# adapted method the member is rejected because of M_z, with it it is designed.
MEMBERS = {
MEMBER_EQUIVALENT_MEMBER_METHOD: {
'method': "Equivalent member method acc. to EN 1993-1-1, 6.3.1 to 6.3.3",
'check_types': (
'ST3100.00', # Bending and buckling about principal axes acc. to EN 1993-1-1, 6.3.3
),
'detail_keys': ('k_yy', 'k_yz', 'k_zy', 'k_zz'), # Interaction factors of 6.3.3(4)
},
MEMBER_GENERAL_METHOD: {
'method': "2D general method acc. to EN 1993-1-1, 6.3.4",
'check_types': (
'ST4100.03', # Compression and/or bending acc. to EN 1993-1-1, 6.3.4 | General method
'ER3100.00', # General method acc. to EN 1993-1-1, 6.3.4 is not applicable for bending about z-axis
),
'detail_keys': (), # The interaction factors of 6.3.3(4) do not apply here
},
}
# The two design runs. Each one is a set of keys of the tree table
# "settings_ec3" of the Steel Design ULS configuration.
DESIGN_VARIANTS = [
(
"=== Method 1 acc. to Annex A | adapted method OFF ===",
{
# Interaction factors of 6.3.3(4) acc. to Method 1, Annex A
'param_k_annex_a': True,
# General method without the adapted method, so it covers in-plane
# bending and lateral-torsional buckling only
'extensional_methods': False,
'european_lateral_torsional_buckling_curves': False,
'adapted_method': False,
},
),
(
"=== Method 2 acc. to Annex B | adapted method ON (double bending) ===",
{
# Interaction factors of 6.3.3(4) acc. to Method 2, Annex B
'param_k_annex_b': True,
# "Adapted method (enable double bending)" extends the general
# method of 6.3.4 to biaxial bending. The checkbox is nested under
# two parents, and a leaf under an inactive parent is stored but
# has no effect, so the whole branch is written, not the leaf alone.
'extensional_methods': True,
'european_lateral_torsional_buckling_curves': True,
'interpolation_acc_to_eq_666': False,
'adapted_method': True,
},
),
]
def define_structure_objects() -> list:
# Both members are single-span beam-columns, 6.0 m long, IPE 300 in S235.
# Each of them is held in position and against torsion at both ends and is
# free to rotate about both principal axes, which is the fork support case
# and the k = 1.0 reference for lateral-torsional buckling.
#
# Member 1 lies at y = 0.0 m, member 2 at y = 3.0 m. Their supports and
# loads are identical, so any difference in the design check comes from
# the stability method alone.
return [
# Materials
rfem.structure_core.Material(
no=1,
name="S235",
),
# Cross-Sections
rfem.structure_core.CrossSection(
no=1,
name="IPE 300",
material=1,
),
# Nodes | Member 1
rfem.structure_core.Node(
no=1,
),
rfem.structure_core.Node(
no=2,
coordinate_1=6.0,
),
# Nodes | Member 2
rfem.structure_core.Node(
no=3,
coordinate_2=3.0,
),
rfem.structure_core.Node(
no=4,
coordinate_1=6.0,
coordinate_2=3.0,
),
# Lines
rfem.structure_core.Line(
no=1,
definition_nodes=[1, 2],
),
rfem.structure_core.Line(
no=2,
definition_nodes=[3, 4],
),
# Members
rfem.structure_core.Member(
no=MEMBER_EQUIVALENT_MEMBER_METHOD,
line=1,
cross_section_start=1,
),
rfem.structure_core.Member(
no=MEMBER_GENERAL_METHOD,
line=2,
cross_section_start=1,
),
# Nodal Supports
rfem.types_for_nodes.NodalSupport(
no=1,
user_defined_name_enabled=True,
name="Fork support | held in position",
nodes=[1, 3],
spring=common.Vector3d(x=inf, y=inf, z=inf),
rotational_restraint=common.Vector3d(x=inf, y=0, z=0),
),
rfem.types_for_nodes.NodalSupport(
no=2,
user_defined_name_enabled=True,
name="Fork support | free along the member axis",
nodes=[2, 4],
spring=common.Vector3d(x=0, y=inf, z=inf),
rotational_restraint=common.Vector3d(x=inf, y=0, z=0),
),
]
def define_loading_objects() -> list:
# Both members carry the same three actions: a compressive normal force at
# the free end, a uniform load about the major y-axis and a uniform load
# about the minor z-axis.
#
# The normal force is what makes the interaction factors kyy, kyz, kzy and
# kzz relevant at all, and the load about the minor axis is what makes the
# member a case of double bending. Both are needed to tell the two
# stability methods apart.
return [
# Static Analysis Settings
rfem.loading.StaticAnalysisSettings(
no=1,
analysis_type=rfem.loading.StaticAnalysisSettings.ANALYSIS_TYPE_GEOMETRICALLY_LINEAR,
),
# Load Cases
rfem.loading.LoadCase(
no=1,
name="Permanent",
action_category=rfem.loading.LoadCase.ACTION_CATEGORY_PERMANENT_G,
static_analysis_settings=1,
self_weight_active=False,
),
rfem.loading.LoadCase(
no=2,
name="Wind",
action_category=rfem.loading.LoadCase.ACTION_CATEGORY_WIND_QW,
static_analysis_settings=1,
self_weight_active=False,
),
# Nodal Loads | LC1
rfem.loads.NodalLoad(
no=1,
nodes=[2, 4],
load_case=1,
load_type=rfem.loads.NodalLoad.LOAD_TYPE_COMPONENTS,
components_force_x=-80000, # Compressive normal force (N)
),
# Member Loads | LC1
rfem.loads.MemberLoad(
no=1,
members=[MEMBER_EQUIVALENT_MEMBER_METHOD, MEMBER_GENERAL_METHOD],
load_case=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=-2500, # Bending about the major y-axis (N/m)
),
# Member Loads | LC2
rfem.loads.MemberLoad(
no=2,
members=[MEMBER_EQUIVALENT_MEMBER_METHOD, MEMBER_GENERAL_METHOD],
load_case=2,
load_type=rfem.loads.MemberLoad.LOAD_TYPE_FORCE,
load_distribution=rfem.loads.MemberLoad.LOAD_DISTRIBUTION_UNIFORM,
load_direction=rfem.loads.MemberLoad.LOAD_DIRECTION_GLOBAL_Y_OR_USER_DEFINED_V_TRUE_LENGTH,
magnitude=1000, # Bending about the minor z-axis (N/m)
),
# Design Situations
rfem.loading.DesignSituation(
no=1,
user_defined_name_enabled=True,
name="ULS - Permanent and transient",
design_situation_type=rfem.loading.DesignSituation.DESIGN_SITUATION_TYPE_STR_PERMANENT_AND_TRANSIENT_6_10,
consider_inclusive_exclusive_load_cases=False,
),
# Load Combinations
rfem.loading.LoadCombination(
no=1,
design_situation=1,
name="CO1",
static_analysis_settings=1,
to_solve=True,
items=rfem.loading.LoadCombination.ItemsTable(
rows=[
rfem.loading.LoadCombination.ItemsRow(
load_case=1,
factor=1.35,
),
rfem.loading.LoadCombination.ItemsRow(
load_case=2,
factor=1.5,
),
]
),
),
]
def define_steel_design_objects() -> list:
# The configuration applies to both members. The stability object is what
# differs: effective lengths select the equivalent member method, boundary
# conditions select the 2D general method.
return [
# Steel Design Configurations
rfem.steel_design_objects.SteelDesignUlsConfiguration(
no=1,
user_defined_name_enabled=True,
name="ULS_configuration",
assigned_to_all_members=True,
),
# Steel Effective Lengths | Member 1
rfem.steel_design.SteelEffectiveLengths(
no=1,
user_defined_name_enabled=True,
name="Equivalent member method",
members=[MEMBER_EQUIVALENT_MEMBER_METHOD],
flexural_buckling_about_y=True,
flexural_buckling_about_z=True,
torsional_buckling=True,
lateral_torsional_buckling=True,
intermediate_nodes=False,
different_properties=False,
determination_mcr_europe=rfem.steel_design.SteelEffectiveLengths.DeterminationMcrEurope.DETERMINATION_MCR_EUROPE_EIGENVALUE,
),
# Steel Boundary Conditions | Member 2
# The default definition type is 2D, which gives four degrees of
# freedom and fork supports at both member ends.
rfem.steel_design.SteelBoundaryConditions(
no=1,
user_defined_name_enabled=True,
name="2D general method",
members=[MEMBER_GENERAL_METHOD],
definition_type=rfem.steel_design.SteelBoundaryConditions.DEFINITION_TYPE_2D,
),
]
def get_member_checks(design_checks_df: pandas.DataFrame, member_no: int,
check_types: tuple) -> pandas.DataFrame:
"""
This function returns the rows of the given check types for the given
member. The results are aggregated per member, so there is at most one row
per check type.
"""
return design_checks_df.loc[
(design_checks_df['member_no'] == member_no)
& design_checks_df['design_check_type'].isin(check_types)
]
def get_design_check_value_by_key(design_check_details_df, key) -> str:
"""
This function extracts the 'value', 'unit', and 'caption' from the design check details
based on the specified 'key'.
"""
row = design_check_details_df.loc[design_check_details_df['key'] == key]
if row.empty:
raise ValueError(f"Key '{key}' not found in design check details.")
caption = row['caption'].values[0]
key = row['key'].values[0]
value = row['value'].values[0]
unit = row['unit'].values[0]
return f"{key} = {value} [{unit}]\t| {caption}"
# Connect to the RFEM application
with rfem.Application() as rfem_app:
# Initialize model
rfem_app.close_all_models(save_changes=False)
rfem_app.create_model(name="steel_design_stability_methods")
# Edit base data
base_data = rfem_app.get_base_data()
base_data.addons.steel_design_active = True
base_data.standards.steel_design_standard = rfem.BaseData.Standards.STEEL_DESIGN_NATIONAL_ANNEX_AND_EDITION_EN_1993_NF_2016_02_STANDARD
rfem_app.set_base_data(base_data=base_data)
rfem_app.delete_all_objects()
# Create model objects
rfem_app.create_object_list(
objs=
define_structure_objects()+
define_loading_objects()+
define_steel_design_objects()
)
# Calculate the model
rfem_app.calculate_all(skip_warnings=True)
# Retrieve the internal forces both members are designed for. They are
# identical, which is the point of the comparison.
internal_forces_df = rfem_app.get_results(
results_type=rfem.results.STATIC_ANALYSIS_MEMBERS_INTERNAL_FORCES,
filters=[
rfem.results.ResultsFilter(
column_id="loading",
filter_expression="CO1"
)
],
).data
print("\nDesign internal forces | CO1")
print(f" {'Member':<8}{'N [kN]':>12}{'M_y [kNm]':>12}{'M_z [kNm]':>12}")
for member_no, member_forces_df in internal_forces_df.groupby('member_no'):
print(
f" {member_no:<8}"
f"{member_forces_df['n'].abs().max() / 1000:>12.2f}"
f"{member_forces_df['m_y'].abs().max() / 1000:>12.2f}"
f"{member_forces_df['m_z'].abs().max() / 1000:>12.2f}"
)
# Retrieve the design check ratios of both members, to show which columns
# the design results carry. to_string() is used instead of printing the
# DataFrame directly, because the default representation truncates itself
# to the terminal width and fails on the metadata that the API attaches to
# a results DataFrame.
design_checks_df = rfem_app.get_results(
results_type=rfem.results.STEEL_DESIGN_MEMBERS_DESIGN_RATIOS_BY_MEMBER,
).data
print(f"\nDesign Check Ratios:\n{design_checks_df.to_string()}")
# Design both members once per variant of the stability sub-options
for label, settings in DESIGN_VARIANTS:
# Retrieve the design configuration and write the settings of the
# variant into its tree table
steel_uls_config = rfem_app.get_object(
obj=rfem.steel_design_objects.SteelDesignUlsConfiguration(no=1)
)
settings_ec3_uls_tree = steel_uls_config.settings_ec3
for key, value in settings.items():
common.tree_table.set_values_by_key(
tree=settings_ec3_uls_tree,
key=key,
values=[value]
)
# Apply the updated configuration to the model
rfem_app.update_object(
obj=rfem.steel_design_objects.SteelDesignUlsConfiguration(
no=1,
settings_ec3=settings_ec3_uls_tree
)
)
# Calculate the model and retrieve the design check ratios
rfem_app.calculate_all(skip_warnings=True)
design_checks_df = rfem_app.get_results(
results_type=rfem.results.STEEL_DESIGN_MEMBERS_DESIGN_RATIOS_BY_MEMBER,
).data
print(f"\n{label}")
for member_no, member in MEMBERS.items():
print(f"\n Member {member_no} | {member['method']}")
for _, check in get_member_checks(design_checks_df, member_no, member['check_types']).iterrows():
print(f" {check['design_check_type']} | {check['design_check_description']}")
# An "ER" row states why no check was performed and carries no
# design ratio and no details
if pandas.isna(check['design_ratio']):
continue
print(f" Design ratio: {check['design_ratio']:.3f}")
# Retrieve the design check details and read the values of
# interest from them
if member['detail_keys']:
design_check_details_df = rfem_app.get_results(
results_type=rfem.results.STEEL_DESIGN_DESIGN_CHECK_DETAILS,
filters=[
rfem.results.ResultsFilter(
column_id="design_check_details_id",
filter_expression=str(check['design_check_details_id'])
)
],
).data
for key in member['detail_keys']:
print(f" {get_design_check_value_by_key(design_check_details_df, key=key)}")
print(
"\nBoth members carry the same internal forces, so the difference in the\n"
"design ratios comes from the stability method alone:\n"
"\n"
f" - Member {MEMBER_EQUIVALENT_MEMBER_METHOD} is verified with the interaction formulae 6.61 and 6.62.\n"
" Method 1 and Method 2 are two ways of determining the interaction\n"
" factors of the same formulae, so the ratio changes while the design\n"
" check stays the same.\n"
"\n"
f" - Member {MEMBER_GENERAL_METHOD} is verified with the general method of 6.3.4, which starts\n"
" from the elastic critical moment of the member instead of from\n"
" tabulated interaction factors. It is a different mechanical model,\n"
" so its ratio is not expected to match the one of the equivalent\n"
" member method, and it needs the adapted method to accept M_z at all."
)
using Rfem = Dlubal.Api.Rfem;
using Common = Dlubal.Api.Common;
using Google.Protobuf;
using System.Globalization;
// -------------------------------------------------------
// This example demonstrates the two stability design methods of the
// Steel Design add-on side by side, in accordance with EN 1993-1-1 and
// the French National Annex NF:2016-02.
//
// The calculation method is not a setting of the ULS configuration. It
// follows from the stability object assigned to the member:
//
// SteelEffectiveLengths -> equivalent member method, 6.3.1 to 6.3.3
// SteelBoundaryConditions -> 2D general method, 6.3.4 (4 DOF)
//
// Assigning both objects to the same member is rejected by the add-on
// with the message "More than one stability method assigned". The model
// therefore contains two identical members, one for each method, so that
// a single design run covers both of them.
//
// It includes:
// - two identical beam-columns under an axial compressive force and
// bending about both principal axes
// - the interaction factors kyy, kyz, kzy and kzz of 6.3.3(4)
// determined acc. to Method 1 (Annex A) and acc. to Method 2
// (Annex B), and their effect on the design ratio
// - the adapted method that extends the general method of 6.3.4 to
// double bending, without which a member carrying M_z is rejected
// - extraction of the interaction factors from the design check details
// -------------------------------------------------------
// The two members are geometrically and structurally identical. They differ
// only in the stability object assigned to them.
const int memberEquivalentMemberMethod = 1; // carries SteelEffectiveLengths
const int memberGeneralMethod = 2; // carries SteelBoundaryConditions
// What to report for each member: the method its stability object selects, the
// design check types to look up, and the values to read from the design check
// details. A "ST" check type carries a design ratio, an "ER" type carries no
// ratio and states why a check could not be performed. Both outcomes of the
// general method are expected results of the two design runs below: without the
// adapted method the member is rejected because of M_z, with it it is designed.
var members = new[]
{
(
No: memberEquivalentMemberMethod,
Method: "Equivalent member method acc. to EN 1993-1-1, 6.3.1 to 6.3.3",
CheckTypes: new[]
{
"ST3100.00", // Bending and buckling about principal axes acc. to EN 1993-1-1, 6.3.3
},
DetailKeys: new[] { "k_yy", "k_yz", "k_zy", "k_zz" } // Interaction factors of 6.3.3(4)
),
(
No: memberGeneralMethod,
Method: "2D general method acc. to EN 1993-1-1, 6.3.4",
CheckTypes: new[]
{
"ST4100.03", // Compression and/or bending acc. to EN 1993-1-1, 6.3.4 | General method
"ER3100.00", // General method acc. to EN 1993-1-1, 6.3.4 is not applicable for bending about z-axis
},
DetailKeys: Array.Empty<string>() // The interaction factors of 6.3.3(4) do not apply here
),
};
// The two design runs. Each one is a set of keys of the tree table
// "settings_ec3" of the Steel Design ULS configuration.
var designVariants = new[]
{
(
Label: "=== Method 1 acc. to Annex A | adapted method OFF ===",
Settings: new Dictionary<string, object?>
{
// Interaction factors of 6.3.3(4) acc. to Method 1, Annex A
{ "param_k_annex_a", true },
// General method without the adapted method, so it covers in-plane
// bending and lateral-torsional buckling only
{ "extensional_methods", false },
{ "european_lateral_torsional_buckling_curves", false },
{ "adapted_method", false },
}
),
(
Label: "=== Method 2 acc. to Annex B | adapted method ON (double bending) ===",
Settings: new Dictionary<string, object?>
{
// Interaction factors of 6.3.3(4) acc. to Method 2, Annex B
{ "param_k_annex_b", true },
// "Adapted method (enable double bending)" extends the general
// method of 6.3.4 to biaxial bending. The checkbox is nested under
// two parents, and a leaf under an inactive parent is stored but
// has no effect, so the whole branch is written, not the leaf alone.
{ "extensional_methods", true },
{ "european_lateral_torsional_buckling_curves", true },
{ "interpolation_acc_to_eq_666", false },
{ "adapted_method", true },
}
),
};
// Return the cell of the given column as text, or null if the table is empty
// or the cell has no value.
string? Cell(Common.Table table, string columnId)
=> table.Data.Rows.Count == 0 ? null : table.Col(columnId)?[0]?.ToString();
// Return the largest absolute value of the given column.
double MaxAbs(Common.Table table, string columnId)
{
var column = table.Col(columnId);
if (column == null) return 0.0;
var maximum = 0.0;
for (long rowIndex = 0; rowIndex < column.Length; rowIndex++)
{
var cell = column[rowIndex];
if (cell != null)
{
maximum = Math.Max(maximum, Math.Abs(Convert.ToDouble(cell, CultureInfo.InvariantCulture)));
}
}
return maximum;
}
ApplicationRfem? rfemApp = null;
try
{
// Connect to the RFEM application
rfemApp = new ApplicationRfem();
// Initialize model
rfemApp.close_all_models(saveChanges: false);
rfemApp.create_model(name: "steel_design_stability_methods");
// Edit base data
var baseData = rfemApp.get_base_data();
baseData.Addons.SteelDesignActive = true;
baseData.Standards.SteelDesignStandard =
Rfem.BaseData.Types.Standards.Types.SteelDesignStandard.SteelDesignNationalAnnexAndEditionEn1993Nf201602Standard;
rfemApp.set_base_data(baseData: baseData);
rfemApp.delete_all_objects();
// Create model objects
//
// Both members are single-span beam-columns, 6.0 m long, IPE 300 in S235.
// Each of them is held in position and against torsion at both ends and is
// free to rotate about both principal axes, which is the fork support case
// and the k = 1.0 reference for lateral-torsional buckling.
//
// Member 1 lies at y = 0.0 m, member 2 at y = 3.0 m. Their supports and
// loads are identical, so any difference in the design check comes from
// the stability method alone.
//
// Both members carry a compressive normal force at the free end, a uniform
// load about the major y-axis and a uniform load about the minor z-axis.
// The normal force is what makes the interaction factors kyy, kyz, kzy and
// kzz relevant at all, and the load about the minor axis is what makes the
// member a case of double bending.
rfemApp.create_object_list(new List<IMessage>
{
// Materials
new Rfem.StructureCore.Material { No = 1, Name = "S235" },
// Cross-Sections
new Rfem.StructureCore.CrossSection { No = 1, Name = "IPE 300", Material = 1 },
// Nodes | Member 1
new Rfem.StructureCore.Node { No = 1 },
new Rfem.StructureCore.Node { No = 2, Coordinate1 = 6.0 },
// Nodes | Member 2
new Rfem.StructureCore.Node { No = 3, Coordinate2 = 3.0 },
new Rfem.StructureCore.Node { No = 4, Coordinate1 = 6.0, Coordinate2 = 3.0 },
// Lines
new Rfem.StructureCore.Line { No = 1, DefinitionNodes = { 1, 2 } },
new Rfem.StructureCore.Line { No = 2, DefinitionNodes = { 3, 4 } },
// Members
new Rfem.StructureCore.Member
{
No = memberEquivalentMemberMethod, Line = 1, CrossSectionStart = 1
},
new Rfem.StructureCore.Member
{
No = memberGeneralMethod, Line = 2, CrossSectionStart = 1
},
// Nodal Supports
new Rfem.TypesForNodes.NodalSupport
{
No = 1,
UserDefinedNameEnabled = true,
Name = "Fork support | held in position",
Nodes = { 1, 3 },
Spring = new Common.Vector3d
{
X = double.PositiveInfinity, Y = double.PositiveInfinity, Z = double.PositiveInfinity
},
RotationalRestraint = new Common.Vector3d { X = double.PositiveInfinity, Y = 0, Z = 0 },
},
new Rfem.TypesForNodes.NodalSupport
{
No = 2,
UserDefinedNameEnabled = true,
Name = "Fork support | free along the member axis",
Nodes = { 2, 4 },
Spring = new Common.Vector3d { X = 0, Y = double.PositiveInfinity, Z = double.PositiveInfinity },
RotationalRestraint = new Common.Vector3d { X = double.PositiveInfinity, Y = 0, Z = 0 },
},
// Static Analysis Settings
new Rfem.Loading.StaticAnalysisSettings
{
No = 1,
AnalysisType = Rfem.Loading.StaticAnalysisSettings.Types.AnalysisType.GeometricallyLinear,
},
// Load Cases
new Rfem.Loading.LoadCase
{
No = 1,
Name = "Permanent",
ActionCategory = Rfem.Loading.LoadCase.Types.ActionCategory.PermanentG,
StaticAnalysisSettings = 1,
SelfWeightActive = false,
},
new Rfem.Loading.LoadCase
{
No = 2,
Name = "Wind",
ActionCategory = Rfem.Loading.LoadCase.Types.ActionCategory.WindQw,
StaticAnalysisSettings = 1,
SelfWeightActive = false,
},
// Nodal Loads | LC1
new Rfem.Loads.NodalLoad
{
No = 1,
Nodes = { 2, 4 },
LoadCase = 1,
LoadType = Rfem.Loads.NodalLoad.Types.LoadType.Components,
ComponentsForceX = -80000, // Compressive normal force (N)
},
// Member Loads | LC1
new Rfem.Loads.MemberLoad
{
No = 1,
Members = { memberEquivalentMemberMethod, memberGeneralMethod },
LoadCase = 1,
LoadType = Rfem.Loads.MemberLoad.Types.LoadType.Force,
LoadDistribution = Rfem.Loads.MemberLoad.Types.LoadDistribution.Uniform,
LoadDirection = Rfem.Loads.MemberLoad.Types.LoadDirection.GlobalZOrUserDefinedWTrueLength,
Magnitude = -2500, // Bending about the major y-axis (N/m)
},
// Member Loads | LC2
new Rfem.Loads.MemberLoad
{
No = 2,
Members = { memberEquivalentMemberMethod, memberGeneralMethod },
LoadCase = 2,
LoadType = Rfem.Loads.MemberLoad.Types.LoadType.Force,
LoadDistribution = Rfem.Loads.MemberLoad.Types.LoadDistribution.Uniform,
LoadDirection = Rfem.Loads.MemberLoad.Types.LoadDirection.GlobalYOrUserDefinedVTrueLength,
Magnitude = 1000, // Bending about the minor z-axis (N/m)
},
// Design Situations
new Rfem.Loading.DesignSituation
{
No = 1,
UserDefinedNameEnabled = true,
Name = "ULS - Permanent and transient",
DesignSituationType =
Rfem.Loading.DesignSituation.Types.DesignSituationType.StrPermanentAndTransient610,
ConsiderInclusiveExclusiveLoadCases = false,
},
// Load Combinations
new Rfem.Loading.LoadCombination
{
No = 1,
DesignSituation = 1,
Name = "CO1",
StaticAnalysisSettings = 1,
ToSolve = true,
Items = new Rfem.Loading.LoadCombination.Types.ItemsTable
{
Rows =
{
new Rfem.Loading.LoadCombination.Types.ItemsRow { LoadCase = 1, Factor = 1.35 },
new Rfem.Loading.LoadCombination.Types.ItemsRow { LoadCase = 2, Factor = 1.5 },
}
},
},
// Steel Design Configurations
//
// The configuration applies to both members. The stability object is
// what differs: effective lengths select the equivalent member method,
// boundary conditions select the 2D general method.
new Rfem.SteelDesignObjects.SteelDesignUlsConfiguration
{
No = 1,
UserDefinedNameEnabled = true,
Name = "ULS_configuration",
AssignedToAllMembers = true,
},
// Steel Effective Lengths | Member 1
new Rfem.SteelDesign.SteelEffectiveLengths
{
No = 1,
UserDefinedNameEnabled = true,
Name = "Equivalent member method",
Members = { memberEquivalentMemberMethod },
FlexuralBucklingAboutY = true,
FlexuralBucklingAboutZ = true,
TorsionalBuckling = true,
LateralTorsionalBuckling = true,
IntermediateNodes = false,
DifferentProperties = false,
DeterminationMcrEurope =
Rfem.SteelDesign.SteelEffectiveLengths.Types.DeterminationMcrEurope.Eigenvalue,
},
// Steel Boundary Conditions | Member 2
// The default definition type is 2D, which gives four degrees of
// freedom and fork supports at both member ends.
new Rfem.SteelDesign.SteelBoundaryConditions
{
No = 1,
UserDefinedNameEnabled = true,
Name = "2D general method",
Members = { memberGeneralMethod },
DefinitionType = Rfem.SteelDesign.SteelBoundaryConditions.Types.DefinitionType._2D,
},
});
// Calculate the model
rfemApp.calculate_all(skipWarnings: true);
// Retrieve the internal forces both members are designed for. They are
// identical, which is the point of the comparison.
Console.WriteLine("\nDesign internal forces | CO1");
Console.WriteLine($" {"Member",-8}{"N [kN]",12}{"M_y [kNm]",12}{"M_z [kNm]",12}");
foreach (var member in members)
{
var internalForces = rfemApp.get_results(
resultsType: Rfem.Results.ResultsType.StaticAnalysisMembersInternalForces,
filters: new List<Rfem.Results.ResultsFilter>
{
new() { ColumnId = "loading", FilterExpression = "CO1" },
new() { ColumnId = "member_no", FilterExpression = member.No.ToString() },
}
);
Console.WriteLine(
$" {member.No,-8}"
+ $"{MaxAbs(internalForces, "n") / 1000,12:F2}"
+ $"{MaxAbs(internalForces, "m_y") / 1000,12:F2}"
+ $"{MaxAbs(internalForces, "m_z") / 1000,12:F2}"
);
}
// Retrieve the design check ratios of both members, to show which columns
// the design results carry
var designChecks = rfemApp.get_results(
resultsType: Rfem.Results.ResultsType.SteelDesignMembersDesignRatiosByMember
);
Console.WriteLine("\nDesign Check Ratios:");
designChecks.Print();
// Design both members once per variant of the stability sub-options
foreach (var variant in designVariants)
{
// Retrieve the design configuration and write the settings of the
// variant into its tree table
var steelUlsConfig = rfemApp.get_object<Rfem.SteelDesignObjects.SteelDesignUlsConfiguration>(
new Rfem.SteelDesignObjects.SteelDesignUlsConfiguration { No = 1 }
);
var settingsEc3UlsTree = steelUlsConfig.SettingsEc3;
foreach (var setting in variant.Settings)
{
Common.TreeTable.SetValuesByKey(
tree: settingsEc3UlsTree,
key: setting.Key,
values: new List<object?> { setting.Value }
);
}
// Apply the updated configuration to the model
rfemApp.update_object(
obj: new Rfem.SteelDesignObjects.SteelDesignUlsConfiguration
{
No = 1, SettingsEc3 = settingsEc3UlsTree
}
);
// Calculate the model and retrieve the design check ratios
rfemApp.calculate_all(skipWarnings: true);
designChecks = rfemApp.get_results(
resultsType: Rfem.Results.ResultsType.SteelDesignMembersDesignRatiosByMember
);
Console.WriteLine($"\n{variant.Label}");
foreach (var member in members)
{
Console.WriteLine($"\n Member {member.No} | {member.Method}");
var memberChecks = designChecks.FilterEquals("member_no", member.No.ToString());
foreach (var checkType in member.CheckTypes)
{
// An outcome that did not occur in this design run has no row
var check = memberChecks.FilterEquals("design_check_type", checkType);
if (check.Data.Rows.Count == 0) continue;
Console.WriteLine($" {checkType} | {Cell(check, "design_check_description")}");
// An "ER" row states why no check was performed and carries no
// design ratio and no details. A missing ratio is reported as
// NaN, so it is filtered out by the parse below.
if (!double.TryParse(Cell(check, "design_ratio"),
NumberStyles.Float,
CultureInfo.InvariantCulture,
out var designRatio)
|| double.IsNaN(designRatio)) continue;
Console.WriteLine($" Design ratio: {designRatio:F3}");
// Retrieve the design check details and read the values of
// interest from them
if (member.DetailKeys.Length == 0) continue;
var designCheckDetails = rfemApp.get_results(
resultsType: Rfem.Results.ResultsType.SteelDesignDesignCheckDetails,
filters: new List<Rfem.Results.ResultsFilter>
{
new()
{
ColumnId = "design_check_details_id",
FilterExpression = Cell(check, "design_check_details_id"),
},
}
);
foreach (var key in member.DetailKeys)
{
var detail = designCheckDetails.FilterEquals("key", key);
Console.WriteLine(
$" {key} = {Cell(detail, "value")} [{Cell(detail, "unit")}]"
+ $"\t| {Cell(detail, "caption")}"
);
}
}
}
}
Console.WriteLine(
"\nBoth members carry the same internal forces, so the difference in the\n"
+ "design ratios comes from the stability method alone:\n"
+ "\n"
+ $" - Member {memberEquivalentMemberMethod} is verified with the interaction formulae 6.61 and 6.62.\n"
+ " Method 1 and Method 2 are two ways of determining the interaction\n"
+ " factors of the same formulae, so the ratio changes while the design\n"
+ " check stays the same.\n"
+ "\n"
+ $" - Member {memberGeneralMethod} is verified with the general method of 6.3.4, which starts\n"
+ " from the elastic critical moment of the member instead of from\n"
+ " tabulated interaction factors. It is a different mechanical model,\n"
+ " so its ratio is not expected to match the one of the equivalent\n"
+ " member method, and it needs the adapted method to accept M_z at all."
);
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
finally
{
if (rfemApp != null) rfemApp.close_connection();
}