Steel Hall#
|
Modelling a parametric steel hall using manipulation functions such as
Keywords:
steel hall portal frame manipulation mirror move rotate numbering increments purlins bracing |
from dlubal.api import rstab, common
import math
# -------------------------------------------------------
# This example demonstrates how to model a parametric steel
# hall with the help of manipulation functions like move,
# mirror or rotate including the copy option. The idea is to reduce
# manual modeling by leveraging symmetry and transformations.
#
# The tapering of the rafter is defined before the mirroring, so it
# has to be described only once - the mirror copies the attribute to
# the mirrored rafter. Both rafters keep their start at the eaves and
# their end at the ridge, so a tapering at the start of the member
# puts the haunch at the eaves on both slopes.
#
# The replication of the frames uses 'numbering_increments' to control
# the numbers of the created copies. The numbers of the k-th copy are
# '<original number> + k * <increment>', so every frame owns its own
# block of numbers and the purlins and bracing modelled afterwards
# derive the nodes of the neighbouring frame straight from the
# increment instead of counting the objects per frame.
# -------------------------------------------------------
# Editable parameters (SI units)
FRAME_SPACING = 5.0
FRAME_COUNT = 5
FRAME_WIDTH = 11.0
FRAME_HEIGHT_MIN = 4.5
FRAME_HEIGHT_MAX = 6.0
RAFTER_SLOPE_LENGTH = 0.75
PURLIN_FIELDS = 4
PURLIN_OFFSET_ON_TOP = 0.25
STEEL_GRADE = "S235 | EN 10025-2:2004-11"
CROSS_SECTION_COLUMN = "IPE 300"
CROSS_SECTION_RAFTER = "I 300/110/6/14/0/0/H"
CROSS_SECTION_RAFTER_TAPERED = "I 600/110/6/14/0/0/H"
CROSS_SECTION_PURLINS = "CHC 139.7x8.0"
CROSS_SECTION_BRACING = "R 20"
# Editable numbering increment of the copied frames. It has to be larger
# than the highest number used inside one frame, otherwise a copy would
# land on a number that is already taken and RSTAB refuses the whole
# move with "Object 'Node No. ...' already exists".
NUMBERING_INCREMENT_FRAME = 100
# Editable first numbers of the object groups modelled after the frames
NUMBER_FIRST_PURLIN = 1001
NUMBER_FIRST_BRACING = 2001
MODEL_NAME = 'steel_hall'
def build_half_portal_frame() -> list:
inf = float('inf')
portal_frame = [
rstab.structure_core.Material(no=1, name=STEEL_GRADE),
rstab.structure_core.CrossSection(no=1, name=CROSS_SECTION_COLUMN, material=1),
rstab.structure_core.CrossSection(no=2, name=CROSS_SECTION_RAFTER, material=1),
rstab.structure_core.CrossSection(no=3, name=CROSS_SECTION_RAFTER_TAPERED, material=1),
]
portal_frame.append(rstab.structure_core.Node(no=1))
portal_frame.append(rstab.structure_core.Node(no=2, coordinate_3=-FRAME_HEIGHT_MIN))
node_no = 3
rafter_length = math.sqrt((FRAME_HEIGHT_MAX - FRAME_HEIGHT_MIN)**2 + (FRAME_WIDTH / 2)**2)
purlin_offset = (rafter_length - PURLIN_OFFSET_ON_TOP) / PURLIN_FIELDS
for j in range(1, PURLIN_FIELDS+1):
portal_frame.append(
rstab.structure_core.Node(
no=node_no,
type=rstab.structure_core.Node.TYPE_ON_MEMBER,
on_member_reference_member=2,
distance_from_start_is_defined_as_relative=False,
distance_from_start_absolute=purlin_offset*j,
)
)
node_no += 1
portal_frame.append(rstab.structure_core.Node(no=node_no, coordinate_1=FRAME_WIDTH/2, coordinate_3=-FRAME_HEIGHT_MAX))
# Members
portal_frame.append(rstab.structure_core.Member(
no=1, node_start=1, node_end=2, cross_section_start=1,
type=rstab.structure_core.Member.TYPE_BEAM))
portal_frame.append(rstab.structure_core.Member(
no=2, node_start=2, node_end=node_no, cross_section_start=3,
type=rstab.structure_core.Member.TYPE_BEAM,
))
# Nodal support (hinge)
portal_frame.append(rstab.types_for_nodes.NodalSupport(
no=1, nodes=[1],
spring_x=inf, spring_y=inf, spring_z=inf,
rotational_restraint_z=inf
))
return portal_frame
def enhance_rafter_with_tapering() -> list:
# The rafter is modelled from the eaves to the ridge, so the haunch
# is a tapering at the start of the member
return [
rstab.structure_core.Member(
no=2,
cross_section_end=2,
cross_section_distribution_type=rstab.structure_core.Member.CROSS_SECTION_DISTRIBUTION_TYPE_TAPERED_AT_START_OF_MEMBER,
cross_section_distance_from_start_is_defined_as_relative=False,
section_distance_from_start_absolute=RAFTER_SLOPE_LENGTH
)
]
def build_purlins_in_first_bay_half() -> list:
purlins = [
rstab.structure_core.CrossSection(no=4, name=CROSS_SECTION_PURLINS, material=1),
]
for purlin in range(1, PURLIN_FIELDS+2):
purlin_no = NUMBER_FIRST_PURLIN + (purlin - 1)
# Eaves node and the nodes on the rafter of the first frame, the
# second frame repeats them shifted by the frame increment
purlin_node_start = 2 + (purlin - 1)
purlin_node_end = purlin_node_start + NUMBERING_INCREMENT_FRAME
purlins.append(rstab.structure_core.Member(
no=purlin_no, cross_section_start=4,
node_start=purlin_node_start, node_end=purlin_node_end,
type=rstab.structure_core.Member.TYPE_TRUSS)
)
return purlins
def build_bracing_in_first_bay_half() -> list:
bracing = [
rstab.structure_core.CrossSection(no=5, name=CROSS_SECTION_BRACING, material=1)
]
for i in range(1, PURLIN_FIELDS + 2):
for j in range(2):
bracing_no = NUMBER_FIRST_BRACING + 2*(i - 1) + j
# Diagonals of one bracing field, crossing between the first
# and the second frame
bracing_node_start = i + j
bracing_node_end = (i - j + 1) + NUMBERING_INCREMENT_FRAME
bracing.append(rstab.structure_core.Member(
no=bracing_no, cross_section_start=5,
node_start=bracing_node_start, node_end=bracing_node_end,
type=rstab.structure_core.Member.TYPE_TRUSS_ONLY_N
))
return bracing
def get_members_by_type(rstab_app, member_type) -> list:
member_list = rstab_app.get_object_list(
objs=[
rstab.structure_core.Member()
]
)
member_list_filtered = []
for member in member_list:
# == not is: these enum values are plain ints, so identity only
# happens to hold while they stay inside CPython's small-int cache.
if member.type == member_type:
member_list_filtered.append(member)
return member_list_filtered
# --- MAIN SCRIPT ---
# Connect to the RSTAB application
with rstab.Application() as rstab_app:
rstab_app.close_all_models(save_changes=False)
rstab_app.create_model(name=MODEL_NAME)
rstab_app.delete_all_objects()
# Build the half of the portal frame
portal_frame = build_half_portal_frame()
rstab_app.create_object_list(
objs=portal_frame
)
# Enhance the rafter with tapering before mirroring, the mirrored
# rafter then gets its haunch at the eaves as well
tapered_rafter = enhance_rafter_with_tapering()
rstab_app.update_object_list(
objs=tapered_rafter
)
# Mirror the half of the portal frame as copy to its mirrored position
rstab_app.mirror_objects(
objs=portal_frame,
mirroring_type=rstab.manipulation.MIRRORING_PLANE_SPECIFICATION_TYPE_POINT_AND_PARALLEL_PLANE,
plane=rstab.manipulation.PLANE_YZ,
point_1=common.Vector3d(x=FRAME_WIDTH/2,y=0,z=-FRAME_HEIGHT_MAX),
create_copy=True,
)
# Move the frame to create multiple frames by applying a vector displacement.
# Every frame is numbered from 1 + <frame index> * NUMBERING_INCREMENT_FRAME,
# which the purlins and the bracing rely on below. RSTAB models the topology
# with members only, so nodes and members are the only types to increment.
rstab_app.move_objects(
objs=rstab_app.get_object_list(
objs=[
rstab.structure_core.Node(),
rstab.structure_core.Member(),
]
),
create_copy=True,
number_of_steps=FRAME_COUNT-1,
direction_through=rstab.manipulation.DIRECTION_THROUGH_DISPLACEMENT_VECTOR,
displacement_vector=common.Vector3d(x=0.0, y=FRAME_SPACING, z=0.0),
numbering_increments={
rstab.OBJECT_TYPE_NODE: NUMBERING_INCREMENT_FRAME,
rstab.OBJECT_TYPE_MEMBER: NUMBERING_INCREMENT_FRAME,
},
)
# Model purlins between the first two frames, on one symmetric half of the hall
purlins = build_purlins_in_first_bay_half()
rstab_app.create_object_list(objs=purlins)
# Rotate purlins to symmetric half of the hall
rstab_app.rotate_objects(
objs=purlins,
rotation_angle=math.pi - 2*math.atan((FRAME_HEIGHT_MAX - FRAME_HEIGHT_MIN) / (FRAME_WIDTH / 2)),
point_1=common.Vector3d(x=FRAME_WIDTH/2,y=0,z=-FRAME_HEIGHT_MAX),
axis=rstab.manipulation.COORDINATE_AXIS_Y,
rotation_axis=rstab.manipulation.ROTATION_AXIS_SPECIFICATION_TYPE_POINT_AND_PARALLEL_AXIS,
create_copy=True,
number_of_steps=1
)
# Copy the purlins parallel to the axis for the remaining bays
rstab_app.move_objects(
objs=get_members_by_type(
rstab_app, rstab.structure_core.Member.TYPE_TRUSS
),
create_copy=True,
number_of_steps=FRAME_COUNT-2,
direction_through=rstab.manipulation.DIRECTION_THROUGH_PARALLEL_TO_AXIS,
axis=rstab.manipulation.COORDINATE_AXIS_Y,
spacing=FRAME_SPACING
)
# Model bracing between the first two frames, on one symmetric half of the hall
bracing = build_bracing_in_first_bay_half()
rstab_app.create_object_list(objs=bracing)
# Mirror the bracing to symmetric half of the hall
rstab_app.rotate_objects(
objs=get_members_by_type(
rstab_app, rstab.structure_core.Member.TYPE_TRUSS_ONLY_N
),
rotation_angle=math.pi,
point_1=common.Vector3d(x=FRAME_WIDTH/2,y=FRAME_SPACING/2,z=-FRAME_HEIGHT_MAX),
axis=rstab.manipulation.COORDINATE_AXIS_Z,
rotation_axis=rstab.manipulation.ROTATION_AXIS_SPECIFICATION_TYPE_POINT_AND_PARALLEL_AXIS,
create_copy=True,
number_of_steps=1
)
/**
Parametric steel hall modelled with the manipulation functions.
This example demonstrates how to model a parametric steel hall with the help of
manipulation functions like move, mirror or rotate including the copy option.
The idea is to reduce manual modelling by leveraging symmetry and transformations.
The tapering of the rafter is defined before the mirroring, so it has to be
described only once - the mirror copies the attribute to the mirrored rafter.
Both rafters keep their start at the eaves and their end at the ridge, so a
tapering at the start of the member puts the haunch at the eaves on both slopes.
The replication of the frames uses 'numberingIncrements' to control the numbers
of the created copies. The numbers of the k-th copy are
'<original number> + k * <increment>', so every frame owns its own block of
numbers and the purlins and bracing modelled afterwards derive the nodes of the
neighbouring frame straight from the increment instead of counting the objects
per frame.
**/
using Rstab = Dlubal.Api.Rstab;
using Common = Dlubal.Api.Common;
using Google.Protobuf;
// Editable parameters (SI units)
const double FRAME_SPACING = 5.0;
const int FRAME_COUNT = 5;
const double FRAME_WIDTH = 11.0;
const double FRAME_HEIGHT_MIN = 4.5;
const double FRAME_HEIGHT_MAX = 6.0;
const double RAFTER_SLOPE_LENGTH = 0.75;
const int PURLIN_FIELDS = 4;
const double PURLIN_OFFSET_ON_TOP = 0.25;
const string STEEL_GRADE = "S235 | EN 10025-2:2004-11";
const string CROSS_SECTION_COLUMN = "IPE 300";
const string CROSS_SECTION_RAFTER = "I 300/110/6/14/0/0/H";
const string MODEL_NAME = "steel_hall";
const string CROSS_SECTION_RAFTER_TAPERED = "I 600/110/6/14/0/0/H";
const string CROSS_SECTION_PURLIN = "CHC 139.7x8.0";
const string CROSS_SECTION_BRACING = "R 20";
// Editable numbering increment of the copied frames. It has to be larger than
// the highest number used inside one frame, otherwise a copy would land on a
// number that is already taken and RSTAB refuses the whole move with
// "Object 'Node No. ...' already exists".
const int NUMBERING_INCREMENT_FRAME = 100;
// Editable first numbers of the object groups modelled after the frames
const int NUMBER_FIRST_PURLIN = 1001;
const int NUMBER_FIRST_BRACING = 2001;
static List<IMessage> BuildHalfPortalFrame()
{
double inf = double.PositiveInfinity;
var portalFrame = new List<IMessage>
{
// Material
new Rstab.StructureCore.Material{No=1, Name=STEEL_GRADE},
// Cross-section
new Rstab.StructureCore.CrossSection{No=1, Name=CROSS_SECTION_COLUMN, Material=1},
new Rstab.StructureCore.CrossSection{No=2, Name=CROSS_SECTION_RAFTER, Material=1},
new Rstab.StructureCore.CrossSection{No=3, Name=CROSS_SECTION_RAFTER_TAPERED, Material=1},
};
// Nodes
portalFrame.Add(new Rstab.StructureCore.Node{No=1});
portalFrame.Add(new Rstab.StructureCore.Node{No=2, Coordinate3=-FRAME_HEIGHT_MIN});
var nodeNo = 3;
var rafterLength = Math.Sqrt(Math.Pow(FRAME_HEIGHT_MAX - FRAME_HEIGHT_MIN, 2) + Math.Pow(FRAME_WIDTH / 2, 2));
var purlinOffset = (rafterLength - PURLIN_OFFSET_ON_TOP) / PURLIN_FIELDS;
for (int j = 1; j <= PURLIN_FIELDS; j++)
{
var node = new Rstab.StructureCore.Node
{
No = nodeNo,
Type = Rstab.StructureCore.Node.Types.Type.OnMember,
OnMemberReferenceMember = 2,
DistanceFromStartIsDefinedAsRelative = false,
DistanceFromStartAbsolute = purlinOffset * j
};
portalFrame.Add(node);
nodeNo++;
}
portalFrame.Add(new Rstab.StructureCore.Node{No=nodeNo, Coordinate1=FRAME_WIDTH/2, Coordinate3=-FRAME_HEIGHT_MAX});
// Members
portalFrame.Add(new Rstab.StructureCore.Member{
No=1, NodeStart=1, NodeEnd=2, CrossSectionStart=1,
Type=Rstab.StructureCore.Member.Types.Type.Beam
});
portalFrame.Add(new Rstab.StructureCore.Member{
No=2, NodeStart=2, NodeEnd=nodeNo, CrossSectionStart=3,
Type=Rstab.StructureCore.Member.Types.Type.Beam
});
// Nodal support (hinge)
portalFrame.Add(new Rstab.TypesForNodes.NodalSupport{
No=1, Nodes={ 1 },
SpringX=inf, SpringY=inf, SpringZ=inf,
RotationalRestraintZ=inf
});
return portalFrame;
}
static List<IMessage> EnhanceRafterWithTapering()
{
// The rafter is modelled from the eaves to the ridge, so the haunch is a
// tapering at the start of the member
return new List<IMessage>
{
new Rstab.StructureCore.Member
{
No = 2,
CrossSectionEnd = 2,
CrossSectionDistributionType = Rstab.StructureCore.Member.Types.CrossSectionDistributionType.TaperedAtStartOfMember,
CrossSectionDistanceFromStartIsDefinedAsRelative = false,
SectionDistanceFromStartAbsolute = RAFTER_SLOPE_LENGTH
}
};
}
static List<IMessage> BuildPurlinsInFirstBayHalf()
{
var purlins = new List<IMessage>
{
new Rstab.StructureCore.CrossSection
{
No = 4,
Name = CROSS_SECTION_PURLIN,
Material = 1
}
};
for (int purlin = 1; purlin <= PURLIN_FIELDS + 1; purlin++)
{
int purlinNo = NUMBER_FIRST_PURLIN + (purlin - 1);
// Eaves node and the nodes on the rafter of the first frame, the second
// frame repeats them shifted by the frame increment
int purlinNodeStart = 2 + (purlin - 1);
int purlinNodeEnd = purlinNodeStart + NUMBERING_INCREMENT_FRAME;
purlins.Add(new Rstab.StructureCore.Member
{
No = purlinNo,
NodeStart = purlinNodeStart,
NodeEnd = purlinNodeEnd,
CrossSectionStart = 4,
Type = Rstab.StructureCore.Member.Types.Type.Truss
});
}
return purlins;
}
static List<IMessage> BuildBracingInFirstBayHalf()
{
var bracing = new List<IMessage>
{
new Rstab.StructureCore.CrossSection
{
No = 5,
Name = CROSS_SECTION_BRACING,
Material = 1
}
};
for (int i = 1; i <= PURLIN_FIELDS + 1; i++)
{
for (int j = 0; j < 2; j++)
{
int bracingNo = NUMBER_FIRST_BRACING + 2 * (i - 1) + j;
// Diagonals of one bracing field, crossing between the first and
// the second frame
int bracingNodeStart = i + j;
int bracingNodeEnd = (i - j + 1) + NUMBERING_INCREMENT_FRAME;
bracing.Add(new Rstab.StructureCore.Member
{
No = bracingNo,
NodeStart = bracingNodeStart,
NodeEnd = bracingNodeEnd,
CrossSectionStart = 5,
Type = Rstab.StructureCore.Member.Types.Type.TrussOnlyN
});
}
}
return bracing;
}
static List<IMessage> GetMembersByType(
ApplicationRstab rstabApp,
Rstab.StructureCore.Member.Types.Type memberType)
{
var memberList = rstabApp.get_object_list(
objs: new List<IMessage>{
new Rstab.StructureCore.Member{}
}
);
var memberListFiltered = new List<IMessage>();
foreach (var obj in memberList)
{
if (obj is Rstab.StructureCore.Member member &&
member.Type == memberType)
{
memberListFiltered.Add(member);
}
}
return memberListFiltered;
}
// --- MAIN SCRIPT ---
ApplicationRstab? rstabApp = null;
try
{
// Connect to the RSTAB application
rstabApp = new ApplicationRstab();
rstabApp.close_all_models(saveChanges: false);
rstabApp.create_model(MODEL_NAME);
rstabApp.delete_all_objects();
// Build the half of the portal frame
var portalFrame = BuildHalfPortalFrame();
rstabApp.create_object_list(objs: portalFrame);
// Enhance the rafter with tapering before mirroring, the mirrored rafter
// then gets its haunch at the eaves as well
var taperedRafter = EnhanceRafterWithTapering();
rstabApp.update_object_list(objs: taperedRafter);
// Mirror the half of the portal frame as copy to its mirrored position
rstabApp.mirror_objects(
objs: portalFrame,
mirroringType: Rstab.Manipulation.MirroringPlaneSpecificationType.PointAndParallelPlane,
plane: Rstab.Manipulation.Plane.Yz,
point1: new Common.Vector3d{X=FRAME_WIDTH/2, Y=0, Z=-FRAME_HEIGHT_MAX},
createCopy: true
);
// Move the frame to create multiple frames by applying a vector displacement.
// Every frame is numbered from 1 + <frame index> * NUMBERING_INCREMENT_FRAME,
// which the purlins and the bracing rely on below. RSTAB models the topology
// with members only, so nodes and members are the only types to increment.
rstabApp.move_objects(
objs: rstabApp.get_object_list(
objs: new List<IMessage> {
new Rstab.StructureCore.Node {},
new Rstab.StructureCore.Member {}
}
),
createCopy: true,
numberOfSteps: FRAME_COUNT - 1,
directionThrough: Rstab.Manipulation.DirectionThrough.DisplacementVector,
displacementVector: new Common.Vector3d{X=0.0, Y=FRAME_SPACING, Z=0.0},
numberingIncrements: new Dictionary<Rstab.ObjectType, int>
{
{ Rstab.ObjectType.Node, NUMBERING_INCREMENT_FRAME },
{ Rstab.ObjectType.Member, NUMBERING_INCREMENT_FRAME },
}
);
// Model purlins between the first two frames, on one symmetric half of the hall
var purlins = BuildPurlinsInFirstBayHalf();
rstabApp.create_object_list(objs: purlins);
// Rotate purlins to symmetric half of the hall
rstabApp.rotate_objects(
objs: purlins,
rotationAngle: Math.PI - 2*Math.Atan((FRAME_HEIGHT_MAX - FRAME_HEIGHT_MIN) / (FRAME_WIDTH / 2)),
point1: new Common.Vector3d{X=FRAME_WIDTH/2, Y=0, Z=-FRAME_HEIGHT_MAX},
axis: Rstab.Manipulation.CoordinateAxis.Y,
rotationAxis: Rstab.Manipulation.RotationAxisSpecificationType.PointAndParallelAxis,
createCopy: true,
numberOfSteps: 1
);
// Copy the purlins parallel to the axis for the remaining bays
rstabApp.move_objects(
objs: GetMembersByType(
rstabApp, Rstab.StructureCore.Member.Types.Type.Truss
),
createCopy: true,
numberOfSteps: FRAME_COUNT-2,
directionThrough: Rstab.Manipulation.DirectionThrough.ParallelToAxis,
axis: Rstab.Manipulation.CoordinateAxis.Y,
spacing: FRAME_SPACING
);
// Model bracing between the first two frames, on one symmetric half of the hall
var bracing = BuildBracingInFirstBayHalf();
rstabApp.create_object_list(objs: bracing);
// Mirror the bracing to symmetric half of the hall
rstabApp.rotate_objects(
objs: GetMembersByType(
rstabApp, Rstab.StructureCore.Member.Types.Type.TrussOnlyN
),
rotationAngle: Math.PI,
point1: new Common.Vector3d{X=FRAME_WIDTH/2, Y=FRAME_SPACING/2, Z=-FRAME_HEIGHT_MAX},
axis: Rstab.Manipulation.CoordinateAxis.Z,
rotationAxis: Rstab.Manipulation.RotationAxisSpecificationType.PointAndParallelAxis,
createCopy: true,
numberOfSteps: 1
);
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
finally
{
if (rstabApp != null) rstabApp.close_connection();
}