concrete-beam-section #1
8 changed files with 961 additions and 583 deletions
28
concrete-beam/assets/beam-section.typ
Normal file
28
concrete-beam/assets/beam-section.typ
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Generated by beam_section.py from input.yaml - do not edit by hand.
|
||||||
|
#let beam-section() = {
|
||||||
|
box(width: 180pt, height: 180pt)[
|
||||||
|
#place(dx: 48pt, dy: 28pt, rect(width: 76pt, height: 114pt, fill: rgb("#f2f2f2"), stroke: 1pt + black))
|
||||||
|
#place(dx: 58.5pt, dy: 38.5pt, rect(width: 55pt, height: 93pt, radius: 3pt, stroke: 2pt + black))
|
||||||
|
#place(dx: 59.5pt, dy: 124.6pt, circle(radius: 2.969pt, fill: black))
|
||||||
|
#place(dx: 106.6pt, dy: 124.6pt, circle(radius: 2.969pt, fill: black))
|
||||||
|
#place(dx: 59.5pt, dy: 39.5pt, circle(radius: 2.375pt, fill: black))
|
||||||
|
#place(dx: 107.8pt, dy: 39.5pt, circle(radius: 2.375pt, fill: black))
|
||||||
|
#place(line(start: (48pt, 158pt), end: (124pt, 158pt), stroke: 0.6pt + black))
|
||||||
|
#place(line(start: (48pt, 155pt), end: (48pt, 161pt), stroke: 0.6pt + black))
|
||||||
|
#place(line(start: (124pt, 155pt), end: (124pt, 161pt), stroke: 0.6pt + black))
|
||||||
|
#place(dx: 48pt, dy: 161pt, box(width: 76pt, align(center, text(size: 7pt)[$b_w = 8 " in"$])))
|
||||||
|
#place(line(start: (42pt, 28pt), end: (42pt, 142pt), stroke: 0.6pt + black))
|
||||||
|
#place(line(start: (39pt, 28pt), end: (45pt, 28pt), stroke: 0.6pt + black))
|
||||||
|
#place(line(start: (39pt, 142pt), end: (45pt, 142pt), stroke: 0.6pt + black))
|
||||||
|
#place(dx: 0.5pt, dy: 82pt, text(size: 7pt)[$h = 12 " in"$])
|
||||||
|
#place(line(start: (110.1pt, 41.88pt), end: (129pt, 42pt), stroke: 0.5pt + rgb("#626b73")))
|
||||||
|
#place(dx: 108.6pt, dy: 40.38pt, circle(radius: 1.5pt, fill: black))
|
||||||
|
#place(dx: 132pt, dy: 37pt, text(size: 7pt, "(2) #4"))
|
||||||
|
#place(line(start: (113.5pt, 85pt), end: (129pt, 82pt), stroke: 0.5pt + rgb("#626b73")))
|
||||||
|
#place(dx: 112pt, dy: 83.5pt, circle(radius: 1.5pt, fill: black))
|
||||||
|
#place(dx: 132pt, dy: 77pt, text(size: 7pt, "#3 @ 6″"))
|
||||||
|
#place(line(start: (109.5pt, 127.5pt), end: (129pt, 128pt), stroke: 0.5pt + rgb("#626b73")))
|
||||||
|
#place(dx: 108pt, dy: 126pt, circle(radius: 1.5pt, fill: black))
|
||||||
|
#place(dx: 132pt, dy: 123pt, text(size: 7pt, "(2) #5"))
|
||||||
|
]
|
||||||
|
}
|
||||||
180
concrete-beam/beam_section.py
Normal file
180
concrete-beam/beam_section.py
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
HERE = Path(__file__).resolve().parent
|
||||||
|
|
||||||
|
SCALE = 9.5 # points per inch
|
||||||
|
LEFT = 48.0
|
||||||
|
RIGHT = 56.0
|
||||||
|
TOP = 28.0
|
||||||
|
BOTTOM = 38.0
|
||||||
|
|
||||||
|
STIRRUP_STROKE = 2.0 # drawn stirrup line width, points
|
||||||
|
STIRRUP_RADIUS = 3.0 # drawn stirrup corner radius, points
|
||||||
|
DRAW_COVER = 1.0 # schematic cover shown in the drawing, inches
|
||||||
|
|
||||||
|
MUTED = 'rgb("#626b73")'
|
||||||
|
|
||||||
|
|
||||||
|
def _f(value: float) -> str:
|
||||||
|
return f"{value:.4g}"
|
||||||
|
|
||||||
|
|
||||||
|
def _centers(size: float, number: int, bw: float, edge: float) -> list[float]:
|
||||||
|
half = size / 2.0
|
||||||
|
left = edge + half
|
||||||
|
right = bw - left
|
||||||
|
if number == 1:
|
||||||
|
return [bw / 2.0]
|
||||||
|
return [left + index * (right - left) / (number - 1) for index in range(number)]
|
||||||
|
|
||||||
|
|
||||||
|
def render(values: dict) -> str:
|
||||||
|
s = SCALE
|
||||||
|
bw = float(values["bw"])
|
||||||
|
h = float(values["h"])
|
||||||
|
stirrup_size = int(values["stirrup_size"])
|
||||||
|
stirrup_spacing = float(values["stirrup_spacing"])
|
||||||
|
bar_size = int(values["bar_size"])
|
||||||
|
bar_db = float(values["bar_db"])
|
||||||
|
n_bars = int(values["n_bars"])
|
||||||
|
top_bar_size = int(values["top_bar_size"])
|
||||||
|
top_bar_db = float(values["top_bar_db"])
|
||||||
|
n_top_bars = int(values["n_top_bars"])
|
||||||
|
|
||||||
|
w_sec = bw * s
|
||||||
|
h_sec = h * s
|
||||||
|
width = LEFT + w_sec + RIGHT
|
||||||
|
height = TOP + h_sec + BOTTOM
|
||||||
|
ox = LEFT
|
||||||
|
oy = TOP
|
||||||
|
|
||||||
|
def sx(x_in: float) -> float:
|
||||||
|
return ox + x_in * s
|
||||||
|
|
||||||
|
def sy(y_in: float) -> float:
|
||||||
|
return oy + (h - y_in) * s
|
||||||
|
|
||||||
|
edge = DRAW_COVER + STIRRUP_STROKE / s
|
||||||
|
inset = DRAW_COVER + STIRRUP_STROKE / s / 2.0
|
||||||
|
y_bottom = edge + bar_db / 2.0
|
||||||
|
bottom = _centers(bar_db, n_bars, bw, edge)
|
||||||
|
top_y = h - (edge + top_bar_db / 2.0)
|
||||||
|
top = _centers(top_bar_db, n_top_bars, bw, edge)
|
||||||
|
|
||||||
|
lines: list[str] = []
|
||||||
|
add = lines.append
|
||||||
|
add("// Generated by beam_section.py from input.yaml - do not edit by hand.")
|
||||||
|
add("#let beam-section() = {")
|
||||||
|
add(f" box(width: {_f(width)}pt, height: {_f(height)}pt)[")
|
||||||
|
|
||||||
|
add(
|
||||||
|
f" #place(dx: {_f(ox)}pt, dy: {_f(oy)}pt, "
|
||||||
|
f'rect(width: {_f(w_sec)}pt, height: {_f(h_sec)}pt, fill: rgb("#f2f2f2"), stroke: 1pt + black))'
|
||||||
|
)
|
||||||
|
add(
|
||||||
|
f" #place(dx: {_f(sx(inset))}pt, dy: {_f(sy(h - inset))}pt, "
|
||||||
|
f"rect(width: {_f((bw - 2.0 * inset) * s)}pt, height: {_f((h - 2.0 * inset) * s)}pt, "
|
||||||
|
f"radius: {_f(STIRRUP_RADIUS)}pt, stroke: {_f(STIRRUP_STROKE)}pt + black))"
|
||||||
|
)
|
||||||
|
|
||||||
|
for x_in in bottom:
|
||||||
|
r = bar_db / 2.0 * s
|
||||||
|
add(
|
||||||
|
f" #place(dx: {_f(sx(x_in) - r)}pt, dy: {_f(sy(y_bottom) - r)}pt, "
|
||||||
|
f"circle(radius: {_f(r)}pt, fill: black))"
|
||||||
|
)
|
||||||
|
for x_in in top:
|
||||||
|
r = top_bar_db / 2.0 * s
|
||||||
|
add(
|
||||||
|
f" #place(dx: {_f(sx(x_in) - r)}pt, dy: {_f(sy(top_y) - r)}pt, "
|
||||||
|
f"circle(radius: {_f(r)}pt, fill: black))"
|
||||||
|
)
|
||||||
|
|
||||||
|
dim_y = oy + h_sec + 16.0
|
||||||
|
add(
|
||||||
|
f" #place(line(start: ({_f(ox)}pt, {_f(dim_y)}pt), end: ({_f(ox + w_sec)}pt, {_f(dim_y)}pt), "
|
||||||
|
f"stroke: 0.6pt + black))"
|
||||||
|
)
|
||||||
|
for tick in (ox, ox + w_sec):
|
||||||
|
add(
|
||||||
|
f" #place(line(start: ({_f(tick)}pt, {_f(dim_y - 3)}pt), end: ({_f(tick)}pt, {_f(dim_y + 3)}pt), "
|
||||||
|
f"stroke: 0.6pt + black))"
|
||||||
|
)
|
||||||
|
add(
|
||||||
|
f" #place(dx: {_f(ox)}pt, dy: {_f(dim_y + 3)}pt, "
|
||||||
|
f'box(width: {_f(w_sec)}pt, align(center, text(size: 7pt)[$b_w = {_f(bw)} " in"$])))'
|
||||||
|
)
|
||||||
|
|
||||||
|
x_h = ox - 6.0
|
||||||
|
add(
|
||||||
|
f" #place(line(start: ({_f(x_h)}pt, {_f(oy)}pt), end: ({_f(x_h)}pt, {_f(oy + h_sec)}pt), "
|
||||||
|
f"stroke: 0.6pt + black))"
|
||||||
|
)
|
||||||
|
for tick in (oy, oy + h_sec):
|
||||||
|
add(
|
||||||
|
f" #place(line(start: ({_f(x_h - 3)}pt, {_f(tick)}pt), end: ({_f(x_h + 3)}pt, {_f(tick)}pt), "
|
||||||
|
f"stroke: 0.6pt + black))"
|
||||||
|
)
|
||||||
|
add(
|
||||||
|
f" #place(dx: 0.5pt, dy: {_f(oy + h_sec / 2.0 - 3)}pt, "
|
||||||
|
f'text(size: 7pt)[$h = {_f(h)} " in"$])'
|
||||||
|
)
|
||||||
|
|
||||||
|
label_x = ox + w_sec + 8.0
|
||||||
|
lead_x = ox + w_sec + 5.0
|
||||||
|
|
||||||
|
def leader(fx: float, fy: float, lx: float, ly: float) -> None:
|
||||||
|
add(
|
||||||
|
f" #place(line(start: ({_f(fx)}pt, {_f(fy)}pt), end: ({_f(lx)}pt, {_f(ly)}pt), "
|
||||||
|
f"stroke: 0.5pt + {MUTED}))"
|
||||||
|
)
|
||||||
|
add(f" #place(dx: {_f(fx - 1.5)}pt, dy: {_f(fy - 1.5)}pt, circle(radius: 1.5pt, fill: black))")
|
||||||
|
|
||||||
|
leader(sx(top[-1]), sy(top_y), lead_x, oy + 14.0)
|
||||||
|
add(
|
||||||
|
f" #place(dx: {_f(label_x)}pt, dy: {_f(oy + 9)}pt, "
|
||||||
|
f'text(size: 7pt, "({n_top_bars}) #{top_bar_size}"))'
|
||||||
|
)
|
||||||
|
|
||||||
|
leader(sx(bw - inset), sy(h / 2.0), lead_x, oy + h_sec / 2.0 - 3.0)
|
||||||
|
add(
|
||||||
|
f" #place(dx: {_f(label_x)}pt, dy: {_f(oy + h_sec / 2.0 - 8)}pt, "
|
||||||
|
f'text(size: 7pt, "#{stirrup_size} @ {_f(stirrup_spacing)}″"))'
|
||||||
|
)
|
||||||
|
|
||||||
|
leader(sx(bottom[-1]), sy(y_bottom), lead_x, oy + h_sec - 14.0)
|
||||||
|
add(
|
||||||
|
f" #place(dx: {_f(label_x)}pt, dy: {_f(oy + h_sec - 19)}pt, "
|
||||||
|
f'text(size: 7pt, "({n_bars}) #{bar_size}"))'
|
||||||
|
)
|
||||||
|
|
||||||
|
add(" ]")
|
||||||
|
add("}")
|
||||||
|
return "\n".join(lines) + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
def write_fragment(values: dict, path: Path) -> None:
|
||||||
|
path.write_text(render(values), encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: list[str] | None = None) -> int:
|
||||||
|
argv = sys.argv[1:] if argv is None else argv
|
||||||
|
input_path = Path(argv[0]) if argv else HERE / "input.yaml"
|
||||||
|
output_path = Path(argv[1]) if len(argv) > 1 else HERE / "assets" / "beam-section.typ"
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
import calc
|
||||||
|
|
||||||
|
with input_path.open(encoding="utf-8") as handle:
|
||||||
|
result = calc.compute(yaml.safe_load(handle))
|
||||||
|
write_fragment(result["values"], output_path)
|
||||||
|
print(output_path)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
|
|
@ -36,6 +36,23 @@ def quantity(value, unit: str, name: str) -> float:
|
||||||
return magnitude
|
return magnitude
|
||||||
|
|
||||||
|
|
||||||
|
def count(value, name: str, minimum: int = 1) -> int:
|
||||||
|
try:
|
||||||
|
number = int(value)
|
||||||
|
except (TypeError, ValueError) as exc:
|
||||||
|
raise ValueError(f"{name}: expected an integer, got {value!r}") from exc
|
||||||
|
if number < minimum:
|
||||||
|
raise ValueError(f"{name} must be >= {minimum}")
|
||||||
|
return number
|
||||||
|
|
||||||
|
|
||||||
|
def bar_number(value, name: str) -> int:
|
||||||
|
number = count(value, name, minimum=3)
|
||||||
|
if number > 18:
|
||||||
|
raise ValueError(f"{name} must be between 3 and 18")
|
||||||
|
return number
|
||||||
|
|
||||||
|
|
||||||
def compute(inp: dict) -> dict:
|
def compute(inp: dict) -> dict:
|
||||||
span = quantity(inp["span"], "ft", "span")
|
span = quantity(inp["span"], "ft", "span")
|
||||||
tributary = quantity(inp["tributary"], "ft", "tributary")
|
tributary = quantity(inp["tributary"], "ft", "tributary")
|
||||||
|
|
@ -46,8 +63,19 @@ def compute(inp: dict) -> dict:
|
||||||
d = quantity(inp["d"], "in", "d")
|
d = quantity(inp["d"], "in", "d")
|
||||||
fc = quantity(inp["fc"], "ksi", "fc")
|
fc = quantity(inp["fc"], "ksi", "fc")
|
||||||
fy = quantity(inp["fy"], "ksi", "fy")
|
fy = quantity(inp["fy"], "ksi", "fy")
|
||||||
As = quantity(inp["As"], "in**2", "As")
|
|
||||||
concrete_density = quantity(inp["concrete_density"], "pcf", "concrete_density")
|
concrete_density = quantity(inp["concrete_density"], "pcf", "concrete_density")
|
||||||
|
cover = quantity(inp["cover"], "in", "cover")
|
||||||
|
stirrup_size = bar_number(inp["stirrup_size"], "stirrup_size")
|
||||||
|
stirrup_spacing = quantity(inp["stirrup_spacing"], "in", "stirrup_spacing")
|
||||||
|
bar_size = bar_number(inp["bar_size"], "bar_size")
|
||||||
|
n_bars = count(inp["n_bars"], "n_bars")
|
||||||
|
top_bar_size = bar_number(inp["top_bar_size"], "top_bar_size")
|
||||||
|
n_top_bars = count(inp["n_top_bars"], "n_top_bars")
|
||||||
|
|
||||||
|
stirrup_db = stirrup_size / 8.0
|
||||||
|
bar_db = bar_size / 8.0
|
||||||
|
top_bar_db = top_bar_size / 8.0
|
||||||
|
As = n_bars * math.pi * bar_db**2 / 4.0
|
||||||
|
|
||||||
self_weight = (bw * h / 144.0) * concrete_density / 1000.0
|
self_weight = (bw * h / 144.0) * concrete_density / 1000.0
|
||||||
wD = D * tributary / 1000.0 + self_weight
|
wD = D * tributary / 1000.0 + self_weight
|
||||||
|
|
@ -73,6 +101,14 @@ def compute(inp: dict) -> dict:
|
||||||
Vc = 2.0 * math.sqrt(fc * 1000.0) * bw * d / 1000.0
|
Vc = 2.0 * math.sqrt(fc * 1000.0) * bw * d / 1000.0
|
||||||
phiVc = 0.75 * Vc
|
phiVc = 0.75 * Vc
|
||||||
|
|
||||||
|
bottom_centroid = h - d
|
||||||
|
min_bottom_centroid = cover + stirrup_db + bar_db / 2.0
|
||||||
|
if bottom_centroid < min_bottom_centroid:
|
||||||
|
raise ValueError("effective depth d is too large for the specified cover and bars")
|
||||||
|
inner_width = bw - 2.0 * (cover + stirrup_db)
|
||||||
|
if n_bars * bar_db > inner_width:
|
||||||
|
raise ValueError("bottom bars do not fit within the stirrup width")
|
||||||
|
|
||||||
def q(value: float) -> float:
|
def q(value: float) -> float:
|
||||||
return round(value, 6)
|
return round(value, 6)
|
||||||
|
|
||||||
|
|
@ -106,6 +142,16 @@ def compute(inp: dict) -> dict:
|
||||||
"As_min": q(As_min),
|
"As_min": q(As_min),
|
||||||
"Vc": q(Vc),
|
"Vc": q(Vc),
|
||||||
"phiVc": q(phiVc),
|
"phiVc": q(phiVc),
|
||||||
|
"cover": q(cover),
|
||||||
|
"stirrup_size": stirrup_size,
|
||||||
|
"stirrup_spacing": q(stirrup_spacing),
|
||||||
|
"stirrup_db": q(stirrup_db),
|
||||||
|
"bar_size": bar_size,
|
||||||
|
"n_bars": n_bars,
|
||||||
|
"bar_db": q(bar_db),
|
||||||
|
"top_bar_size": top_bar_size,
|
||||||
|
"n_top_bars": n_top_bars,
|
||||||
|
"top_bar_db": q(top_bar_db),
|
||||||
},
|
},
|
||||||
"checks": {
|
"checks": {
|
||||||
"flexure": {
|
"flexure": {
|
||||||
|
|
@ -131,6 +177,17 @@ def write_results(result: dict, path: Path) -> None:
|
||||||
path.write_text(json.dumps(result, indent=2) + "\n", encoding="utf-8")
|
path.write_text(json.dumps(result, indent=2) + "\n", encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def _load_section_module():
|
||||||
|
import importlib.util
|
||||||
|
|
||||||
|
spec = importlib.util.spec_from_file_location("beam_section", HERE / "beam_section.py")
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise SystemExit("Could not load beam_section.py")
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
input_path = Path(sys.argv[1]) if len(sys.argv) > 1 else HERE / "input.yaml"
|
input_path = Path(sys.argv[1]) if len(sys.argv) > 1 else HERE / "input.yaml"
|
||||||
output_path = Path(sys.argv[2]) if len(sys.argv) > 2 else input_path.with_name("results.json")
|
output_path = Path(sys.argv[2]) if len(sys.argv) > 2 else input_path.with_name("results.json")
|
||||||
|
|
@ -139,6 +196,9 @@ def main() -> int:
|
||||||
result = compute(inp)
|
result = compute(inp)
|
||||||
write_results(result, output_path)
|
write_results(result, output_path)
|
||||||
print(output_path)
|
print(output_path)
|
||||||
|
section_path = HERE / "assets" / "beam-section.typ"
|
||||||
|
_load_section_module().write_fragment(result["values"], section_path)
|
||||||
|
print(section_path)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,4 +1,5 @@
|
||||||
#import "assets/sheet.typ": calcline, calcsheet, check
|
#import "assets/sheet.typ": calcline, calcsheet, check
|
||||||
|
#import "assets/beam-section.typ": beam-section
|
||||||
|
|
||||||
#let data = json("results.json")
|
#let data = json("results.json")
|
||||||
#let n = data.values
|
#let n = data.values
|
||||||
|
|
@ -37,6 +38,13 @@ Simple-span rectangular beam under uniform gravity load. Numbers come from `calc
|
||||||
caption: [#n.span ft simply supported beam, #n.bw in × #n.h in rectangular section.],
|
caption: [#n.span ft simply supported beam, #n.bw in × #n.h in rectangular section.],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
== Section and Reinforcement
|
||||||
|
|
||||||
|
#align(right, block(width: 2.5in, figure(
|
||||||
|
beam-section(),
|
||||||
|
caption: [Beam cross-section],
|
||||||
|
)))
|
||||||
|
|
||||||
== Loads and Beam Demand
|
== Loads and Beam Demand
|
||||||
|
|
||||||
#calcline([$L = #n.span " ft"$], [Simple span])
|
#calcline([$L = #n.span " ft"$], [Simple span])
|
||||||
|
|
@ -64,7 +72,7 @@ Simple-span rectangular beam under uniform gravity load. Numbers come from `calc
|
||||||
#calcline([$d = #n.d " in"$], [Effective depth])
|
#calcline([$d = #n.d " in"$], [Effective depth])
|
||||||
#calcline([$f'_c = #n.fc " ksi"$], [Concrete compressive strength])
|
#calcline([$f'_c = #n.fc " ksi"$], [Concrete compressive strength])
|
||||||
#calcline([$f_y = #n.fy " ksi"$], [Steel yield strength])
|
#calcline([$f_y = #n.fy " ksi"$], [Steel yield strength])
|
||||||
#calcline([$A_s = #n.As " in"^2$], [Provided tension steel (2 No. 5)])
|
#calcline([$A_s = #n.As " in"^2$], [Provided tension steel (#n.n_bars No. #n.bar_size)])
|
||||||
#calcline(
|
#calcline(
|
||||||
[$a = A_s f_y / (0.85 f'_c b_w) = #round(n.a, digits: 3) " in"$],
|
[$a = A_s f_y / (0.85 f'_c b_w) = #round(n.a, digits: 3) " in"$],
|
||||||
[Equivalent compression-block depth],
|
[Equivalent compression-block depth],
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,42 @@
|
||||||
|
# Project name shown in the Typst report header.
|
||||||
project: "Deer Creek Shoring"
|
project: "Deer Creek Shoring"
|
||||||
|
# Person or organization shown in the Typst report footer.
|
||||||
prepared_by: "Conemco Engineering"
|
prepared_by: "Conemco Engineering"
|
||||||
|
|
||||||
|
# Simple span used for the moment and shear demands.
|
||||||
span: "16 ft"
|
span: "16 ft"
|
||||||
|
# Tributary width carried by the beam.
|
||||||
tributary: "6.25 ft"
|
tributary: "6.25 ft"
|
||||||
|
# Dead load including superimposed dead (psf).
|
||||||
D: "55 psf"
|
D: "55 psf"
|
||||||
|
# Live load (psf).
|
||||||
L: "20 psf"
|
L: "20 psf"
|
||||||
|
# Beam web width b_w.
|
||||||
bw: "8 in"
|
bw: "8 in"
|
||||||
|
# Overall beam depth h.
|
||||||
h: "12 in"
|
h: "12 in"
|
||||||
|
# Effective depth to the tension-steel centroid, used for flexure.
|
||||||
d: "9.5 in"
|
d: "9.5 in"
|
||||||
|
# Concrete compressive strength f'_c.
|
||||||
fc: "3.0 ksi"
|
fc: "3.0 ksi"
|
||||||
|
# Steel yield strength f_y.
|
||||||
fy: "60 ksi"
|
fy: "60 ksi"
|
||||||
As: "0.62 in**2"
|
# Concrete unit weight used for the beam self-weight.
|
||||||
concrete_density: "150 pcf"
|
concrete_density: "150 pcf"
|
||||||
|
# Factored load combination applied to the demands.
|
||||||
load_combination: "1.2D + 1.6L"
|
load_combination: "1.2D + 1.6L"
|
||||||
|
|
||||||
|
# Clear cover from the concrete face to the stirrup.
|
||||||
|
cover: "1.5 in"
|
||||||
|
# Stirrup bar number (#3).
|
||||||
|
stirrup_size: 3
|
||||||
|
# Stirrup spacing along the span, on center.
|
||||||
|
stirrup_spacing: "6 in"
|
||||||
|
# Bottom (tension) longitudinal bar number (#5).
|
||||||
|
bar_size: 5
|
||||||
|
# Number of bottom longitudinal bars.
|
||||||
|
n_bars: 2
|
||||||
|
# Top (hanger) longitudinal bar number (#4).
|
||||||
|
top_bar_size: 4
|
||||||
|
# Number of top hanger bars.
|
||||||
|
n_top_bars: 2
|
||||||
|
|
|
||||||
|
|
@ -19,25 +19,35 @@
|
||||||
"d": 9.5,
|
"d": 9.5,
|
||||||
"fc": 3.0,
|
"fc": 3.0,
|
||||||
"fy": 60.0,
|
"fy": 60.0,
|
||||||
"As": 0.62,
|
"As": 0.613592,
|
||||||
"a": 1.823529,
|
"a": 1.804683,
|
||||||
"et": 0.010285,
|
"et": 0.010423,
|
||||||
"phi": 0.9,
|
"phi": 0.9,
|
||||||
"Mn": 26.623529,
|
"Mn": 26.377285,
|
||||||
"phiMn": 23.961176,
|
"phiMn": 23.739557,
|
||||||
"As_min": 0.253333,
|
"As_min": 0.253333,
|
||||||
"Vc": 8.325383,
|
"Vc": 8.325383,
|
||||||
"phiVc": 6.244037
|
"phiVc": 6.244037,
|
||||||
|
"cover": 1.5,
|
||||||
|
"stirrup_size": 3,
|
||||||
|
"stirrup_spacing": 6.0,
|
||||||
|
"stirrup_db": 0.375,
|
||||||
|
"bar_size": 5,
|
||||||
|
"n_bars": 2,
|
||||||
|
"bar_db": 0.625,
|
||||||
|
"top_bar_size": 4,
|
||||||
|
"n_top_bars": 2,
|
||||||
|
"top_bar_db": 0.5
|
||||||
},
|
},
|
||||||
"checks": {
|
"checks": {
|
||||||
"flexure": {
|
"flexure": {
|
||||||
"demand": 23.44,
|
"demand": 23.44,
|
||||||
"capacity": 23.961176,
|
"capacity": 23.739557,
|
||||||
"ok": true
|
"ok": true
|
||||||
},
|
},
|
||||||
"minimum_steel": {
|
"minimum_steel": {
|
||||||
"demand": 0.253333,
|
"demand": 0.253333,
|
||||||
"capacity": 0.62,
|
"capacity": 0.613592,
|
||||||
"ok": true
|
"ok": true
|
||||||
},
|
},
|
||||||
"shear": {
|
"shear": {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import importlib.util
|
import importlib.util
|
||||||
|
import math
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
@ -13,6 +14,14 @@ spec.loader.exec_module(calc_module)
|
||||||
compute = calc_module.compute
|
compute = calc_module.compute
|
||||||
|
|
||||||
|
|
||||||
|
def _load(name: str, filename: str):
|
||||||
|
spec = importlib.util.spec_from_file_location(name, HERE / filename)
|
||||||
|
assert spec is not None and spec.loader is not None
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def result():
|
def result():
|
||||||
import yaml
|
import yaml
|
||||||
|
|
@ -31,10 +40,10 @@ def test_example_demands(result):
|
||||||
|
|
||||||
def test_example_flexure(result):
|
def test_example_flexure(result):
|
||||||
v = result["values"]
|
v = result["values"]
|
||||||
assert v["a"] == pytest.approx(1.823529, rel=1e-5)
|
assert v["a"] == pytest.approx(1.804683, rel=1e-5)
|
||||||
assert v["et"] == pytest.approx(0.010283, rel=1e-3)
|
assert v["et"] == pytest.approx(0.010423, rel=1e-3)
|
||||||
assert v["phi"] == pytest.approx(0.9)
|
assert v["phi"] == pytest.approx(0.9)
|
||||||
assert v["phiMn"] == pytest.approx(23.961176, rel=1e-5)
|
assert v["phiMn"] == pytest.approx(23.739557, rel=1e-5)
|
||||||
assert result["checks"]["flexure"]["ok"] is True
|
assert result["checks"]["flexure"]["ok"] is True
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -44,3 +53,35 @@ def test_example_min_steel_and_shear(result):
|
||||||
assert v["phiVc"] == pytest.approx(6.244016, rel=1e-4)
|
assert v["phiVc"] == pytest.approx(6.244016, rel=1e-4)
|
||||||
assert result["checks"]["minimum_steel"]["ok"] is True
|
assert result["checks"]["minimum_steel"]["ok"] is True
|
||||||
assert result["checks"]["shear"]["ok"] is True
|
assert result["checks"]["shear"]["ok"] is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_reinforcement_geometry(result):
|
||||||
|
v = result["values"]
|
||||||
|
assert v["cover"] == pytest.approx(1.5)
|
||||||
|
assert v["stirrup_size"] == 3
|
||||||
|
assert v["stirrup_db"] == pytest.approx(0.375)
|
||||||
|
assert v["stirrup_spacing"] == pytest.approx(6.0)
|
||||||
|
assert v["bar_size"] == 5
|
||||||
|
assert v["n_bars"] == 2
|
||||||
|
assert v["bar_db"] == pytest.approx(0.625)
|
||||||
|
assert v["top_bar_size"] == 4
|
||||||
|
assert v["n_top_bars"] == 2
|
||||||
|
assert v["top_bar_db"] == pytest.approx(0.5)
|
||||||
|
assert v["As"] == pytest.approx(2 * math.pi * 0.625**2 / 4.0, rel=1e-6)
|
||||||
|
|
||||||
|
|
||||||
|
def test_rejects_depth_without_room_for_bars():
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
with (HERE / "input.yaml").open(encoding="utf-8") as handle:
|
||||||
|
inp = yaml.safe_load(handle)
|
||||||
|
inp["d"] = "11.9 in"
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
compute(inp)
|
||||||
|
|
||||||
|
|
||||||
|
def test_section_fragment_is_current(result):
|
||||||
|
module = _load("beam_section", "beam_section.py")
|
||||||
|
expected = module.render(result["values"])
|
||||||
|
actual = (HERE / "assets" / "beam-section.typ").read_text(encoding="utf-8")
|
||||||
|
assert actual == expected
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue