# CivilPy
# Copyright (C) 2019-2026 Dane Parks
#
# SPDX-License-Identifier: MIT
# See the LICENSE file in the project root for full license text.
"""Ohio DOT standard roadway (at-grade) portable concrete barriers.
Office of Roadway Engineering successors to Structural Engineering's
PCB-91 (:mod:`civilpy.structural.odot.portable_barrier`): the same New
Jersey shape family, reused here via :class:`~civilpy.structural.odot
.bridge_railing.BridgeRailing` so ``name`` containing ``"portable"``
routes through :func:`civilpy.structural.rhino_barrier.shape_family`'s
freestanding symmetric F-shape profile exactly as PCB-91 does.
Both are pin-and-loop / hinge-bar connected precast segments, NOT
suitable at bridge deck edges or similar dropoffs (a fixed anchored
barrier or bridge-mounted PCB is required there instead).
Sources (SCD number — drawing date / latest cited revision):
RM-4.2 32 in Portable Concrete Barrier, New Jersey shape, pin & loop
connection (rev. 2026-01-16)
RM-4.1 50 in Portable Concrete Barrier + 50->32 in transition section
(hinge bar connection) (rev. 2020-01-17)
"""
from dataclasses import dataclass
from civilpy.structural.odot.bridge_railing import BridgeRailing
[docs]
@dataclass(frozen=True)
class TransitionSection:
"""A tapered end section joining two different barrier heights."""
scd: str
name: str
height_from_in: float
height_to_in: float
length_ft: float
notes: str = ""
_CATALOG: list[BridgeRailing] = [
# ============================================================ RM-4.2
# 32 in portable concrete barrier, New Jersey shape, pin & loop
# connection. Segments 10'-0" min to 20'-0" max (hinge-bar loop
# spacing varies with length: 4 spaces @ 19" for 10', 6 @ 16.7" for
# 12', 11 @ 17.8" for 20'). May be cast with rebar (Y301/X501) or
# welded wire fabric (6x6 W2.9/W2.9 WWF + 2 longitudinal #4 bars).
# f'c >= 4000 psi (CMS 499); connecting hardware galvanized per
# CMS 711.02/711.09. Not for use at bridge deck edges/dropoffs (use
# the Structural Engineering PCB-91 bridge-mounted barrier there).
BridgeRailing(
scd="RM-4.2", scd_date="2026-01-16",
designation="RM Portable (32 in, pin & loop)",
name="Portable concrete barrier, 32 in, New Jersey shape, pin & loop",
shape="New Jersey", material="precast concrete", test_level="",
height=32.0, base_width=24.0, top_width=6.0, f_c=4.0,
segment_length_ft=(10.0, 12.0, 20.0),
notes="Office of Roadway Engineering successor to PCB-91: pin & "
"loop connection (vs. PCB-91's hinge bar + through-bolt), segments "
"10-20 ft (vs. PCB-91's fixed 10/12 ft). Not for bridge deck edges "
"or similar dropoffs. Drawing states no numeric NCHRP/MASH test "
"level.",
),
# ============================================================ RM-4.1
# 50 in portable concrete barrier. Segments 12'-0" to 14'-0", hinge
# bar Type A/B loops + vertical pin connection (3/4 in dia., ASTM
# A36). Only the 50" Transition Section's 32 in end may attach to an
# Impact Attenuator or to RM-4.2's 32 in barrier -- never connect an
# Impact Attenuator directly to the 50 in end. Marked
# "PCB-RXX-350-TL3" (XX = year cast).
BridgeRailing(
scd="RM-4.1", scd_date="2020-01-17",
designation="RM Portable (50 in, hinge bar)",
name="Portable concrete barrier, 50 in, New Jersey shape, hinge bar",
shape="New Jersey", material="precast concrete", test_level="TL-3",
height=50.0, base_width=24.0, top_width=12.0, f_c=4.0,
segment_length_ft=(12.0, 14.0),
notes="Marked PCB-RXX-350-TL3. Not to be used on bridge deck edges "
"or similar dropoffs -- the only suitable barrier there is the 32 "
"in PCB per Structural Engineering's PCB-91 (or an approved "
"alternative). Mates to a 32 in barrier (RM-4.2 or PCB-91) only "
"through the 50 in Transition Section; Impact Attenuators may only "
"attach to that transition's 32 in end.",
),
]
#: Catalog keyed by ``designation``.
ROADWAY_PORTABLE_BARRIERS: dict[str, BridgeRailing] = {
b.designation: b for b in _CATALOG
}
#: The RM-4.1 50 in -> 32 in taper joining the two roadway PCB families.
TRANSITION_50_TO_32 = TransitionSection(
scd="RM-4.1", name="50 in to 32 in portable barrier transition section",
height_from_in=50.0, height_to_in=32.0, length_ft=6.0,
notes="Tapers RM-4.1's 50 in barrier down to a 32 in end compatible "
"with RM-4.2 or PCB-91; only this 32 in end may attach to an Impact "
"Attenuator or to Guardrail per RM-4.2 notes/MT-101.80.",
)
[docs]
def roadway_portable_barrier(designation: str) -> BridgeRailing:
"""Look up a roadway portable barrier by its ``designation`` (e.g.
``"RM Portable (32 in, pin & loop)"``)."""
try:
return ROADWAY_PORTABLE_BARRIERS[designation]
except KeyError:
raise ValueError(
f"unknown roadway portable barrier {designation!r}; choose one "
f"of {sorted(ROADWAY_PORTABLE_BARRIERS)}"
)
# ═══════════════ RM-4.7 thrie-beam transition between PCB types ═══════════
#
# Transcribed from SCD RM-4.7, "Thrie-Beam Transition for Portable
# Concrete Barrier" (rev. 2025-01-17, 3 sheets, one connection pair per
# sheet). A 6 ft-3 in nested 12-gauge thrie-beam bridges the <= 1 ft
# gap between two 32 in PCB runs of different shape families, with a
# thrie-beam terminal connector (SCD MGS-1.1) bolted to each barrier
# end and a common galvanized toe plate along the base.
RM47_SCD = "RM-4.7"
RM47_REVISION = "2025-01-17"
#: Nested thrie-beam element bridging the joint.
THRIE_BEAM_ELEMENT = "6 ft-3 in nested 12-gauge thrie-beam"
#: Maximum plan gap between the two PCB ends.
PCB_GAP_MAX_IN = 12.0
#: Minimum distance from the PCB end to the terminal connector.
CONNECTOR_END_DISTANCE_MIN_IN = 6.0
#: Terminal-connector through bolts: 7/8 in dia ASTM A325 or A449; at
#: least 5 installed, 3 in the outer vertical row, >= 6 in from the
#: segment end and >= 3 in from lifting holes/voids.
CONNECTOR_BOLT_SPEC = '7/8" dia through bolts, ASTM A325 or A449'
CONNECTOR_BOLTS_MIN = 5
#: Terminal-connector steel spacer (CMS 711.01, galvanized 711.02):
#: 6 x 10 x 1/4 in plate with 1 in dia holes; add spacers to fit field
#: conditions.
SPACER_PLATE_IN = (6.0, 10.0, 0.25)
#: Toe plate along the base (CMS 711.01, galvanized 711.02): 9 ft-0 in
#: x 5-1/2 in x 5/8 in with 24 holes (1 in dia) at 4-3/4 in; anchors
#: 7/8 x 6 in (CMS 712.01 or FF-S325 Grp VIII Type 1), >= 4 per end.
TOE_PLATE_IN = (108.0, 5.5, 0.625)
TOE_PLATE_ANCHOR_SPEC = '7/8" x 6" anchors, CMS 712.01'
TOE_PLATE_ANCHORS_MIN_PER_END = 4
#: Deployment limits (general notes, all sheets).
USE_LIMIT = "once per mile with project engineer approval"
UNANCHORED_PCB_MIN_FT = 100.0
[docs]
@dataclass(frozen=True)
class ThrieBeamPCBTransition:
"""One RM-4.7 connection pair (one sheet each). ``barrier_a`` /
``barrier_b`` name the generic/proprietary 32 in PCB shapes joined;
the hardware set is common to all three pairs."""
sheet: int
barrier_a: str
barrier_b: str
notes: str = ""
#: RM-4.7 connection pairs keyed by ``(a, b)`` shape names.
THRIE_BEAM_PCB_TRANSITIONS: dict[tuple[str, str], ThrieBeamPCBTransition] = {
(t.barrier_a, t.barrier_b): t
for t in (
ThrieBeamPCBTransition(
1, 'Generic 32" New Jersey shape PCB', 'Generic 32" F-shape PCB',
notes="May also connect barriers of the same shape. Not "
"approved for the J-J Hook 32 in New Jersey shape PCB."),
ThrieBeamPCBTransition(
2, 'Generic 32" New Jersey shape PCB', 'J-J Hook 32" F-shape PCB'),
ThrieBeamPCBTransition(
3, 'Generic 32" F-shape PCB', 'J-J Hook 32" F-shape PCB'),
)
}
[docs]
def thrie_beam_pcb_transition(barrier_a: str,
barrier_b: str) -> ThrieBeamPCBTransition:
"""Look up the RM-4.7 pair joining two PCB shapes (order-free).
Raises ``ValueError`` naming the cataloged pairs otherwise — in
particular there is **no** approved pair involving the J-J Hook
32 in New Jersey shape PCB."""
for (a, b), t in THRIE_BEAM_PCB_TRANSITIONS.items():
if {a, b} == {barrier_a, barrier_b}:
return t
raise ValueError(
f"RM-4.7 has no transition joining {barrier_a!r} to {barrier_b!r}; "
f"cataloged pairs are {sorted(THRIE_BEAM_PCB_TRANSITIONS)}")
[docs]
def thrie_beam_transition_notes() -> tuple[str, ...]:
"""The deployment / payment rules common to every RM-4.7 pair."""
return (
f"ODOT {RM47_SCD} thrie-beam transition for portable concrete "
f"barrier (rev. {RM47_REVISION})",
f"{THRIE_BEAM_ELEMENT} across a {PCB_GAP_MAX_IN:g} in max gap; "
"thrie-beam terminal connector (SCD MGS-1.1) each end, "
f"{CONNECTOR_BOLT_SPEC} ({CONNECTOR_BOLTS_MIN} min, outer "
"vertical row filled first, 6 in min from the segment end).",
"Galvanized terminal-connector spacer plate(s) "
f"{SPACER_PLATE_IN[0]:g} x {SPACER_PLATE_IN[1]:g} x "
f"{SPACER_PLATE_IN[2]:g} in and toe plate "
f"{TOE_PLATE_IN[0] / 12.0:g} ft x {TOE_PLATE_IN[1]:g} x "
f"{TOE_PLATE_IN[2]:g} in ({TOE_PLATE_ANCHOR_SPEC}, "
f"{TOE_PLATE_ANCHORS_MIN_PER_END} min per end).",
f"Use {USE_LIMIT}; {UNANCHORED_PCB_MIN_FT:g} ft of unanchored "
"PCB required each side; traffic on either or both sides.",
"Incidental to pay Item 622 - Portable Barrier, Unanchored "
"(all hardware, material, labor, installation, and removal).",
)