manaskhan commited on
Commit
741324e
·
verified ·
1 Parent(s): 2984c2b

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +209 -0
app.py ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ from fpdf import FPDF
4
+ import io
5
+ import openai
6
+
7
+ # ========== PAGE CONFIG ==========
8
+ st.set_page_config(page_title="ASME Calculator", layout="wide")
9
+
10
+ # App Title + Description
11
+ st.markdown("<h1 style='text-align: center;'>🛠️ ASME CALCULATOR</h1>", unsafe_allow_html=True)
12
+ st.markdown(
13
+ "<p style='text-align: center;'>This tool calculates <b>required thicknesses, nozzle reinforcement, "
14
+ "PWHT, and impact test requirements</b><br>using ASME Section VIII Division 1 formulas.</p>",
15
+ unsafe_allow_html=True
16
+ )
17
+
18
+ # ========== API CLIENT ==========
19
+ if "OPENAI_API_KEY" in st.secrets:
20
+ openai.api_key = st.secrets["OPENAI_API_KEY"]
21
+ else:
22
+ openai.api_key = None
23
+
24
+ # ========== PDF GENERATOR ==========
25
+ class PDF(FPDF):
26
+ def __init__(self):
27
+ super().__init__()
28
+ self.add_page()
29
+ self.set_font("Helvetica", "", 12)
30
+
31
+ def header(self):
32
+ self.set_font("Helvetica", "B", 14)
33
+ self.cell(0, 10, "ASME VIII Div.1 Vessel Design Report", 0, 1, "C")
34
+
35
+ def chapter_title(self, title):
36
+ self.set_font("Helvetica", "B", 12)
37
+ self.cell(0, 10, title, 0, 1, "L")
38
+
39
+ def chapter_body(self, body):
40
+ self.set_font("Helvetica", "", 11)
41
+ self.multi_cell(0, 8, body)
42
+
43
+ # ========== CALCULATION FUNCTIONS ==========
44
+ def shell_thickness(P, D, S, E, corrosion):
45
+ R = D / 2 # convert diameter to radius
46
+ return (P * R) / (S * E - 0.6 * P) + corrosion
47
+
48
+ def head_thickness(P, D, S, E, corrosion, head_type):
49
+ R = D / 2
50
+ if head_type == "Ellipsoidal": # UG-32
51
+ return (0.5 * P * R) / (S * E - 0.1 * P) + corrosion
52
+ elif head_type == "Torispherical": # Approx formula
53
+ return (0.885 * P * R) / (S * E - 0.1 * P) + corrosion
54
+ elif head_type == "Hemispherical": # UG-32
55
+ return (P * R) / (2 * S * E - 0.2 * P) + corrosion
56
+ return None
57
+
58
+ def nozzle_reinforcement(P, d, t_shell, t_nozzle, S, E):
59
+ return (P * d) / (2 * S * E) <= (t_shell + t_nozzle)
60
+
61
+ def pwht_required(thickness, material="CS"):
62
+ return material == "CS" and thickness > 38
63
+
64
+ def impact_test_required(thickness, MDMT=-20, material="CS"):
65
+ return material == "CS" and (MDMT < -29 and thickness > 12)
66
+
67
+ # ========== SESSION STATE ==========
68
+ if "run_done" not in st.session_state:
69
+ st.session_state.run_done = False
70
+ if "ai_done" not in st.session_state:
71
+ st.session_state.ai_done = False
72
+
73
+ # ========== SIDEBAR INPUTS ==========
74
+ with st.sidebar.expander("📥 Manual Design Inputs", expanded=True):
75
+ input_mode = st.radio("Input Mode:", ["Manual Entry", "Upload CSV"])
76
+ run_calculation = False
77
+
78
+ if input_mode == "Manual Entry":
79
+ P = st.number_input("Design Pressure (MPa)", value=2.0, format="%.2f")
80
+ D = st.number_input("Internal Diameter (mm)", value=2000.0, format="%.1f")
81
+ S = st.number_input("Allowable Stress (MPa)", value=120.0, format="%.1f")
82
+ corrosion = st.number_input("Corrosion Allowance (mm)", value=1.5, format="%.2f")
83
+
84
+ joint_method = st.radio("Joint Efficiency Selection", ["Preset (UW-12)", "Manual Entry"])
85
+ if joint_method == "Preset (UW-12)":
86
+ E = st.selectbox("Select E (Joint Efficiency)", [1.0, 0.85, 0.7, 0.65, 0.6, 0.45])
87
+ else:
88
+ E = st.number_input("Manual Joint Efficiency (0-1)", value=0.85, min_value=0.1, max_value=1.0)
89
+
90
+ head_type = st.selectbox("Head Type", ["Ellipsoidal", "Torispherical", "Hemispherical"])
91
+ d_nozzle = st.number_input("Nozzle Diameter (mm)", value=200.0, format="%.1f")
92
+ t_shell = st.number_input("Shell Thickness Provided (mm)", value=12.0, format="%.1f")
93
+ t_nozzle = st.number_input("Nozzle Thickness Provided (mm)", value=10.0, format="%.1f")
94
+ thickness = st.number_input("Governing Thickness (mm)", value=40.0, format="%.1f")
95
+ MDMT = st.number_input("MDMT (°C)", value=-20.0, format="%.1f")
96
+
97
+ if st.button("🚀 Run Calculation", use_container_width=True):
98
+ st.session_state.run_done = True
99
+ run_calculation = True
100
+
101
+ if st.session_state.run_done:
102
+ st.success("✅ Calculations completed! See results in the tabs.")
103
+
104
+ else:
105
+ uploaded_file = st.file_uploader("Upload CSV File", type=["csv"])
106
+ if uploaded_file:
107
+ df = pd.read_csv(uploaded_file)
108
+ st.dataframe(df.head())
109
+ if st.button("🚀 Run Calculation", use_container_width=True):
110
+ st.session_state.run_done = True
111
+ run_calculation = True
112
+
113
+ if st.session_state.run_done:
114
+ st.success("✅ Calculations completed! See results in the tabs.")
115
+
116
+ # ========== TABS ==========
117
+ tabs = st.tabs(["Shell", "Head", "Nozzle", "PWHT", "Impact Test", "Summary", "AI Explanation"])
118
+
119
+ if st.session_state.run_done:
120
+ # --- SHELL TAB ---
121
+ with tabs[0]:
122
+ t_shell_calc = shell_thickness(P, D, S, E, corrosion)
123
+ st.metric("Required Shell Thickness (mm) [UG-27]", f"{t_shell_calc:.2f}")
124
+
125
+ # --- HEAD TAB ---
126
+ with tabs[1]:
127
+ t_head_calc = head_thickness(P, D, S, E, corrosion, head_type)
128
+ st.metric(f"Required {head_type} Head Thickness (mm) [UG-32]", f"{t_head_calc:.2f}")
129
+
130
+ # --- NOZZLE TAB ---
131
+ with tabs[2]:
132
+ safe_nozzle = nozzle_reinforcement(P, d_nozzle, t_shell, t_nozzle, S, E)
133
+ st.write("Nozzle Reinforcement Check [UG-37]:", "✅ Safe" if safe_nozzle else "❌ Not Safe")
134
+
135
+ # --- PWHT TAB ---
136
+ with tabs[3]:
137
+ st.write("PWHT Required:", "✅ Yes" if pwht_required(thickness) else "❌ No")
138
+
139
+ # --- IMPACT TEST TAB ---
140
+ with tabs[4]:
141
+ st.write("Impact Test Required:", "✅ Yes" if impact_test_required(thickness, MDMT) else "❌ No")
142
+
143
+ # --- SUMMARY TAB ---
144
+ with tabs[5]:
145
+ summary_data = {
146
+ "Shell Thickness (UG-27)": round(t_shell_calc, 2),
147
+ "Head Thickness (UG-32)": round(t_head_calc, 2),
148
+ "Nozzle Safe (UG-37)": safe_nozzle,
149
+ "PWHT Required": pwht_required(thickness),
150
+ "Impact Test Required": impact_test_required(thickness, MDMT),
151
+ }
152
+ df_summary = pd.DataFrame([summary_data])
153
+ st.dataframe(df_summary)
154
+
155
+ # CSV export
156
+ csv = df_summary.to_csv(index=False).encode("utf-8")
157
+ st.download_button("📥 Download Results (CSV)", csv, "results.csv")
158
+
159
+ # PDF export
160
+ pdf = PDF()
161
+ pdf.chapter_title("Calculation Summary")
162
+ for k, v in summary_data.items():
163
+ pdf.chapter_body(f"{k}: {v}")
164
+ pdf_bytes = pdf.output(dest="S").encode("latin1")
165
+ st.download_button("📄 Download PDF Report", pdf_bytes, "results.pdf", "application/pdf")
166
+
167
+ # --- AI EXPLANATION TAB ---
168
+ with tabs[6]:
169
+ st.markdown("### 🤖 Ask AI for Explanation")
170
+ if not openai.api_key:
171
+ st.warning("⚠️ Add your OpenAI API key in Streamlit secrets to enable AI explanations.")
172
+ else:
173
+ if st.button("✨ Ask AI", use_container_width=True):
174
+ st.session_state.ai_done = True
175
+ with st.spinner("AI is preparing explanation..."):
176
+ prompt = f"Explain these ASME vessel design results in simple terms: {summary_data}"
177
+
178
+ try:
179
+ chat_completion = openai.ChatCompletion.create(
180
+ model="gpt-4o-mini",
181
+ messages=[
182
+ {"role": "system", "content": "You are an ASME design expert who explains results clearly."},
183
+ {"role": "user", "content": prompt}
184
+ ],
185
+ temperature=0.3
186
+ )
187
+ explanation = chat_completion["choices"][0]["message"]["content"]
188
+ st.success("✅ AI Explanation Generated Below")
189
+ st.write(explanation)
190
+
191
+ except Exception as e:
192
+ st.error(f"⚠️ Error generating AI explanation: {e}")
193
+
194
+ if st.session_state.ai_done:
195
+ st.info("✨ AI explanation already generated. Rerun to refresh.")
196
+
197
+ else:
198
+ # Placeholders
199
+ for i, msg in enumerate([
200
+ "Shell results", "Head results", "Nozzle results",
201
+ "PWHT decision", "Impact Test decision",
202
+ "Summary", "AI explanation"
203
+ ]):
204
+ with tabs[i]:
205
+ st.info(f"Run calculation to see {msg}.")
206
+
207
+ # ========== FOOTER ==========
208
+ st.markdown("---")
209
+ st.caption("Disclaimer: This tool is for demo/educational purposes. Final design must be verified by a qualified engineer per ASME VIII Div.1.")