"""A modular computational framework for the generation and nonlinear
modelling of reinforced concrete (RC) moment-resisting frames (MRFs).
It orchestrates the BCIM → BDIM → BNSM pipeline, generating portfolio-level
building class and design information data (exported CSV files), alongside
OpenSees-compatible nonlinear numerical models (in Python or Tcl).
:meth:`simdesign.rcmrf.generate` serves as the entry point for running the
pipeline.
Pipeline::
BCIM (Realisation Layer)
↓
BDIM (Design Layer)
↓
BNSM (Modelling Layer)
↓
OpenSees (Analysis Layer)
"""
from typing import Dict, Literal, Tuple, List
from pathlib import Path
import warnings
from .bcim import BCIM
from .bdim import BDIM, TaxonomyData
from .bdim.factory import BuildingBase as BuildingDesign
from .bnsm import BNSM
from .bnsm.factory import BuildingBase as BuildingModel
from .geometry import StandardGeometry, CustomGeometry
from ..utils.misc import make_dir
__all__ = ["BCIM", "BDIM", "BNSM",
"TaxonomyData", "StandardGeometry", "CustomGeometry"]
[docs]
def generate(
inputs: Dict[Literal['bcim', 'bnsm'], Dict], outdir: str | Path = None
) -> Tuple[BCIM, List[BuildingDesign | None], List[BuildingModel | None]]:
"""Generates BCIM, BDIM and BNSM data by following complete workflow
in Built Data Environment (BED) SimDesign framework for RC-MRF systems.
Parameters
----------
inputs : Dict[Literal['bcim', 'bnsm'], Dict]
Contains input parameters required for data generation.
These will replace the defaults obtained for the specified
`design_class`. The ``'opensees'`` entry of ``'bnsm'`` selects the
exported models, ``'py'`` (OpenSeesPy, default) or ``'tcl'``
(OpenSees Tcl). The given dictionaries are not modified.
outdir : str | Path, optional
Path to the user-defined output directory.
If None, set to `Path.cwd() / 'Outputs'`. by default None.
The directory is created if needed and its other contents are kept;
only the outputs written by this function (``BCIM.csv`` and the
``BDIM`` and ``BNSM`` subdirectories) are replaced.
Returns
-------
bcim : BCIM
Building Class Information Model.
bdim : List[BuildingDesign | None]
List of Building Design Information Models (None if design failed).
bnsm : List[BuildingModel | None]
List of Building Nonlinear Structural Models (None if design failed).
Raises
------
KeyError
`inputs['bcim']['design_class']` is not specified.
ValueError
`inputs['bnsm']['opensees']` is neither 'py' nor 'tcl'.
Examples
--------
Example dictionary to pass as ``inputs``:
.. code-block:: python
inputs = {
"bcim": {
"design_class": "eu_cdh",
"beta": 0.1,
"sample_size": 150,
"num_storeys": 5,
# Distribution parameters
"typical_storey_height": {
"cv": 0.07,
"mu": 2.90,
"lower_bound": 2.3,
"upper_bound": 3.8,
},
"staircase_bay_width": {
"lower_bound": 2.8,
"upper_bound": 3.2,
},
"standard_bay_width": {
"corr_coeff_xy": -0.92,
"lower_bound_x": 3.5,
"upper_bound_x": 7.5,
"theta_x": 4.5,
"sigma_x": 0.35,
"lower_bound_y": 3.5,
"upper_bound_y": 7.5,
"theta_y": 4.5,
"sigma_y": 0.35,
},
"steel": {
"grade": ["S400", "S500"],
"probability": [0.10, 0.90],
},
"concrete": {
"grade": ["C20/25", "C25/30", "C30/37", "C35/45"],
"probability": [0.30, 0.45, 0.20, 0.05],
},
"ground_storey_height": {
"maximum": 4.20,
"factor": [1.0, 1.1, 1.2, 1.3, 1.4],
"probability": [0.55, 0.10, 0.20, 0.10, 0.05],
},
"construction_quality": {"probability": [0.6, 0.3, 0.1]},
"slab_typology": {
"ss1_prob_given_ss1_or_hs": 0.50,
"ss2_prob_given_ss2_or_hs": 0.65,
"max_ss_short_span": 6.0,
"max_ss2_aspect_ratio": 2.0,
"staircase_slab_depth": 0.15,
"floor_slab_thickness": 0.15,
},
"wb_prob_given_hs": 0.50,
"square_column_prob": 0.50,
"layout": "all",
"seed": 1993,
},
"bnsm": {
"model": "CP01",
"load_factors": {"G": 1.0, "Q": 0.3},
"mass_factors": {"G": 1.0, "Q": 0.3},
"scheme": "EQL",
"max_drift": 0.05,
"dincr": 0.001,
"opensees": "py",
},
}
"""
# Get bcim generation settings
bcim_inputs = inputs.get('bcim')
if not bcim_inputs or not bcim_inputs.get('design_class'):
raise KeyError('design_class in bcim is not specified!')
# Get bnsm settings (a copy, the caller's inputs are not modified)
bnsm_inputs = dict(inputs.get('bnsm') or {})
lang = str(bnsm_inputs.pop('opensees', 'py') or 'py').strip().lower()
if lang not in ('py', 'tcl'):
raise ValueError(
f"Invalid opensees option: {lang!r}. Valid options are: "
"'py', 'tcl'.")
# Create output directory
if outdir is None:
outdir = Path.cwd() / 'Outputs'
else:
outdir = Path(outdir)
bcim_path = outdir / "BCIM.csv"
bdim_path = outdir / 'BDIM'
bnsm_path = outdir / 'BNSM'
outdir.mkdir(parents=True, exist_ok=True) # keep any existing contents
for path in (bdim_path, bnsm_path):
if path.exists():
warnings.warn(f"Replacing the existing outputs in {path}.",
UserWarning)
make_dir(path)
# Initialize BCIM
bcim = BCIM()
# Generate a building portfolio
bcim.generate(**bcim_inputs)
# BDIM database
bdim = []
# BNSM database
bnsm = []
# Loop through each building in portfolio
for i, taxonomy in enumerate(bcim.taxonomy):
print("----------------------------------------------------")
print(f"Designing {taxonomy.design_class} building",
f"{i + 1}/{len(bcim.taxonomy)} for beta={taxonomy.beta}g")
# Initialize BDIM
bdim_ = BDIM(taxonomy)
# To ensure the reproducibility of BDIM set the seed
bdim_.set_seed_for_quality_adjustments(bcim.inputs.seed + i)
# Make simumlated design
bdim_.run_iterative_design_algorithm()
# Update BCIM in case some properties changed during design
if bdim_.ok: # If the design is ok, continue with the model
bcim.column_section[i] = bdim_.column_section
bcim.beam_type[i] = bdim_.beam_type
bcim.concrete_grade[i] = bdim_.concrete_grade
bcim.steel_grade[i] = bdim_.steel_grade
# Export to csv
bdim_.to_csv(bdim_path / f'Building_{i + 1}')
# Initialize BNSM
kwargs = bnsm_inputs.copy()
kwargs['design'] = bdim_
bnsm_ = BNSM(**kwargs)
if lang == 'py':
# Export numerical models for OpenSeesPy
bnsm_.to_py(bnsm_path / f'Building_{i + 1}')
elif lang == 'tcl':
# Export numerical models for OpenSeesTcl
bnsm_.to_tcl(bnsm_path / f'Building_{i + 1}')
# Plot the model and save
bnsm_.plot_model(directory=bnsm_path / f'Building_{i + 1}',
show=False)
# Add to the BDIM and BNSM databases
bdim.append(bdim_)
bnsm.append(bnsm_)
else:
print(f"No design solution is found; building {i + 1} is "
"skipped.")
bdim.append(None)
bnsm.append(None)
# Save bcim information
bcim.to_csv(bcim_path)
return bcim, bdim, bnsm