← back to mjamiv__natural-language-builder

Function bodies 335 total

All specs Real LLM only Function bodies
compute_qult function · python · L615-L655 (41 LOC)
src/nlb/opensees/materials.py
def compute_qult(diameter: float, depth: float,
                 soil_layer: SoilLayer) -> float:
    """Compute ultimate tip bearing capacity.

    For clay: q_p = Nc * su (Nc = 9 for deep foundations).
    For sand: q_p = Nq * sigma_v' (Reese & O'Neill / FHWA method).

    Args:
        diameter:    Pile diameter (inches).
        depth:       Depth to pile tip (inches).
        soil_layer:  SoilLayer at tip.

    Returns:
        Ultimate tip resistance qult (kip) — total force at tip.

    Reference:
        Reese & O'Neill (1988). FHWA-HI-88-042.
        FHWA-NHI-10-016: Drilled Shafts, Chapter 13.
        AASHTO LRFD 10.8.3.5.
    """
    area = math.pi * (diameter / 2.0) ** 2  # in²

    if soil_layer.soil_type == "clay":
        # Bearing capacity: qp = Nc * su
        # Nc = 6[1 + 0.2(Z/D)] <= 9 for drilled shafts
        Nc = min(9.0, 6.0 * (1.0 + 0.2 * depth / diameter)) if diameter > 0 else 9.0
        qp = Nc * soil_layer.su  # ksi
    else:
        # Sand: q_p = 0.6 * Nq 
elastomeric_shear function · python · L662-L682 (21 LOC)
src/nlb/opensees/materials.py
def elastomeric_shear(tag: int, G: float, A: float, h: float) -> int:
    """Define elastic material for elastomeric bearing pad shear stiffness.

    Stiffness: k = G * A / h (kip/in).

    Args:
        tag: Material tag.
        G:   Shear modulus of elastomer (ksi). Typical: 0.080-0.175 ksi
             (AASHTO Table 14.7.6.2-1).
        A:   Plan area of elastomer (in²).
        h:   Total elastomer thickness (in), sum of all layers.

    Returns:
        Material tag.

    Reference:
        AASHTO LRFD 14.7.6.2: Design of Elastomeric Bearings.
    """
    k = G * A / h  # kip/in
    ops.uniaxialMaterial('Elastic', tag, k)
    return tag
friction_model function · python · L685-L720 (36 LOC)
src/nlb/opensees/materials.py
def friction_model(tag: int, mu_slow: float = 0.06, mu_fast: float = 0.10,
                   rate: float = 0.001) -> int:
    """Define velocity-dependent friction material for sliding bearings.

    Models the transition from static (slow) to kinetic (fast) friction,
    typical of PTFE/stainless steel sliding surfaces.

    Note: OpenSees uses ``frictionModel`` for friction pendulum elements.
    For simpler modeling, this uses a VelDepMultiLinear or equivalent.
    Here we use the Elastic material as a simplified placeholder with
    the slow friction coefficient, since VelDependent friction models
    are element-level in OpenSees (not uniaxial materials).

    For full friction pendulum modeling, use the SingleFPBearing element
    or FlatSliderBearing element directly.

    Args:
        tag:      Material tag.
        mu_slow:  Coefficient of friction at low velocity. Default: 0.06.
        mu_fast:  Coefficient of friction at high velocity. Default: 0.10.
        rate:     Rat
compression_only function · python · L723-L740 (18 LOC)
src/nlb/opensees/materials.py
def compression_only(tag: int, k: float) -> int:
    """Define elastic-no-tension (ENT) material.

    For bearings that only transmit compression (e.g., concrete on concrete,
    rocker bearings, expansion bearings under uplift).

    Args:
        tag: Material tag.
        k:   Compressive stiffness (kip/in).

    Returns:
        Material tag.

    Reference:
        AASHTO LRFD 14.8: Anchorage and Uplift.
    """
    ops.uniaxialMaterial('ENT', tag, k)
    return tag
steel_i_section function · python · L74-L145 (72 LOC)
src/nlb/opensees/sections.py
def steel_i_section(tag: int, d: float, bf_top: float, tf_top: float,
                    bf_bot: float, tf_bot: float, tw: float,
                    mat_flange: int, mat_web: int,
                    nf_flange: int = 4, nf_web: int = 8) -> int:
    """Create fiber section for a steel I-shape (plate girder or rolled).

    The section is modeled with rectangular fiber patches for each component:
    top flange, bottom flange, and web.

    Coordinate system:
        y=0 at centroid, positive up.
        z=0 at center of web.

    Args:
        tag:        Section tag.
        d:          Total depth (inches).
        bf_top:     Top flange width (inches).
        tf_top:     Top flange thickness (inches).
        bf_bot:     Bottom flange width (inches).
        tf_bot:     Bottom flange thickness (inches).
        tw:         Web thickness (inches).
        mat_flange: Material tag for flanges.
        mat_web:    Material tag for web.
        nf_flange:  Number of fibers across flan
composite_section function · python · L152-L237 (86 LOC)
src/nlb/opensees/sections.py
def composite_section(tag: int, steel_section: Dict[str, float],
                      slab_width: float, slab_thick: float,
                      haunch: float, mat_steel: int,
                      mat_concrete: int,
                      nf_slab: int = 4, nf_flange: int = 4,
                      nf_web: int = 8) -> int:
    """Create composite steel + concrete slab fiber section.

    Models a steel I-girder acting compositely with a concrete deck slab.
    The slab is placed above the steel section with an optional haunch.

    Args:
        tag:            Section tag.
        steel_section:  Dict with keys: 'd', 'bf_top', 'tf_top', 'bf_bot',
                        'tf_bot', 'tw' (all in inches).
        slab_width:     Effective slab width (inches). Per AASHTO 4.6.2.6.
        slab_thick:     Slab thickness (inches).
        haunch:         Haunch depth between top flange and slab bottom (inches).
        mat_steel:      Material tag for steel.
        mat_concrete:   Material 
circular_rc_section function · python · L244-L298 (55 LOC)
src/nlb/opensees/sections.py
def circular_rc_section(tag: int, diameter: float, cover: float,
                        num_bars: int, bar_area: float,
                        mat_confined: int, mat_unconfined: int,
                        mat_steel: int,
                        n_core_circ: int = 16, n_core_rad: int = 8,
                        n_cover_circ: int = 16, n_cover_rad: int = 2) -> int:
    """Create fiber section for a circular reinforced concrete column.

    Divides the section into:
    1. Core (confined concrete): inside the reinforcing cage
    2. Cover (unconfined concrete): outside the cage to surface
    3. Reinforcing steel: single layer of bars on a circle

    Args:
        tag:             Section tag.
        diameter:        Outer diameter (inches).
        cover:           Clear cover to outside of hoop/spiral (inches).
        num_bars:        Number of longitudinal bars.
        bar_area:        Area of each bar (in²). e.g., #8 = 0.79 in².
        mat_confined:    Material tag for confi
All rows scored by the Repobility analyzer (https://repobility.com)
BarLayout class · python · L306-L316 (11 LOC)
src/nlb/opensees/sections.py
class BarLayout:
    """Defines reinforcing bar layout for one face of a rectangular section.

    Attributes:
        num_bars: Number of bars on this face.
        bar_area: Area of each bar (in²).
        face:     "top", "bottom", "left", "right", or "corner".
    """
    num_bars: int
    bar_area: float
    face: str = "bottom"
rectangular_rc_section function · python · L319-L403 (85 LOC)
src/nlb/opensees/sections.py
def rectangular_rc_section(tag: int, width: float, height: float,
                           cover: float, bars_layout: List[BarLayout],
                           mat_confined: int, mat_unconfined: int,
                           mat_steel: int,
                           nfy_core: int = 10, nfz_core: int = 10,
                           nf_cover: int = 2) -> int:
    """Create fiber section for a rectangular reinforced concrete column.

    Divides the section into confined core, four cover patches (top, bottom,
    left, right), and reinforcing bar layers.

    Args:
        tag:            Section tag.
        width:          Section width, z-direction (inches).
        height:         Section height, y-direction (inches).
        cover:          Clear cover (inches).
        bars_layout:    List of BarLayout objects defining reinforcement.
        mat_confined:   Material tag for confined concrete.
        mat_unconfined: Material tag for unconfined concrete.
        mat_steel:   
TendonProfile class · python · L411-L421 (11 LOC)
src/nlb/opensees/sections.py
class TendonProfile:
    """Tendon location within a section.

    Attributes:
        y:     Vertical position from section centroid (inches).
        z:     Horizontal position from section center (inches).
        area:  Tendon area (in²).
    """
    y: float
    z: float
    area: float
box_girder_section function · python · L424-L509 (86 LOC)
src/nlb/opensees/sections.py
def box_girder_section(tag: int, depth: float, top_width: float,
                       bot_width: float, top_thick: float,
                       bot_thick: float, web_thick: float,
                       num_cells: int, mat_concrete: int,
                       tendons: Optional[List[TendonProfile]] = None,
                       mat_strand: Optional[int] = None,
                       nf_slab: int = 4, nf_web: int = 8) -> int:
    """Create fiber section for a post-tensioned concrete box girder.

    Models single or multi-cell box with top slab, bottom slab, and webs.
    Optional tendons modeled as steel layers.

    Args:
        tag:          Section tag.
        depth:        Total depth (inches).
        top_width:    Top slab width (inches).
        bot_width:    Bottom slab width (inches).
        top_thick:    Top slab thickness (inches).
        bot_thick:    Bottom slab thickness (inches).
        web_thick:    Individual web thickness (inches).
        num_cells:    Numb
StrandPattern class · python · L517-L527 (11 LOC)
src/nlb/opensees/sections.py
class StrandPattern:
    """Strand pattern for prestressed girder.

    Attributes:
        rows: List of (y_from_bottom, num_strands) tuples.
        strand_area: Area per strand (in²). Default: 0.217 in² (0.6" dia).
        debond: Optional dict of {row_index: debond_length_inches}.
    """
    rows: List[Tuple[float, int]]
    strand_area: float = 0.217  # 0.6" diameter strand
    debond: Optional[Dict[int, float]] = None
prestressed_i_section function · python · L530-L616 (87 LOC)
src/nlb/opensees/sections.py
def prestressed_i_section(tag: int, girder_type: str,
                          mat_concrete: int,
                          strand_pattern: StrandPattern,
                          mat_strand: int,
                          nf_flange: int = 4, nf_web: int = 8) -> int:
    """Create fiber section for a prestressed concrete I-girder.

    Supports AASHTO, BT (Bulb-Tee), and NU (Nebraska University) girder types.
    Girder dimensions are looked up from the built-in GIRDER_LIBRARY.

    Args:
        tag:            Section tag.
        girder_type:    Key into GIRDER_LIBRARY (e.g., "BT_72", "AASHTO_IV").
        mat_concrete:   Material tag for concrete.
        strand_pattern: StrandPattern defining row positions and counts.
        mat_strand:     Material tag for prestressing strand.
        nf_flange:      Fibers through flange thickness. Default: 4.
        nf_web:         Fibers along web depth. Default: 8.

    Returns:
        Section tag.

    Raises:
        KeyError: If girde
AssembledModel class · python · L68-L105 (38 LOC)
src/nlb/tools/assembler.py
class AssembledModel:
    """Complete bridge model ready for analysis.

    Attributes:
        script:              Complete OpenSees Python script.
        node_count:          Total number of nodes.
        element_count:       Total number of elements.
        material_count:      Total number of materials/sections.
        load_cases:          Number of load cases.
        load_combinations:   Number of load combinations.
        analysis_sequence:   Ordered list of analysis step descriptions.
        nodes:               All node dicts with global tags.
        elements:            All element dicts with global tags.
        materials:           All material dicts with global tags.
        sections:            All section dicts with global tags.
        constraints:         All constraint dicts with global tags.
        boundary_conditions: All fixity dicts with global tags.
        connections:         Connection metadata (foundation→sub→bearing→super).
        bounding_cases:  
AnalysisResults class · python · L109-L126 (18 LOC)
src/nlb/tools/assembler.py
class AnalysisResults:
    """Results from all analyses.

    Attributes:
        envelopes:         {element_tag: {force_type: {max, min, controlling_combo}}}
        dcr:               {element_tag: {limit_state: dcr_value}}
        reactions:         {node_tag: {Fx, Fy, Fz, Mx, My, Mz} per combo}
        displacements:     {node_tag: {dx, dy, dz, rx, ry, rz} per combo}
        modal:             {mode: {period, frequency, mass_participation}}
        controlling_cases: [{element, check, dcr, combo, description}]
    """
    envelopes: dict = field(default_factory=dict)
    moving_load_envelopes: dict = field(default_factory=dict)
    dcr: dict = field(default_factory=dict)
    reactions: dict = field(default_factory=dict)
    displacements: dict = field(default_factory=dict)
    modal: dict = field(default_factory=dict)
    controlling_cases: list[dict] = field(default_factory=list)
Repobility · severity-and-effort ranking · https://repobility.com
TagRemapper class · python · L133-L195 (63 LOC)
src/nlb/tools/assembler.py
class TagRemapper:
    """Remaps local component tags to global non-colliding ranges.

    Each component tool assigns tags starting from 1. This remapper shifts
    all tags into the appropriate global range and tracks the mapping so
    inter-component connections can be resolved.
    """

    def __init__(self, component: str, support_index: int = 0):
        """
        Args:
            component:     One of 'foundation', 'substructure', 'bearing', 'superstructure'.
            support_index: Index of the support (0, 1, 2, ...) to space out
                          foundations/substructures/bearings within their range.
        """
        self.component = component
        self.support_index = support_index
        ranges = TAG_RANGES[component]

        # Compute per-support offset within the component range
        range_size = ranges["node"][1] - ranges["node"][0] + 1
        # Divide range evenly among supports (max 20 supports)
        max_supports = 20
        per_support =
__init__ method · python · L141-L163 (23 LOC)
src/nlb/tools/assembler.py
    def __init__(self, component: str, support_index: int = 0):
        """
        Args:
            component:     One of 'foundation', 'substructure', 'bearing', 'superstructure'.
            support_index: Index of the support (0, 1, 2, ...) to space out
                          foundations/substructures/bearings within their range.
        """
        self.component = component
        self.support_index = support_index
        ranges = TAG_RANGES[component]

        # Compute per-support offset within the component range
        range_size = ranges["node"][1] - ranges["node"][0] + 1
        # Divide range evenly among supports (max 20 supports)
        max_supports = 20
        per_support = range_size // max_supports

        self.offsets = {}
        for kind in ("node", "element", "material"):
            base = ranges[kind][0]
            self.offsets[kind] = base + support_index * per_support

        self._map: dict[str, dict[int, int]] = {"node": {}, "element": {}, "mater
remap method · python · L165-L179 (15 LOC)
src/nlb/tools/assembler.py
    def remap(self, local_tag: int, kind: str) -> int:
        """Remap a local tag to global.

        Args:
            local_tag: Original tag from component tool.
            kind:      'node', 'element', or 'material'.

        Returns:
            Global tag.
        """
        if local_tag in self._map[kind]:
            return self._map[kind][local_tag]
        global_tag = self.offsets[kind] + local_tag
        self._map[kind][local_tag] = global_tag
        return global_tag
get_global method · python · L181-L183 (3 LOC)
src/nlb/tools/assembler.py
    def get_global(self, local_tag: int, kind: str) -> int:
        """Look up a previously remapped tag."""
        return self._map[kind].get(local_tag, self.offsets[kind] + local_tag)
_normalize_component function · python · L198-L327 (130 LOC)
src/nlb/tools/assembler.py
def _normalize_component(component: dict) -> dict:
    """Normalize all tags in a component dict so they start from 1.
    
    Component tools may use arbitrary internal tag ranges (e.g., foundation
    starts nodes at 1004 or 2000). This renumbers everything to start from 1
    so the TagRemapper works correctly.
    """
    comp = dict(component)
    
    # Build offset maps for each tag type
    for tag_type, list_key in [("node", "nodes"), ("element", "elements"), ("material", "materials"), ("section", "sections")]:
        items = comp.get(list_key, [])
        if not items:
            continue
        tags = [item.get("tag", 0) for item in items]
        if not tags or min(tags) <= 1:
            continue
        
        min_tag = min(tags)
        offset = min_tag - 1
        tag_map = {}
        
        # Renumber items
        new_items = []
        for item in items:
            new = dict(item)
            old_tag = new.get("tag", 0)
            new_tag = old_tag - offse
_remap_component_nodes function · python · L330-L339 (10 LOC)
src/nlb/tools/assembler.py
def _remap_component_nodes(nodes: list[dict], remapper: TagRemapper) -> list[dict]:
    """Remap node tags in a list of node dicts."""
    result = []
    for n in nodes:
        new = dict(n)
        new["tag"] = remapper.remap(n["tag"], "node")
        new["_original_tag"] = n["tag"]
        new["_component"] = remapper.component
        result.append(new)
    return result
_remap_component_elements function · python · L342-L364 (23 LOC)
src/nlb/tools/assembler.py
def _remap_component_elements(elements: list[dict], remapper: TagRemapper) -> list[dict]:
    """Remap element and node tags in a list of element dicts."""
    result = []
    for e in elements:
        new = dict(e)
        new["tag"] = remapper.remap(e["tag"], "element")
        new["_original_tag"] = e["tag"]
        new["_component"] = remapper.component
        if "nodes" in e:
            new["nodes"] = [remapper.remap(n, "node") for n in e["nodes"]]
        if "section" in e and isinstance(e["section"], int):
            new["section"] = remapper.remap(e["section"], "material")
        if "transform" in e and isinstance(e["transform"], int):
            new["transform"] = remapper.remap(e["transform"], "material")
        if "material" in e and isinstance(e["material"], int):
            new["material"] = remapper.remap(e["material"], "material")
        if "materials" in e and isinstance(e["materials"], list):
            new["materials"] = [
                remapper.remap(m, "
_remap_component_materials function · python · L367-L392 (26 LOC)
src/nlb/tools/assembler.py
def _remap_component_materials(materials: list[dict], remapper: TagRemapper) -> list[dict]:
    """Remap material tags (and cross-references) in a list of material dicts."""
    result = []
    for m in materials:
        new = dict(m)
        new["tag"] = remapper.remap(m["tag"], "material")
        new["_original_tag"] = m["tag"]
        new["_component"] = remapper.component
        # Remap cross-references in params if they are material tags
        if "params" in m and isinstance(m["params"], dict):
            params = dict(m["params"])
            for key in ("mat_confined", "mat_unconfined", "mat_steel",
                        "mat_concrete", "mat_flange", "mat_web", "mat_strand",
                        "material", "matTag"):
                if key in params and isinstance(params[key], int):
                    params[key] = remapper.remap(params[key], "material")
            # Handle nested dicts (FiberSection core/cover/steel)
            for sub_key in ("core", "cover", "s
Repobility — same analyzer, your code, free for public repos · /scan/
_remap_component_sections function · python · L395-L417 (23 LOC)
src/nlb/tools/assembler.py
def _remap_component_sections(sections: list[dict], remapper: TagRemapper) -> list[dict]:
    """Remap section tags and material references."""
    result = []
    for s in sections:
        new = dict(s)
        new["tag"] = remapper.remap(s["tag"], "material")  # sections share material tag space
        new["_original_tag"] = s["tag"]
        new["_component"] = remapper.component
        # Remap top-level material references
        for key in ("mat_confined", "mat_unconfined", "mat_steel",
                    "mat_concrete", "mat_flange", "mat_web", "mat_strand"):
            if key in new and isinstance(new[key], int):
                new[key] = remapper.remap(new[key], "material")
        if "params" in s and isinstance(s["params"], dict):
            params = dict(s["params"])
            for key in ("mat_confined", "mat_unconfined", "mat_steel",
                        "mat_concrete", "mat_flange", "mat_web", "mat_strand",
                        "matTag"):
                if 
_remap_constraints function · python · L420-L432 (13 LOC)
src/nlb/tools/assembler.py
def _remap_constraints(constraints: list[dict], remapper: TagRemapper) -> list[dict]:
    """Remap node references in constraints."""
    result = []
    for c in constraints:
        new = dict(c)
        if "master" in c:
            new["master"] = remapper.remap(c["master"], "node")
        if "slave" in c:
            new["slave"] = remapper.remap(c["slave"], "node")
        if "nodes" in c:
            new["nodes"] = [remapper.remap(n, "node") for n in c["nodes"]]
        result.append(new)
    return result
ConnectionError class · python · L439-L441 (3 LOC)
src/nlb/tools/assembler.py
class ConnectionError(Exception):
    """Raised when components cannot be connected."""
    pass
_connect_foundation_to_substructure function · python · L444-L481 (38 LOC)
src/nlb/tools/assembler.py
def _connect_foundation_to_substructure(
    fnd_remapper: TagRemapper,
    sub_remapper: TagRemapper,
    fnd_model: dict,
    sub_model: dict,
) -> list[dict]:
    """Connect foundation top node(s) to substructure base node(s).

    Uses equalDOF constraints to tie the foundation top to the substructure
    base at each support.

    Returns list of constraint dicts.
    """
    constraints = []

    fnd_top = fnd_model.get("top_node", 0)
    sub_bases = sub_model.get("base_nodes", [])

    if not sub_bases:
        raise ConnectionError(
            "Substructure has no base_nodes — cannot connect to foundation."
        )

    # Foundation top_node → substructure base_node(s)
    # If single foundation top, connect to all sub bases via equalDOF
    fnd_top_global = fnd_remapper.get_global(fnd_top, "node")

    for base_node in sub_bases:
        sub_base_global = sub_remapper.get_global(base_node, "node")
        constraints.append({
            "type": "equalDOF",
            "mas
_connect_substructure_to_bearing function · python · L484-L558 (75 LOC)
src/nlb/tools/assembler.py
def _connect_substructure_to_bearing(
    sub_remapper: TagRemapper,
    brg_remapper: TagRemapper,
    sub_model: dict,
    brg_model: dict,
) -> list[dict]:
    """Connect substructure top/cap nodes to bearing bottom nodes.

    Returns list of constraint dicts (equalDOF).
    """
    constraints = []

    # Prefer cap_nodes, fall back to top_nodes
    sub_tops = sub_model.get("cap_nodes", []) or sub_model.get("top_nodes", [])
    brg_bots = brg_model.get("bottom_nodes", [])

    if not sub_tops:
        raise ConnectionError(
            "Substructure has no top_nodes or cap_nodes for bearing connection."
        )
    if not brg_bots:
        raise ConnectionError(
            "Bearing model has no bottom_nodes for substructure connection."
        )

    # Match one-to-one if counts match, or connect all bearings to first sub top
    if len(sub_tops) == len(brg_bots):
        for s_node, b_node in zip(sub_tops, brg_bots):
            s_global = sub_remapper.get_global(s_node, "nod
_connect_bearing_to_superstructure function · python · L561-L630 (70 LOC)
src/nlb/tools/assembler.py
def _connect_bearing_to_superstructure(
    brg_remapper: TagRemapper,
    sup_remapper: TagRemapper,
    brg_model: dict,
    sup_model: dict,
    support_index: int,
    num_supports: int,
    num_girders: int,
) -> list[dict]:
    """Connect bearing top nodes to superstructure support nodes.

    The superstructure support_nodes are ordered:
    [support_0_girder_0, support_0_girder_1, ..., support_1_girder_0, ...]

    Returns list of constraint dicts (equalDOF).
    """
    constraints = []

    brg_tops = brg_model.get("top_nodes", [])
    sup_supports = sup_model.get("support_nodes", [])

    if not brg_tops:
        raise ConnectionError("Bearing has no top_nodes for superstructure connection.")
    if not sup_supports:
        raise ConnectionError("Superstructure has no support_nodes for bearing connection.")

    # Extract superstructure nodes for this support line
    # support_nodes has num_girders entries per support, ordered by support then girder
    start = support_ind
enumerate_bounding_cases function · python · L645-L653 (9 LOC)
src/nlb/tools/assembler.py
def enumerate_bounding_cases() -> dict[str, dict[str, str]]:
    """Return the 4 standard bounding case combinations.

    UU = Upper foundation springs + Upper bearing stiffness (stiffer → higher forces)
    UL = Upper foundation springs + Lower bearing stiffness
    LU = Lower foundation springs + Upper bearing stiffness
    LL = Lower foundation springs + Lower bearing stiffness (softer → larger displacements)
    """
    return dict(BOUNDING_LABELS)
build_analysis_sequence function · python · L660-L695 (36 LOC)
src/nlb/tools/assembler.py
def build_analysis_sequence(
    has_seismic: bool = False,
    sdc: str = "B",
    has_moving_load: bool = True,
) -> list[str]:
    """Build the standard analysis sequence for a bridge model.

    Args:
        has_seismic:   Whether seismic load cases exist.
        sdc:           Seismic Design Category (A, B, C, D).
        has_moving_load: Whether to include HL-93 moving load analysis.

    Returns:
        Ordered list of analysis step names.
    """
    sequence = [
        "model_setup",          # ops.wipe() + ndm=3, ndf=6
        "define_materials",     # All materials, sections, transforms
        "define_nodes",         # All nodes with fixity
        "define_elements",      # All elements
        "gravity_analysis",     # DC + DW, load-controlled
        "modal_analysis",       # Eigenvalue → periods + mode shapes
    ]

    if has_moving_load:
        sequence.append("moving_load_analysis")  # HL-93 influence lines

    if has_seismic:
        sequence.append("response_s
Want this analysis on your repo? https://repobility.com/scan/
_generate_model_setup function · python · L702-L731 (30 LOC)
src/nlb/tools/assembler.py
def _generate_model_setup() -> str:
    """Generate the model setup preamble."""
    return textwrap.dedent("""\
        #!/usr/bin/env python3
        \"\"\"Auto-generated OpenSees bridge model.
        
        Generated by Natural Language Builder assembler tool.
        Units: kip-inch-second (KIS).
        \"\"\"
        
        import json
        import math
        import sys
        
        try:
            import openseespy.opensees as ops
        except (ImportError, RuntimeError):
            try:
                import opensees.openseespy as ops
            except ImportError:
                print("ERROR: openseespy not installed. pip install opensees")
                sys.exit(1)
        
        # ============================================================
        # MODEL SETUP
        # ============================================================
        ops.wipe()
        ops.model('basic', '-ndm', 3, '-ndf', 6)
        
    """)
_generate_materials_script function · python · L734-L1023 (290 LOC)
src/nlb/tools/assembler.py
def _generate_materials_script(materials: list[dict], sections: list[dict]) -> str:
    """Generate OpenSees material/section definition commands."""
    lines = [
        "# ============================================================",
        "# MATERIALS AND SECTIONS",
        "# ============================================================",
        "",
        "# Default geometric transforms (fallback for any component)",
        "ops.geomTransf('PDelta', 1, 0.0, 0.0, 1.0)",
        "ops.geomTransf('Linear', 2, 0.0, 0.0, 1.0)",
        "ops.geomTransf('Corotational', 3, 0.0, 0.0, 1.0)",
        "",
    ]

    # First pass: emit all geomTransf definitions (they share tag space but must not be deduped against materials)
    # We'll defer actual emission until after nodes are known, so we can pick correct vecxz
    # For now, collect transform metadata
    transform_meta = {}  # tag -> {type, vecxz}
    for m in materials:
        mtype = m.get("type", "")
        if mtype in ("geomT
_generate_nodes_script function · python · L1026-L1081 (56 LOC)
src/nlb/tools/assembler.py
def _generate_nodes_script(nodes: list[dict], boundary_conditions: list[dict],
                           elements: list[dict] | None = None,
                           constraints: list[dict] | None = None) -> str:
    """Generate OpenSees node definition commands."""
    lines = [
        "# ============================================================",
        "# NODES",
        "# ============================================================",
        "",
    ]

    # Build element/constraint connectivity set FIRST — needed to validate BCs
    connected = set()
    if elements is not None:
        for e in (elements or []):
            for n in e.get("nodes", []):
                connected.add(n)
    if constraints is not None:
        for c in (constraints or []):
            connected.add(c.get("master", 0))
            connected.add(c.get("slave", 0))

    # Collect fixed nodes for boundary conditions — only if actually connected
    fixed_nodes: dict[int, list[int]] = {}
    for
_generate_elements_script function · python · L1084-L1222 (139 LOC)
src/nlb/tools/assembler.py
def _generate_elements_script(elements: list[dict], all_nodes: list[dict] | None = None) -> str:
    """Generate OpenSees element definition commands."""
    lines = [
        "# ============================================================",
        "# ELEMENTS",
        "# ============================================================",
        "",
    ]

    # Build node coordinate lookup for orientation detection
    node_coords = {}
    if all_nodes:
        for n in all_nodes:
            node_coords[n["tag"]] = (n.get("x", 0.0), n.get("y", 0.0), n.get("z", 0.0))

    # Track orientation-specific transforms (created on the fly)
    # Base transforms 1-3 use vecxz=[0,0,1] — good for horizontal elements
    # Vertical elements (Y-dir) need vecxz=[1,0,0]  
    # Z-direction elements need vecxz=[0,1,0]
    orient_transforms = {}  # (base_tf, orient_key) -> new_tag
    next_orient_tf = 90001  # high range for auto-generated transforms

    def _get_oriented_transform(base_tf: int, n1: in
_generate_constraints_script function · python · L1225-L1253 (29 LOC)
src/nlb/tools/assembler.py
def _generate_constraints_script(constraints: list[dict]) -> str:
    """Generate equalDOF and rigidLink constraint commands."""
    lines = [
        "# ============================================================",
        "# CONSTRAINTS (inter-component connections)",
        "# ============================================================",
        "",
    ]

    for c in constraints:
        ctype = c.get("type", "equalDOF")
        if ctype == "equalDOF":
            master = c.get("master", 0)
            slave = c.get("slave", 0)
            dofs = c.get("dofs", [1, 2, 3, 4, 5, 6])
            conn = c.get("connection", "")
            if conn:
                lines.append(f"# {conn}")
            lines.append(
                f"ops.equalDOF({master}, {slave}, "
                f"{', '.join(str(d) for d in dofs)})"
            )
        elif ctype == "rigidLink":
            master = c.get("master", 0)
            slave = c.get("slave", 0)
            lines.append(f"ops.rigidLin
generate_script function · python · L1687-L1789 (103 LOC)
src/nlb/tools/assembler.py
def generate_script(model: AssembledModel) -> str:
    """Generate a standalone OpenSees Python script from an assembled model.

    The generated script can be run independently:
        python bridge_model.py

    Args:
        model: AssembledModel with all components assembled.

    Returns:
        Complete Python script as a string.
    """
    if model.num_girders <= 1:
        # Line-girder mode: use only superstructure + simple pin supports.
        # Skip substructure/foundation/bearing chain to avoid constraint
        # force amplification from rigidLinks + penalty constraints.
        sup_nodes = [n for n in model.nodes if n.get("_component") == "superstructure"]
        sup_elems = [e for e in model.elements if e.get("_component") == "superstructure"]
        sup_mats = [m for m in model.materials if m.get("_component") == "superstructure"]
        sup_secs = [s for s in model.sections if s.get("_component") == "superstructure"]
        # Identify support nodes from super
_sanitize_script_for_compatibility function · python · L1792-L1901 (110 LOC)
src/nlb/tools/assembler.py
def _sanitize_script_for_compatibility(script: str) -> str:
    """Replace materials/sections that crash on opensees 0.1.x arm64 Mac.

    Strategy: two-pass approach.
    Pass 1: Identify all fiber section blocks and their material dependencies.
    Pass 2: Replace Concrete01/Steel02 with Elastic, replace Fiber sections
             with Elastic sections, skip patch/layer commands.
    """
    import re
    lines = script.split('\n')

    # Pass 1: find all Fiber section tags and the materials referenced by patches/layers
    fiber_tags = set()
    fiber_mat_deps = set()  # material tags used inside fiber sections
    in_fiber = False
    for line in lines:
        s = line.strip()
        m = re.match(r"ops\.section\('Fiber',\s*(\d+)", s)
        if m:
            fiber_tags.add(int(m.group(1)))
            in_fiber = True
            continue
        if in_fiber:
            # Extract material tags from patch/layer calls
            pm = re.match(r"ops\.(?:patch|layer)\('\w+',\s*(\d
_emit_elastic_section function · python · L1904-L1912 (9 LOC)
src/nlb/tools/assembler.py
def _emit_elastic_section(out: list, tag: int, d: float):
    """Emit an elastic section approximating a circular RC section."""
    r = d / 2.0
    A = math.pi * r * r
    I = math.pi * r ** 4 / 4.0
    E = 4000.0  # approximate concrete secant modulus (ksi)
    out.append(f"# Fiber→Elastic: d={d:.1f} in, A={A:.1f}, I={I:.1f}")
    out.append(f"ops.section('Elastic', {tag}, {E}, {A:.2f}, {I:.2f}, {E}, {A:.2f}, {I / 2:.2f})")
    out.append("")
All rows scored by the Repobility analyzer (https://repobility.com)
_extract_empty_results function · python · L1919-L1928 (10 LOC)
src/nlb/tools/assembler.py
def _extract_empty_results() -> AnalysisResults:
    """Return an empty AnalysisResults placeholder."""
    return AnalysisResults(
        envelopes={},
        dcr={},
        reactions={},
        displacements={},
        modal={},
        controlling_cases=[],
    )
assemble_model function · python · L1935-L2204 (270 LOC)
src/nlb/tools/assembler.py
def assemble_model(
    site: dict,
    foundations: list[dict],
    substructures: list[dict],
    bearings: list[dict],
    superstructure: dict,
    loads: dict,
) -> AssembledModel:
    """Assemble all components into a complete OpenSees bridge model.

    This is the primary entry point. It:
    1. Validates component counts
    2. Remaps all tags to global ranges
    3. Connects components (foundation → substructure → bearing → superstructure)
    4. Builds the analysis sequence
    5. Generates the complete OpenSees script
    6. Enumerates bounding cases

    Args:
        site:           Site profile dict (from site_recon).
        foundations:     List of FoundationModel dicts (one per support).
        substructures:  List of SubstructureModel dicts (one per pier/abutment).
        bearings:       List of BearingModel dicts (one set per support).
        superstructure: SuperstructureModel dict.
        loads:          LoadModel dict.

    Returns:
        AssembledModel rea
run_analysis function · python · L2211-L2428 (218 LOC)
src/nlb/tools/assembler.py
def run_analysis(model: AssembledModel) -> AnalysisResults:
    """Execute full analysis sequence and extract results.

    This function requires openseespy to be installed. It:
    1. Generates the complete OpenSees script via generate_script()
    2. Executes the script (which builds model, runs gravity + modal)
    3. Extracts forces, displacements, reactions from the live OpenSees state
    4. Populates AnalysisResults with real data

    Args:
        model: AssembledModel from assemble_model().

    Returns:
        AnalysisResults with envelopes, DCRs, reactions, displacements, modal data.

    Note:
        If openseespy is not available, returns empty results with a warning.
    """
    import json as _json
    import subprocess
    import sys
    import tempfile
    import os

    results = AnalysisResults()

    # Generate the complete OpenSees script
    script = generate_script(model)

    # Build a wrapper script that:
    # 1. Runs the OpenSees analysis (gravity + modal
BearingType class · python · L65-L73 (9 LOC)
src/nlb/tools/bearings.py
class BearingType(str, Enum):
    ELASTOMERIC = "elastomeric"
    POT_FIXED = "pot_fixed"
    POT_GUIDED = "pot_guided"
    PTFE_SLIDING = "ptfe_sliding"
    FRICTION_PENDULUM_SINGLE = "fp_single"
    FRICTION_PENDULUM_TRIPLE = "fp_triple"
    INTEGRAL = "integral"
    ROCKER_ROLLER = "rocker_roller"
ElastomericConfig class · python · L81-L101 (21 LOC)
src/nlb/tools/bearings.py
class ElastomericConfig:
    """Steel-reinforced elastomeric bearing configuration.

    Attributes:
        length_in: Bearing length along bridge (inches).
        width_in: Bearing width transverse (inches).
        total_rubber_thickness_in: Sum of all rubber layers (inches).
        shear_modulus_ksi: Elastomer shear modulus G (ksi).
            AASHTO Table 14.7.6.2-1: 50 dur = 0.080-0.110 ksi,
            60 dur = 0.130-0.200 ksi.
        num_internal_layers: Number of internal rubber layers.
        layer_thickness_in: Individual rubber layer thickness (inches).
        steel_shim_thickness_in: Internal steel shim thickness (inches).
    """
    length_in: float = 14.0
    width_in: float = 9.0
    total_rubber_thickness_in: float = 2.5
    shear_modulus_ksi: float = 0.100
    num_internal_layers: int = 5
    layer_thickness_in: float = 0.50
    steel_shim_thickness_in: float = 0.105
PotBearingConfig class · python · L105-L115 (11 LOC)
src/nlb/tools/bearings.py
class PotBearingConfig:
    """Pot bearing configuration.

    Attributes:
        vertical_capacity_kip: Vertical load capacity (kip).
        guide_direction: For guided: 1=longitudinal, 3=transverse, None=fixed.
        stiffness_kip_per_in: Horizontal stiffness for "fixed" direction.
    """
    vertical_capacity_kip: float = 500.0
    guide_direction: int | None = None  # None=fixed, 1=long, 3=trans
    stiffness_kip_per_in: float = 1.0e6  # essentially rigid
PTFEConfig class · python · L119-L129 (11 LOC)
src/nlb/tools/bearings.py
class PTFEConfig:
    """PTFE sliding bearing configuration.

    Attributes:
        vertical_capacity_kip: Vertical capacity (kip).
        ptfe_type: One of: "unfilled", "glass_filled", "carbon_filled", "woven".
        contact_pressure_ksi: Average contact pressure on PTFE (ksi).
    """
    vertical_capacity_kip: float = 500.0
    ptfe_type: str = "glass_filled"
    contact_pressure_ksi: float = 3.0
FPSingleConfig class · python · L133-L145 (13 LOC)
src/nlb/tools/bearings.py
class FPSingleConfig:
    """Single friction pendulum bearing configuration.

    Attributes:
        radius_in: Radius of curvature R (inches).
        mu: Coefficient of friction at reference temperature.
        displacement_capacity_in: Maximum displacement (inches).
        vertical_capacity_kip: Vertical load capacity (kip).
    """
    radius_in: float = 40.0  # ~40" → T ≈ 2.0s
    mu: float = 0.06
    displacement_capacity_in: float = 8.0
    vertical_capacity_kip: float = 500.0
Repobility · severity-and-effort ranking · https://repobility.com
FPTripleConfig class · python · L149-L170 (22 LOC)
src/nlb/tools/bearings.py
class FPTripleConfig:
    """Triple friction pendulum bearing configuration.

    Attributes:
        R1, R2, R3, R4: Radii of curvature (inches).
        mu1, mu2, mu3, mu4: Friction coefficients per surface.
        d1, d2, d3, d4: Displacement capacities per surface (inches).
        vertical_capacity_kip: Vertical load capacity (kip).
    """
    R1: float = 12.0
    R2: float = 88.0
    R3: float = 88.0
    R4: float = 12.0
    mu1: float = 0.012
    mu2: float = 0.052
    mu3: float = 0.052
    mu4: float = 0.012
    d1: float = 3.0
    d2: float = 12.0
    d3: float = 12.0
    d4: float = 3.0
    vertical_capacity_kip: float = 500.0
RockerRollerConfig class · python · L174-L186 (13 LOC)
src/nlb/tools/bearings.py
class RockerRollerConfig:
    """Rocker/roller (steel) bearing configuration.

    Attributes:
        vertical_capacity_kip: Vertical capacity (kip).
        rocker_radius_in: Rocker contact radius (inches).
        roller_diameter_in: Roller diameter (inches) — 0 if rocker only.
        steel_fy_ksi: Steel yield strength (ksi).
    """
    vertical_capacity_kip: float = 300.0
    rocker_radius_in: float = 6.0
    roller_diameter_in: float = 0.0
    steel_fy_ksi: float = 36.0
BearingModel class · python · L194-L218 (25 LOC)
src/nlb/tools/bearings.py
class BearingModel:
    """Complete bearing model output.

    All tags are integers for direct OpenSees use.
    All coordinates in kip-inch-second.
    """
    nodes: list[dict] = field(default_factory=list)
    elements: list[dict] = field(default_factory=list)
    materials: list[dict] = field(default_factory=list)
    constraints: list[dict] = field(default_factory=list)
    top_nodes: list[int] = field(default_factory=list)
    bottom_nodes: list[int] = field(default_factory=list)
    properties: dict = field(default_factory=dict)
    cases: dict = field(default_factory=dict)
    warnings: list[str] = field(default_factory=list)
    bearing_type: str = ""
    compression_only: bool = True

    def summary(self) -> str:
        return (
            f"BearingModel ({self.bearing_type}): "
            f"{len(self.nodes)} nodes, {len(self.elements)} elements, "
            f"{len(self.materials)} materials | "
            f"top={self.top_nodes}, bot={self.bottom_nodes}"
        )
‹ prevpage 2 / 7next ›