File size: 11,402 Bytes
0be7e4d
 
 
 
 
 
 
b2c3381
 
 
 
 
 
 
0be7e4d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b2c3381
 
 
 
0be7e4d
 
 
 
 
 
 
 
b2c3381
0be7e4d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b2c3381
 
 
 
 
 
 
 
 
 
 
 
0be7e4d
 
 
 
b2c3381
 
 
 
 
0be7e4d
 
 
 
e0ca7be
0be7e4d
e0ca7be
 
0be7e4d
 
 
 
 
 
 
 
 
 
e0ca7be
 
0be7e4d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b2c3381
0be7e4d
b2c3381
 
 
 
 
 
 
0be7e4d
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
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()