Source code for simdesign.rcmrf

"""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