Design ------ The examples on this page demonstrate how to drive the full BED workflow (BDIM → BNSM) using a geometry object — either loaded from an Excel file (:class:`~simdesign.rcmrf.geometry.base.CustomGeometry`) or built programmatically (:class:`~simdesign.rcmrf.geometry.base.StandardGeometry`). A final section shows how to visualise the resulting design in 3-D using the ``design_plotter`` utility. Design with a custom geometry ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A geometry exported with :meth:`~simdesign.rcmrf.geometry.base.StandardGeometry.write_mesh_to_xlsx` can be reloaded as a :class:`~simdesign.rcmrf.geometry.base.CustomGeometry` object and used directly in design. .. code-block:: python import matplotlib.pyplot as plt from pathlib import Path from simdesign import rcmrf input_path = Path("example-custom-geometry.xlsx") custom_frame = rcmrf.CustomGeometry(input_path) custom_frame.add_new_elements_for_stairs() taxonomy_data = { "beta": 0.1, "beam_type": 2, "column_section": 1, "concrete_grade": "B225", "design_class": "eu_cdl", "quality": 1, "slab_type": 1, "steel_grade": "A40", "geometry": custom_frame, } taxonomy = rcmrf.TaxonomyData(**taxonomy_data) bdim = rcmrf.BDIM(taxonomy) bdim.run_iterative_design_algorithm() if bdim.ok: bdim.to_csv(Path("Outputs/BDIM-Data")) bnsm = rcmrf.BNSM( design=bdim, model="DP02", scheme="EQL", dincr=5e-4, max_drift=0.1, include_infills=False, ) bnsm.to_py(Path("Outputs/OpsPy-Model")) bnsm.to_tcl(Path("Outputs/OpsTcl-Model")) bnsm.do_modal(num_modes=2, out_dir=Path("Outputs/Modal-Results")) bnsm.plot_model(directory=Path("Outputs"), show=True) bnsm.plot_mode_shape(mode_number=1, contour="x", show=True, directory=Path("Outputs")) bnsm.plot_mode_shape(mode_number=2, contour="y", show=True, directory=Path("Outputs")) dx, vx, _ = bnsm.do_nspa(ctrl_dof=1, out_dir=Path("Outputs/NSPA-X")) dy, vy, _ = bnsm.do_nspa(ctrl_dof=2, out_dir=Path("Outputs/NSPA-Y")) plt.plot(dx, vx, label="X-dir") plt.plot(dy, vy, label="Y-dir") plt.xlabel("Control Node Displacement [m]") plt.ylabel("Base Shear [kN]") plt.legend() plt.savefig(Path("Outputs/nspa.svg"), dpi=300) plt.close() Design with a specific geometry ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This example shows an 8-storey, 7×5-bay RC-MRF with a non-uniform layout: two staircase bays, several modified bay widths, a modified ground-floor height, and column rotation angles and section shapes assigned per grid position. **Building geometry** .. code-block:: python from pathlib import Path import matplotlib.pyplot as plt from simdesign.rcmrf import StandardGeometry from simdesign import rcmrf outdir = Path("Outputs/specific-frame") regular_frame = StandardGeometry( num_storeys=8, storey_height=3.0, num_bays_x=7, bay_width_x=5.0, num_bays_y=5, bay_width_y=5.0, ) # Two staircase bays regular_frame.set_continuous_stairs_rectangles( stair_loc=(1, 2), stairs_width_x=2.5, stairs_width_y=3.3 ) regular_frame.set_continuous_stairs_rectangles( stair_loc=(5, 2), stairs_width_x=2.5, stairs_width_y=3.3 ) # Non-uniform floor heights and bay widths regular_frame.modify_floor_height(floor_id=1, height=3.4) regular_frame.modify_bay_width(bay_id=2, width=3.3, direction="x") regular_frame.modify_bay_width(bay_id=4, width=3.3, direction="x") regular_frame.modify_bay_width(bay_id=6, width=3.3, direction="x") regular_frame.modify_bay_width(bay_id=3, width=3.3, direction="y") # Exterior infills only regular_frame.add_infills( exterior=True, interior=False, ext_type="Medium", int_type="Weak" ) regular_frame.write_mesh_to_xlsx(outdir / "geometry.xlsx") regular_frame.add_new_elements_for_stairs(infills=True) regular_frame.export_mesh_to_html(str(outdir / "geometry.html")) .. raw:: html **Column rotation angles** The ``rot_angle`` attribute controls the orientation of a column's cross-section in the horizontal plane. Two values are supported: - ``0.0`` — the strong axis is aligned with the global X direction. - ``90.0`` — the strong axis is aligned with the global Y direction. Grids not listed in either group keep the default angle assigned during geometry construction. Each grid is identified by its ``(ix, iy)`` position in the plan. .. code-block:: python # Grids whose columns have their strong axis along X (rot_angle = 0°) grids_x = [ (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0), # front facade (1, 5), (2, 5), (3, 5), (4, 5), (5, 5), (6, 5), # back facade ] # Grids whose columns have their strong axis along Y (rot_angle = 90°) grids_y = [ (0, 1), (0, 2), (0, 3), (0, 4), # left facade (1, 1), (1, 2), (1, 3), (1, 4), (2, 1), (2, 2), (2, 3), (2, 4), (5, 1), (5, 2), (5, 3), (5, 4), (6, 1), (6, 2), (6, 3), (6, 4), (7, 1), (7, 2), (7, 3), (7, 4), # right facade ] # Apply rotation angles to all vertical elements at the listed grids for grid, lines in regular_frame.continuous_lines_along_z.items(): for line in lines: if grid in grids_x: line.rot_angle = 0.0 elif grid in grids_y: line.rot_angle = 90.0 **Simulated design (BDIM) with mixed column sections** .. code-block:: python taxonomy_data = { "beta": 0.09, "beam_type": 2, "column_section": 1, "slab_type": 1, "concrete_grade": "C30/37", "design_class": "eu_cdh", "quality": 0, "slab_orientation": 3, "steel_grade": "S500", "geometry": regular_frame, } taxonomy = rcmrf.TaxonomyData(**taxonomy_data) bdim = rcmrf.BDIM(taxonomy) # Corner and staircase grids keep square sections (section=1); # all other frame columns get rectangular sections (section=2) grids_s = [ (0, 0), (7, 0), (0, 5), (7, 5), (3, 1), (4, 1), (3, 2), (4, 2), (3, 3), (4, 3), (3, 4), (4, 4), ] for grid, columns_list in bdim.continuous_columns.items(): for columns in columns_list: for column in columns: column.section = 2 if grid in grids_x + grids_y else 1 bdim.run_iterative_design_algorithm() bdim.to_csv(outdir / "design") **Nonlinear model and analysis (BNSM)** .. code-block:: python bnsm = rcmrf.BNSM( design=bdim, scheme="EQL", dincr=1e-3, max_drift=0.1, model="DP04", include_infills=True, ) bnsm.to_py(outdir / "OpsPy-Model") bnsm.to_tcl(outdir / "OpsTcl-Model") bnsm.do_modal(num_modes=3, out_dir=outdir / "Modal-Results") bnsm.plot_model(directory=outdir, show=False) .. raw:: html .. code-block:: python bnsm.plot_mode_shape(mode_number=1, contour="y", show=False, directory=outdir) .. raw:: html .. code-block:: python bnsm.plot_mode_shape(mode_number=2, contour="x", show=False, directory=outdir) .. raw:: html .. code-block:: python dx, vx, _ = bnsm.do_nspa(ctrl_dof=1, out_dir=outdir / "NSPA-Results") dy, vy, _ = bnsm.do_nspa(ctrl_dof=2, out_dir=outdir / "NSPA-Results") plt.plot(dx, vx, label="X-dir") plt.plot(dy, vy, label="Y-dir") plt.xlabel("Control Node Displacement [m]") plt.ylabel("Base Shear [kN]") plt.legend() plt.savefig(outdir / "nspa.svg", dpi=300) plt.close() .. image:: ../../_static/design/nspa.svg :width: 100% :alt: Pushover curves in X and Y directions .. note:: The full source script for this example is available at ``scripts/design_with_specific_geometry.py`` in the repository. Visualising the design ^^^^^^^^^^^^^^^^^^^^^^^ The ``scripts/design_plotter.py`` utility reads the CSV outputs written by :meth:`~simdesign.rcmrf.bdim.baselib.building.BDIM.to_csv` and renders a 3-D model of beams, columns, slabs, staircases, and (optionally) infill panels using `PyVista `_. To use it, point ``design_dir`` to the folder produced by ``bdim.to_csv()`` and set ``infills_flag = True`` if infills were included in the design: .. code-block:: python import pyvista as pv import pandas as pd from pathlib import Path from scripts.design_plotter import ( create_beam, create_column, create_slab, create_staircase ) infills_flag = False design_dir = Path("Outputs/specific-frame/design") beams = pd.read_csv(design_dir / "beams.csv") columns = pd.read_csv(design_dir / "columns.csv") slabs = pd.read_csv(design_dir / "slabs.csv") stairs = pd.read_csv(design_dir / "stairs.csv") infills = pd.read_csv(design_dir / "infills.csv") joints = pd.read_csv(design_dir / "joints.csv") df_nodes = joints.set_index("id") plotter = pv.Plotter() beam_meshes = pv.MultiBlock() for _, row in beams.iterrows(): nI, nJ = row["node_i"], row["node_j"] coordsI = df_nodes.loc[nI, ["x-coord [m]", "y-coord [m]", "z-coord [m]"]].to_numpy() coordsJ = df_nodes.loc[nJ, ["x-coord [m]", "y-coord [m]", "z-coord [m]"]].to_numpy() beam_meshes.append(create_beam(coordsI, coordsJ, row["b [mm]"] / 1000, row["h [mm]"] / 1000)) plotter.add_mesh(beam_meshes.combine(), color="red") column_meshes = pv.MultiBlock() for _, row in columns.iterrows(): nI, nJ = row["node_i"], row["node_j"] coordsI = df_nodes.loc[nI, ["x-coord [m]", "y-coord [m]", "z-coord [m]"]].to_numpy() coordsJ = df_nodes.loc[nJ, ["x-coord [m]", "y-coord [m]", "z-coord [m]"]].to_numpy() column_meshes.append(create_column(coordsI, coordsJ, row["bx [mm]"] / 1000, row["by [mm]"] / 1000)) plotter.add_mesh(column_meshes.combine(), color="blue") slab_meshes = pv.MultiBlock() for _, row in slabs.iterrows(): coords = [ df_nodes.loc[row[n], ["x-coord [m]", "y-coord [m]", "z-coord [m]"]].to_numpy() for n in ["node_1", "node_2", "node_3", "node_4"] ] slab_meshes.append(create_slab(*coords, row["t [mm]"] / 1000)) plotter.add_mesh(slab_meshes.combine(), color="lightgray", opacity=0.6) if stairs.any().any(): stair_meshes = create_staircase(stairs, df_nodes) plotter.add_mesh(stair_meshes.combine(), color="green") # Export an interactive HTML file and open the interactive window plotter.export_html("specific-design.html") plotter.show() The interactive visualisation below was produced by the example above for the 8-storey specific-geometry building (beams in red, columns in blue, slabs in grey, staircases in green): .. raw:: html