ysakhale commited on
Commit
bddffd0
·
verified ·
1 Parent(s): c6a8d7d

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +31 -5
  2. app.py +191 -0
  3. requirements.txt +4 -0
README.md CHANGED
@@ -1,12 +1,38 @@
1
  ---
2
- title: Beam Explainer
3
- emoji: 📚
4
- colorFrom: green
5
  colorTo: blue
6
  sdk: gradio
7
- sdk_version: 5.47.2
8
  app_file: app.py
9
  pinned: false
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Beam Analysis Calculator
3
+ colorFrom: gray
 
4
  colorTo: blue
5
  sdk: gradio
6
+ sdk_version: "4.31.5"
7
  app_file: app.py
8
  pinned: false
9
  ---
10
 
11
+ # Beam Analysis Calculator with Natural Language Explanations
12
+
13
+ This Space implements a deterministic engineering calculator for a simply-supported beam with a central point load.
14
+ It shows (i) numerical results and (ii) a natural-language explanation grounded in the computed values.
15
+
16
+ ## Scope and assumptions
17
+ - Simply-supported beam, central point load
18
+ - Square cross-section, linear elastic material
19
+ - Strength check against yield; serviceability check against L/360 deflection
20
+
21
+ ## Inputs and ranges
22
+ - Beam length L: 0.5–10.0 m
23
+ - Square side a: 0.01–0.5 m
24
+ - Load P: 0.1–100 kN
25
+ - Elastic modulus E: 50–300 GPa
26
+ - Yield strength Sy: 100–1000 MPa
27
+
28
+ ## Outputs
29
+ - Max stress, max deflection, allowable deflection
30
+ - Factors of safety (yield, deflection)
31
+ - Pass/fail checks and overall verdict
32
+
33
+ ## Explanation pipeline
34
+ A compact language model (SmolLM2-135M-Instruct) consumes the structured record from the calculator and produces a concise explanation.
35
+ The prompt enforces grounding and avoids introducing numbers beyond what was computed.
36
+
37
+ ## Notes
38
+ This tool is for educational purposes only. For safety-critical design, consult a licensed professional engineer.
app.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import math
3
+ import gradio
4
+ import pandas as pd
5
+ from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
6
+
7
+ # Model for explanations (small, CPU-friendly)
8
+ MODEL_ID = "HuggingFaceTB/SmolLM2-135M-Instruct"
9
+
10
+ # Load model and tokenizer once
11
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
12
+ model = AutoModelForCausalLM.from_pretrained(MODEL_ID)
13
+ pipe = pipeline(task="text-generation", model=model, tokenizer=tokenizer)
14
+
15
+ def beam_calculation_engine(L_m: float, a_m: float, P_N: float, E_GPa: float, Sy_MPa: float) -> dict:
16
+ if any(val <= 0 for val in [L_m, a_m, P_N, E_GPa, Sy_MPa]):
17
+ raise ValueError("All input parameters must be positive values")
18
+
19
+ E_Pa = E_GPa * 1e9
20
+ Sy_Pa = Sy_MPa * 1e6
21
+
22
+ I = a_m**4 / 12.0
23
+ c = a_m / 2.0
24
+
25
+ M_max = P_N * L_m / 4.0
26
+
27
+ sigma_max_Pa = M_max * c / I
28
+ sigma_max_MPa = sigma_max_Pa / 1e6
29
+
30
+ delta_max_m = (P_N * L_m**3) / (48.0 * E_Pa * I)
31
+ delta_max_mm = delta_max_m * 1000
32
+
33
+ delta_allowable_m = L_m / 360.0
34
+ delta_allowable_mm = delta_allowable_m * 1000
35
+
36
+ stress_ratio = sigma_max_Pa / Sy_Pa
37
+ fos_yield = 1.0 / stress_ratio if stress_ratio > 0 else float("inf")
38
+
39
+ deflection_ratio = delta_max_m / delta_allowable_m
40
+ fos_deflection = 1.0 / deflection_ratio if deflection_ratio > 0 else float("inf")
41
+
42
+ passes_strength = sigma_max_Pa <= Sy_Pa
43
+ passes_serviceability = delta_max_m <= delta_allowable_m
44
+ overall_safe = passes_strength and passes_serviceability
45
+
46
+ calculation_summary = {
47
+ "beam_properties": {
48
+ "length_m": L_m,
49
+ "cross_section_side_m": a_m,
50
+ "load_N": P_N,
51
+ "elastic_modulus_GPa": E_GPa,
52
+ "yield_strength_MPa": Sy_MPa
53
+ },
54
+ "calculated_values": {
55
+ "maximum_stress_MPa": round(sigma_max_MPa, 3),
56
+ "maximum_deflection_mm": round(delta_max_mm, 3),
57
+ "allowable_deflection_mm": round(delta_allowable_mm, 3),
58
+ "factor_of_safety_yield": round(fos_yield, 3),
59
+ "factor_of_safety_deflection": round(fos_deflection, 3)
60
+ },
61
+ "engineering_verdict": {
62
+ "strength_check": "PASS" if passes_strength else "FAIL",
63
+ "serviceability_check": "PASS" if passes_serviceability else "FAIL",
64
+ "overall_safety": "SAFE" if overall_safe else "UNSAFE",
65
+ "strength_message": f"Stress ({sigma_max_MPa:.1f} MPa) {'≤' if passes_strength else '>'} Yield ({Sy_MPa} MPa)",
66
+ "deflection_message": f"Deflection ({delta_max_mm:.1f} mm) {'≤' if passes_serviceability else '>'} Allowable ({delta_allowable_mm:.1f} mm)"
67
+ },
68
+ "human_readable_summary": (
69
+ f"Beam Analysis Results:\n"
70
+ f"- Maximum bending stress: {sigma_max_MPa:.1f} MPa\n"
71
+ f"- Maximum deflection: {delta_max_mm:.1f} mm\n"
72
+ f"- Allowable deflection: {delta_allowable_mm:.1f} mm\n"
73
+ f"- Factor of safety (yield): {fos_yield:.2f}\n"
74
+ f"- Factor of safety (deflection): {fos_deflection:.2f}\n"
75
+ f"- Strength check: {'PASS' if passes_strength else 'FAIL'}\n"
76
+ f"- Serviceability check: {'PASS' if passes_serviceability else 'FAIL'}\n"
77
+ f"- Overall verdict: {'SAFE' if overall_safe else 'UNSAFE'}"
78
+ )
79
+ }
80
+ return calculation_summary
81
+
82
+ def format_llm_prompt(system_prompt: str, user_prompt: str) -> str:
83
+ messages = [
84
+ {"role": "system", "content": system_prompt},
85
+ {"role": "user", "content": user_prompt},
86
+ ]
87
+ if hasattr(tokenizer, "chat_template") and tokenizer.chat_template:
88
+ return tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
89
+ return f"System: {system_prompt}\n\nUser: {user_prompt}\n\nAssistant:"
90
+
91
+ def generate_explanation(structured_results: dict) -> str:
92
+ props = structured_results["beam_properties"]
93
+ calc = structured_results["calculated_values"]
94
+ verdict = structured_results["engineering_verdict"]
95
+
96
+ system_prompt = (
97
+ "You are an engineering educator explaining structural analysis results. "
98
+ "Be accurate, concise, and ground the explanation only in provided numbers. "
99
+ "No new assumptions or invented figures."
100
+ )
101
+ user_prompt = (
102
+ f"Explain these beam results in simple terms:\n"
103
+ f"Beam length: {props['length_m']} m, square side: {props['cross_section_side_m']*1000:.1f} mm, "
104
+ f"load: {props['load_N']/1000:.1f} kN. Material: E={props['elastic_modulus_GPa']} GPa, Sy={props['yield_strength_MPa']} MPa.\n"
105
+ f"Stress={calc['maximum_stress_MPa']} MPa, deflection={calc['maximum_deflection_mm']} mm, "
106
+ f"allowable deflection={calc['allowable_deflection_mm']} mm. "
107
+ f"FoS(yield)={calc['factor_of_safety_yield']}, FoS(deflection)={calc['factor_of_safety_deflection']}.\n"
108
+ f"Verdict: Strength={verdict['strength_check']}, Serviceability={verdict['serviceability_check']}, Overall={verdict['overall_safety']}."
109
+ )
110
+ prompt = format_llm_prompt(system_prompt, user_prompt)
111
+ try:
112
+ # Deterministic generation: greedy decoding (no sampling)
113
+ out = pipe(prompt, max_new_tokens=150, do_sample=False, return_full_text=False)
114
+ explanation = out[0]["generated_text"].strip()
115
+ except Exception as e:
116
+ explanation = f"Explanation unavailable: {e}. Raw results:\n{structured_results['human_readable_summary']}"
117
+ return explanation
118
+
119
+ def run_beam_analysis(L_m, a_m, P_kN, E_GPa, Sy_MPa):
120
+ try:
121
+ L_m = float(L_m)
122
+ a_m = float(a_m)
123
+ P_kN = float(P_kN)
124
+ E_GPa = float(E_GPa)
125
+ Sy_MPa = float(Sy_MPa)
126
+
127
+ if not (0.5 <= L_m <= 10.0):
128
+ return None, None, "Error: Beam length must be between 0.5 and 10.0 meters"
129
+ if not (0.01 <= a_m <= 0.5):
130
+ return None, None, "Error: Cross-section side must be between 0.01 and 0.5 meters"
131
+ if not (0.1 <= P_kN <= 100.0):
132
+ return None, None, "Error: Load must be between 0.1 and 100.0 kN"
133
+ if not (50.0 <= E_GPa <= 300.0):
134
+ return None, None, "Error: Elastic modulus must be between 50 and 300 GPa"
135
+ if not (100.0 <= Sy_MPa <= 1000.0):
136
+ return None, None, "Error: Yield strength must be between 100 and 1000 MPa"
137
+
138
+ P_N = P_kN * 1000.0
139
+ results = beam_calculation_engine(L_m, a_m, P_N, E_GPa, Sy_MPa)
140
+
141
+ results_df = pd.DataFrame([
142
+ {"Parameter": "Maximum Stress", "Value": f"{results['calculated_values']['maximum_stress_MPa']} MPa", "Limit": f"{Sy_MPa} MPa", "Status": results['engineering_verdict']['strength_check']},
143
+ {"Parameter": "Factor of Safety (Yield)", "Value": f"{results['calculated_values']['factor_of_safety_yield']}", "Limit": "> 1.0", "Status": "PASS" if results['calculated_values']['factor_of_safety_yield'] > 1.0 else "FAIL"},
144
+ {"Parameter": "Maximum Deflection", "Value": f"{results['calculated_values']['maximum_deflection_mm']} mm", "Limit": f"{results['calculated_values']['allowable_deflection_mm']} mm", "Status": results['engineering_verdict']['serviceability_check']},
145
+ {"Parameter": "Factor of Safety (Deflection)", "Value": f"{results['calculated_values']['factor_of_safety_deflection']}", "Limit": "> 1.0", "Status": "PASS" if results['calculated_values']['factor_of_safety_deflection'] > 1.0 else "FAIL"},
146
+ ])
147
+
148
+ explanation = generate_explanation(results)
149
+ return results_df, results['human_readable_summary'], explanation
150
+ except Exception as e:
151
+ return None, None, f"Calculation error: {e}"
152
+
153
+ with gradio.Blocks(title="Beam Analysis Calculator") as demo:
154
+ gradio.Markdown("# Beam Analysis Calculator with Natural Language Explanations")
155
+ gradio.Markdown("Deterministic simply-supported beam analysis with a central point load. See the numbers and the plain-language explanation.")
156
+
157
+ with gradio.Row():
158
+ with gradio.Column(scale=1):
159
+ gradio.Markdown("## Inputs")
160
+ with gradio.Row():
161
+ L_m = gradio.Number(value=2.0, label="Beam Length (m)", minimum=0.5, maximum=10.0, info="Support-to-support length")
162
+ a_m = gradio.Number(value=0.05, label="Square Side (m)", minimum=0.01, maximum=0.5, info="Side length of square cross-section")
163
+ with gradio.Row():
164
+ P_kN = gradio.Number(value=5.0, label="Central Point Load (kN)", minimum=0.1, maximum=100.0, info="Load applied at midspan")
165
+ with gradio.Row():
166
+ E_GPa = gradio.Number(value=200.0, label="Elastic Modulus (GPa)", minimum=50.0, maximum=300.0, info="Young's modulus of material")
167
+ Sy_MPa = gradio.Number(value=250.0, label="Yield Strength (MPa)", minimum=100.0, maximum=1000.0, info="Material yield strength")
168
+
169
+ calculate_btn = gradio.Button("Run Analysis")
170
+
171
+ gradio.Markdown("### Example Cases")
172
+ gradio.Examples(
173
+ examples=[
174
+ [2.0, 0.05, 5.0, 200.0, 250.0],
175
+ [3.0, 0.08, 10.0, 70.0, 200.0],
176
+ [1.5, 0.03, 2.0, 210.0, 350.0],
177
+ ],
178
+ inputs=[L_m, a_m, P_kN, E_GPa, Sy_MPa],
179
+ label="Click to load parameters"
180
+ )
181
+
182
+ with gradio.Column(scale=1):
183
+ gradio.Markdown("## Results")
184
+ results_table = gradio.Dataframe(headers=["Parameter", "Value", "Limit", "Status"], interactive=False, label="Calculation Results")
185
+ detailed_output = gradio.Textbox(label="Detailed Calculation Summary", interactive=False, lines=6)
186
+ explanation_output = gradio.Textbox(label="Natural Language Explanation", interactive=False, lines=6)
187
+
188
+ calculate_btn.click(fn=run_beam_analysis, inputs=[L_m, a_m, P_kN, E_GPa, Sy_MPa], outputs=[results_table, detailed_output, explanation_output])
189
+
190
+ if __name__ == "__main__":
191
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio
2
+ transformers
3
+ torch
4
+ pandas