RCBeamdesign / app.py
Sompote's picture
Upload 7 files
e0ca7be verified
import gradio as gr
import pandas as pd
from continuous_beam import ContinuousBeam
import matplotlib.pyplot as plt
import numpy as np
import io
import base64
import gc # Garbage collection for memory management
import warnings
warnings.filterwarnings('ignore') # Suppress warnings for cleaner output
# Configure matplotlib for cloud deployment
plt.style.use('default') # Use simple style
plt.rcParams['figure.max_open_warning'] = 0 # Disable figure warnings
class GradioBeamApp:
def __init__(self):
self.beam = ContinuousBeam()
self.spans_data = []
def parse_point_loads(self, point_loads_text):
"""Parse point loads from text input"""
if not point_loads_text or point_loads_text.strip() == "":
return []
point_loads = []
try:
# Expected format: "position1,load1; position2,load2; ..."
# Example: "2.0,50; 4.0,30"
load_pairs = point_loads_text.split(';')
for pair in load_pairs:
if pair.strip():
pos_str, load_str = pair.split(',')
position = float(pos_str.strip())
load = float(load_str.strip())
point_loads.append((position, load))
except:
raise ValueError("Invalid point load format. Use: position1,load1; position2,load2")
return point_loads
def add_span(self, length, distributed_load, point_loads_text, spans_display):
"""Add a span to the beam"""
try:
length = float(length)
distributed_load = float(distributed_load)
if length <= 0 or distributed_load < 0:
return spans_display, "Error: Please enter valid values (length > 0, distributed_load >= 0)"
# Parse point loads
point_loads = self.parse_point_loads(point_loads_text)
# Validate point load positions
for pos, load in point_loads:
if pos < 0 or pos > length:
return spans_display, f"Error: Point load at {pos}m is outside span length {length}m"
if load < 0:
return spans_display, f"Error: Point load {load}kN must be positive"
self.beam.add_span(length, distributed_load, point_loads)
# Create span description
span_info = f"Span {len(self.beam.spans)}: L={length}m, w={distributed_load}kN/m"
if point_loads:
point_load_str = ", ".join([f"P={load}kN@{pos}m" for pos, load in point_loads])
span_info += f", {point_load_str}"
self.spans_data.append(span_info)
# Update display
updated_display = "\n".join(self.spans_data)
return updated_display, f"Added: {span_info}"
except ValueError as e:
return spans_display, f"Error: {str(e)}"
def clear_spans(self):
"""Clear all spans"""
self.beam = ContinuousBeam()
self.spans_data = []
return "", "All spans cleared"
def design_beam(self, width, depth, fc, fy, cover, spans_display):
"""Design the beam and return results"""
if not self.beam.spans:
return "No spans added. Please add at least one span.", None, None, None, None, None
try:
# Limit spans for cloud deployment
if len(self.beam.spans) > 10:
return "Error: Maximum 10 spans allowed for cloud deployment.", None, None, None, None, "Error: Too many spans"
# Update beam properties
self.beam.beam_width = float(width)
self.beam.beam_depth = float(depth)
self.beam.fc = float(fc)
self.beam.fy = float(fy)
self.beam.cover = float(cover)
self.beam.d = self.beam.beam_depth - self.beam.cover
# Perform design with timeout protection
design_results = self.beam.design_beam()
# Generate report
report = self.beam.generate_report(design_results)
# Create summary table
summary_data = []
for span_data in design_results:
span_num = span_data['span']
for i, (reinf, stirrup) in enumerate(zip(span_data['reinforcement'], span_data['stirrups'])):
if reinf['moment'] != 0 or stirrup['shear'] != 0:
summary_data.append([
span_num if i == 0 else '',
reinf['location'],
f"{reinf['moment']:.2f}" if reinf['moment'] != 0 else '-',
reinf['bars'] if reinf['moment'] != 0 else '-',
f"{stirrup['shear']:.2f}" if stirrup['shear'] != 0 else '-',
stirrup['stirrup_spacing'] if stirrup['shear'] != 0 else '-'
])
# Create DataFrame for table display
df = pd.DataFrame(summary_data, columns=[
'Span', 'Location', 'Moment (kN-m)', 'Reinforcement', 'Shear (kN)', 'Stirrups'
])
# Generate plots with error handling
try:
bmd_sfd_plot = self.beam.plot_bmd_sfd(design_results)
reinforcement_plot = self.beam.plot_reinforcement_layout(design_results)
stirrup_plot = self.beam.plot_stirrup_layout(design_results)
except Exception as plot_error:
print(f"Plotting error: {plot_error}")
# Return simplified results if plotting fails
return report, df, None, None, None, "Design completed (plots unavailable)"
# Force garbage collection to free memory
gc.collect()
return report, df, bmd_sfd_plot, reinforcement_plot, stirrup_plot, "Design completed successfully!"
except Exception as e:
gc.collect() # Clean up memory on error
error_msg = str(e)
if "memory" in error_msg.lower() or "allocation" in error_msg.lower():
return "Error: Insufficient memory. Try reducing the number of spans or load complexity.", None, None, None, None, "Memory Error"
return f"Design calculation failed: {error_msg}", None, None, None, None, f"Error: {error_msg}"
def create_interface():
app = GradioBeamApp()
with gr.Blocks(title="Continuous Beam RC Design - Thai Standards", theme=gr.themes.Default()) as interface:
gr.Markdown("# Continuous Beam RC Design - Thai Standards")
gr.Markdown("*Using Finite Element Analysis with Thai reinforcement standards (DB bars, RB stirrups)*")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("## Beam Properties")
with gr.Row():
width_input = gr.Number(label="Beam Width (mm)", value=300)
depth_input = gr.Number(label="Beam Depth (mm)", value=500)
with gr.Row():
fc_input = gr.Number(label="f'c (ksc)", value=280)
fy_input = gr.Number(label="fy (ksc)", value=4000)
cover_input = gr.Number(label="Cover (mm)", value=40)
gr.Markdown("## Spans Configuration")
with gr.Row():
span_length_input = gr.Number(label="Span Length (m)", value=6.0)
distributed_load_input = gr.Number(label="Distributed Load (kN/m)", value=25.0)
point_loads_input = gr.Textbox(
label="Point Loads (position,load; position,load; ...)",
placeholder="Example: 2.0,50; 4.0,30 (means 50kN at 2m and 30kN at 4m)",
value=""
)
with gr.Row():
add_span_btn = gr.Button("Add Span", variant="primary")
clear_spans_btn = gr.Button("Clear All", variant="secondary")
spans_display = gr.Textbox(
label="Added Spans",
lines=5,
interactive=False,
placeholder="No spans added yet..."
)
design_btn = gr.Button("Design Beam", variant="primary", size="lg")
status_output = gr.Textbox(label="Status", interactive=False)
with gr.Column(scale=2):
gr.Markdown("## Design Results")
with gr.Tabs():
with gr.TabItem("Summary"):
summary_table = gr.Dataframe(
label="Design Summary",
headers=["Span", "Location", "Moment (kN-m)", "Reinforcement", "Shear (kN)", "Stirrups"],
interactive=False
)
with gr.TabItem("BMD & SFD"):
bmd_sfd_plot = gr.Plot(label="Bending Moment and Shear Force Diagrams")
with gr.TabItem("Reinforcement Layout"):
reinforcement_plot = gr.Plot(label="Reinforcement Bar Layout")
with gr.TabItem("Stirrup Layout"):
stirrup_plot = gr.Plot(label="Shear Stirrup Layout")
with gr.TabItem("Detailed Report"):
results_output = gr.Textbox(
label="Detailed Design Report",
lines=20,
interactive=False,
show_copy_button=True
)
# Event handlers
add_span_btn.click(
fn=lambda length, dist_load, point_loads, spans: app.add_span(length, dist_load, point_loads, spans),
inputs=[span_length_input, distributed_load_input, point_loads_input, spans_display],
outputs=[spans_display, status_output]
)
clear_spans_btn.click(
fn=lambda: app.clear_spans(),
outputs=[spans_display, status_output]
)
design_btn.click(
fn=lambda w, d, fc, fy, c, spans: app.design_beam(w, d, fc, fy, c, spans),
inputs=[width_input, depth_input, fc_input, fy_input, cover_input, spans_display],
outputs=[results_output, summary_table, bmd_sfd_plot, reinforcement_plot, stirrup_plot, status_output]
)
return interface
def main():
interface = create_interface()
# Optimized for Hugging Face Spaces
interface.launch(
share=False,
debug=False, # Disable debug for production
show_error=True,
quiet=True, # Reduce console output
ssr_mode=False, # Disable SSR for compatibility
auth=None, # No authentication required
max_threads=10 # Limit concurrent threads
)
if __name__ == "__main__":
main()