Skip to content

ConventionalSteelBracedFrame

ConventionalSteelBracedFrame

ConventionalSteelBracedFrame(name_prefix: str = 'conventional_steel_braced_frame', num_stories: int = 5, x_bays: Optional[Sequence[float]] = None, y_bays: Optional[Sequence[float]] = None, story_heights: Optional[Sequence[float]] = None, floor_masses: Optional[Sequence[float]] = None, brace_bays_x: Optional[Dict[str, Sequence[int]]] = None, brace_bays_y: Optional[Dict[str, Sequence[int]]] = None, brace_pattern: str = 'x', brace_area_scale: float = 1.0, target_period: Optional[float] = None, column_sections: Optional[Union[str, Sequence[str], Dict[Tuple[int, int, int], str]]] = None, beam_sections: Optional[Union[str, Sequence[str], Dict[Tuple[int, str, int, int], str]]] = None, brace_sections: Optional[Union[str, Sequence[str], Dict[Tuple[str, int, int], str]]] = None, brace_area: Optional[float] = None, brace_E: Optional[float] = None, n_ele_col: int = 1, n_ele_beam: int = 1, length_unit_system: str = 'm', origin: Tuple[float, float, float] = (0.0, 0.0, 0.0), opensees_exe: Optional[str] = None)

Bases: Building

Generic conventional concentrically braced steel building archetype.

The default model is a symmetric 5-story, 24 m by 24 m braced frame intended for SSI/DRM studies. It is deliberately separate from FEMA_SAC_SteelFrame and represents a stiffer conventional building whose target fixed-base period follows T1 ~= 0.1 * num_stories.

Create the braced-frame archetype definition.

Parameters:

Name Type Description Default
name_prefix str

Mesh part and recorder name prefix.

'conventional_steel_braced_frame'
num_stories int

Number of stories. Defaults to 5.

5
x_bays Optional[Sequence[float]]

X-direction bay widths. Defaults to three 8 m bays.

None
y_bays Optional[Sequence[float]]

Y-direction bay widths. Defaults to three 8 m bays.

None
story_heights Optional[Sequence[float]]

Story heights. Defaults to [4.0, 3.5, ...].

None
floor_masses Optional[Sequence[float]]

True masses at floor COM nodes. Defaults to 0.0 per floor in SI unit systems.

None
brace_bays_x Optional[Dict[str, Sequence[int]]]

Braced X-bay indices on front and back lines.

None
brace_bays_y Optional[Dict[str, Sequence[int]]]

Braced Y-bay indices on left and right lines.

None
brace_pattern str

x, single_diagonal, chevron, or inverted_v.

'x'
brace_area_scale float

Multiplier applied to brace section/direct area.

1.0
target_period Optional[float]

Optional calibration target. Defaults to 0.1N.

None
column_sections Optional[Union[str, Sequence[str], Dict[Tuple[int, int, int], str]]]

Section override for columns.

None
beam_sections Optional[Union[str, Sequence[str], Dict[Tuple[int, str, int, int], str]]]

Section override for beams.

None
brace_sections Optional[Union[str, Sequence[str], Dict[Tuple[str, int, int], str]]]

Section override for braces.

None
brace_area Optional[float]

Direct brace area used when not using AISC sections.

None
brace_E Optional[float]

Elastic modulus for direct-area brace material. If omitted, the supplied steel material modulus is used when available.

None
n_ele_col int

Number of elements per column member.

1
n_ele_beam int

Number of elements per beam member.

1
length_unit_system str

Unit system passed to AISC section creation.

'm'
origin Tuple[float, float, float]

Building origin.

(0.0, 0.0, 0.0)
opensees_exe Optional[str]

Optional OpenSees executable path for validation.

None

Attributes

VALID_BRACE_PATTERNS class-attribute instance-attribute

VALID_BRACE_PATTERNS = {'x', 'single_diagonal', 'chevron', 'inverted_v'}

name_prefix instance-attribute

name_prefix = name_prefix

num_stories instance-attribute

num_stories = int(num_stories)

x_bays instance-attribute

x_bays = list(x_bays) if x_bays is not None else [8.0, 8.0, 8.0]

y_bays instance-attribute

y_bays = list(y_bays) if y_bays is not None else [8.0, 8.0, 8.0]

story_heights instance-attribute

story_heights = list(story_heights) if story_heights is not None else [4.0] + [3.5] * (num_stories - 1)

num_x_grid instance-attribute

num_x_grid = len(x_bays) + 1

num_y_grid instance-attribute

num_y_grid = len(y_bays) + 1

origin instance-attribute

origin = origin

length_unit_system instance-attribute

length_unit_system = length_unit_system

n_ele_col instance-attribute

n_ele_col = int(n_ele_col)

n_ele_beam instance-attribute

n_ele_beam = int(n_ele_beam)

floor_masses instance-attribute

floor_masses = list(floor_masses) if floor_masses is not None else [0.0] * num_stories

brace_bays_x instance-attribute

brace_bays_x = {'front': list(get('front', [])) if brace_bays_x else [mid_x], 'back': list(get('back', [])) if brace_bays_x else [mid_x]}

brace_bays_y instance-attribute

brace_bays_y = {'left': list(get('left', [])) if brace_bays_y else [mid_y], 'right': list(get('right', [])) if brace_bays_y else [mid_y]}

brace_pattern instance-attribute

brace_pattern = brace_pattern

brace_area_scale instance-attribute

brace_area_scale = float(brace_area_scale)

target_period instance-attribute

target_period = float(target_period) if target_period is not None else 0.1 * num_stories

column_sections instance-attribute

column_sections = column_sections if column_sections is not None else 'W14X90'

beam_sections instance-attribute

beam_sections = beam_sections if beam_sections is not None else 'W18X35'

brace_sections instance-attribute

brace_sections = brace_sections if brace_sections is not None else 'HSS8X8X3/8'

brace_area instance-attribute

brace_area = brace_area

brace_E instance-attribute

brace_E = brace_E

opensees_exe instance-attribute

opensees_exe = opensees_exe

building_region instance-attribute

building_region: Optional[RegionBase] = None

_brace_elements instance-attribute

_brace_elements: List[TrussElement] = []

_com_node_tags instance-attribute

_com_node_tags: List[int] = []

_last_period_result instance-attribute

_last_period_result: Optional[Dict[str, object]] = None

_last_material instance-attribute

_last_material: Optional[Material] = None

_last_material_density instance-attribute

_last_material_density = 0.0

total_height property

total_height: float

Total building height.

plan_dimensions property

plan_dimensions: Tuple[float, float]

Plan dimensions (Lx, Ly).

Methods:

get_coordinates

get_coordinates() -> Tuple[np.ndarray, np.ndarray, np.ndarray]

Return X, Y, and Z coordinate arrays for grid and floor elevations.

build

build(model, material: Material, material_density: float = 0.0) -> CompositeMesh

Build columns, beams, braces, and COM nodes as a Femora mesh part.

create_rigid_diaphragms

create_rigid_diaphragms(model, verbose: bool = True) -> None

Create floor rigid diaphragms to COM nodes and fix COM vertical/rocking DOFs.

apply_fixed_base

apply_fixed_base(model, tol: float = 0.0001) -> None

Fix all base structural grid nodes for fixed-base period checks.

create_gravity_pattern

create_gravity_pattern(model, g: float) -> Pattern

Create a plain gravity load pattern using true floor masses times g.

get_modes

get_modes(num_modes: int, *, material: Optional[Union[Dict[str, object], Callable]] = None, material_density: Optional[float] = None, opensees_exe: Optional[str] = None, print_results: bool = False, plot: bool = False, plot_scale: Optional[float] = None) -> Dict[str, np.ndarray]

Return modal frequencies, periods, and eigenvectors.

This method builds a copy of the braced frame in a separate temporary Model instance, applies fixed-base and rigid-diaphragm constraints, runs OpenSees from a temporary Tcl script, and parses modal data from OpenSees output.

Parameters:

Name Type Description Default
num_modes int

Number of eigen modes to request from OpenSees.

required
material Optional[Union[Dict[str, object], Callable]]

Material definition for the isolated modal model. Use None to create the default A992 elastic isotropic steel material. Pass a dict to create an elastic isotropic material from values such as E, nu, and rho. Pass a callable when the material must be created manually inside the temporary modal model.

None
material_density Optional[float]

Density used by build() to compute member self-mass. This is separate from the material object because member mass is a building-generation calculation, while rho on the material is an OpenSees material parameter. If omitted, Femora uses material["rho"] for dict materials, attempts to infer density from callable-created materials, and otherwise falls back to the default A992 density.

None
opensees_exe Optional[str]

Optional OpenSees executable path. If omitted, the FEMORA_OPENSEES environment variable is used.

None
print_results bool

If True, print a formatted table of modal frequencies and periods.

False
plot bool

If True, show PyVista mode-shape plots for the requested modes using the isolated building mesh.

False
plot_scale Optional[float]

Optional deformation scale for plotting. If omitted, a scale is estimated from the mesh size and eigenvector magnitudes.

None

Returns:

Type Description
Dict[str, ndarray]

Dictionary with frequencies, periods, node_tags, and

Dict[str, ndarray]

eigenvectors. The eigenvector array has shape

Dict[str, ndarray]

(num_modes, num_nodes, ndof).

Material ownership

get_modes() builds a separate temporary Model() so modal analysis cannot accidentally mutate the user's active model. For that reason, do not pass a Material object from another model. Use a material dictionary or a callable factory so the material is created inside the temporary modal model with the correct managers and tags.

Examples:

Default A992 steel:

result = frame.get_modes(
    num_modes=6,
    opensees_exe=opensees_path,
    print_results=True,
)

Custom elastic isotropic material from a dictionary:

result = frame.get_modes(
    num_modes=6,
    material={
        "name": "CustomSteel",
        "E": 2.0e8,
        "nu": 0.3,
        "rho": 7.85,
    },
    opensees_exe=opensees_path,
)

Advanced material factory:

result = frame.get_modes(
    num_modes=6,
    material=lambda model: model.material.nd.elastic_isotropic(
        "CustomSteel",
        E=2.0e8,
        nu=0.3,
        rho=7.85,
    ),
    material_density=7.85,
)

get_recorders

get_recorders(model, *, file_name: Optional[str] = None, delta_t: Optional[float] = None, element_responses: List[str] = ['force'], node_responses: List[str] = ['displacement', 'acceleration'])

Return an MPCO recorder scoped to this building region.

view_story

view_story(story: int, mode: str = 'text') -> None

Print or plot a story layout with braced bay locations.

estimate_fixed_base_period

estimate_fixed_base_period(model, opensees_exe: Optional[str] = None, num_modes: int = 6, work_dir: Optional[Union[str, Path]] = None) -> Dict[str, object]

Run a fixed-base OpenSees eigen check and return periods/mode types.

calibrate_to_target_period

calibrate_to_target_period(model, target_period: Optional[float] = None, direction: str = 'x', tolerance: float = 0.1, max_iterations: int = 8, opensees_exe: Optional[str] = None) -> Dict[str, object]

Iteratively scale brace area to match the target fixed-base period.

summarize_archetype

summarize_archetype() -> Dict[str, object]

Return a dictionary summary of geometry, layout, masses, and calibration state.

_validate_brace_layout

_validate_brace_layout() -> None

_section_for

_section_for(source, story: int, i: int, j: int, default: str, direction: Optional[str] = None) -> str

_resolve_brace_E

_resolve_brace_E(material: Material) -> float

Young's modulus for a directly specified brace area (Elastic section).

_scale_elastic_section_geometry staticmethod

_scale_elastic_section_geometry(section: ElasticSection, factor: float) -> None

Scale area and inertias for period calibration (axial stiffness ∝ EA).

_get_or_create_brace_section

_get_or_create_brace_section(model, material: Material) -> ElasticSection

Elastic brace section referenced by trussSection elements.

_add_brace_pattern

_add_brace_pattern(add_member, brace_element, bl, br, tl, tr) -> None

_explicit_rot_mass_kind_for_truss staticmethod

_explicit_rot_mass_kind_for_truss(p1: ndarray, p2: ndarray) -> str

Map brace axis to the same branches as :class:FEMA_SAC_SteelFrame mass logic.

_add_half_rotational_explicit_mass staticmethod

_add_half_rotational_explicit_mass(grid: UnstructuredGrid, pid: int, M_rot_torsion: float, M_rot_iy: float, M_rot_iz: float, kind: str) -> None

Distribute half the member rotational mass inertia to pid (global Rx,Ry,Rz).

_add_member_self_mass

_add_member_self_mass(grid: UnstructuredGrid, model, material_density: float) -> None

Lumped translational + rotational mass per line member (explicit dynamics), matching steel_frame.

_add_center_of_mass_nodes

_add_center_of_mass_nodes(grid: UnstructuredGrid, model) -> pv.UnstructuredGrid

_find_com_node_tags

_find_com_node_tags(model) -> List[int]

_eigen_tcl

_eigen_tcl(output_file: Path, com_node_tags: Sequence[int], num_modes: int) -> str

_parse_eigen_output

_parse_eigen_output(output_file: Path) -> Dict[str, object]

_first_mode_for_direction

_first_mode_for_direction(result: Dict[str, object], direction: str) -> Optional[Dict[str, object]]

_print_validation_summary

_print_validation_summary(result: Dict[str, object], target_period: float) -> None