File size: 7,787 Bytes
1420dc4 1e0ba2b a76f1aa 1e0ba2b a76f1aa 1e0ba2b a76f1aa 1e0ba2b a76f1aa bf6888a a76f1aa 1a520a9 a76f1aa 1e0ba2b a76f1aa 1a520a9 a76f1aa 1a520a9 a76f1aa 1a520a9 a76f1aa 1a520a9 a76f1aa 1e0ba2b a76f1aa c8107d1 a76f1aa 1a520a9 a76f1aa 1e0ba2b a76f1aa 1e0ba2b a76f1aa 1e0ba2b a76f1aa 1e0ba2b a76f1aa 1e0ba2b a76f1aa 1e0ba2b a76f1aa 1e0ba2b a76f1aa 6ee0f96 a76f1aa c8107d1 1e0ba2b a76f1aa 1e0ba2b a76f1aa |
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 |
import streamlit as st
import pandas as pd
import math
from fpdf import FPDF
import os
import matplotlib.pyplot as plt
from groq import Groq
# ========== PAGE CONFIG ==========
st.set_page_config(page_title="ASME Calculator", layout="wide")
# App Title + Description
st.markdown("<h1 style='text-align: center;'>π οΈ ASME CALCULATOR</h1>", unsafe_allow_html=True)
st.markdown(
"<p style='text-align: center;'>This tool calculates <b>required thicknesses, nozzle reinforcement, "
"PWHT, and impact test requirements</b><br>using ASME Section VIII Division 1 formulas.</p>",
unsafe_allow_html=True
)
# ========== API CLIENT ==========
groq_api_key = os.getenv("GROQ_API_KEY")
groq_client = Groq(api_key=groq_api_key) if groq_api_key else None
# ========== PDF GENERATOR ==========
class PDF(FPDF):
def __init__(self):
super().__init__()
self.add_page()
self.set_font("Helvetica", "", 12)
def header(self):
self.set_font("Helvetica", "B", 14)
self.cell(0, 10, "ASME VIII Div.1 Vessel Design Report", 0, 1, "C")
def chapter_title(self, title):
self.set_font("Helvetica", "B", 12)
self.cell(0, 10, title, 0, 1, "L")
def chapter_body(self, body):
self.set_font("Helvetica", "", 11)
self.multi_cell(0, 8, body)
# ========== CALCULATION FUNCTIONS ==========
def shell_thickness(P, R, S, E, corrosion):
return (P * R) / (S * E - 0.6 * P) + corrosion
def head_thickness(P, R, S, E, corrosion, head_type):
if head_type == "Ellipsoidal":
return (0.5 * P * R) / (S * E - 0.1 * P) + corrosion
elif head_type == "Torispherical":
return (0.885 * P * R) / (S * E - 0.1 * P) + corrosion
elif head_type == "Hemispherical":
return (P * R) / (2 * S * E - 0.2 * P) + corrosion
return None
def nozzle_reinforcement(P, d, t_shell, t_nozzle, S, E):
return (P * d) / (2 * S * E) <= (t_shell + t_nozzle)
def pwht_required(thickness, material="CS"):
return material == "CS" and thickness > 38
def impact_test_required(thickness, MDMT=-20, material="CS"):
return material == "CS" and (MDMT < -29 and thickness > 12)
# ========== SESSION STATE ==========
if "run_done" not in st.session_state:
st.session_state.run_done = False
if "ai_done" not in st.session_state:
st.session_state.ai_done = False
# ========== SIDEBAR INPUTS ==========
with st.sidebar.expander("π₯ Manual Design Inputs", expanded=True):
input_mode = st.radio("Input Mode:", ["Manual Entry", "Upload CSV"])
run_calculation = False
if input_mode == "Manual Entry":
P = st.number_input("Design Pressure (MPa)", value=2.0, format="%.2f")
R = st.number_input("Internal Radius (mm)", value=1000.0, format="%.1f")
S = st.number_input("Allowable Stress (MPa)", value=120.0, format="%.1f")
corrosion = st.number_input("Corrosion Allowance (mm)", value=1.5, format="%.2f")
joint_method = st.radio("Joint Efficiency Selection", ["Preset (UW-12)", "Manual Entry"])
if joint_method == "Preset (UW-12)":
E = st.selectbox("Select E (Joint Efficiency)", [1.0, 0.85, 0.7, 0.65, 0.6, 0.45])
else:
E = st.number_input("Manual Joint Efficiency (0-1)", value=0.85, min_value=0.1, max_value=1.0)
head_type = st.selectbox("Head Type", ["Ellipsoidal", "Torispherical", "Hemispherical"])
d_nozzle = st.number_input("Nozzle Diameter (mm)", value=200.0, format="%.1f")
t_shell = st.number_input("Shell Thickness Provided (mm)", value=12.0, format="%.1f")
t_nozzle = st.number_input("Nozzle Thickness Provided (mm)", value=10.0, format="%.1f")
thickness = st.number_input("Governing Thickness (mm)", value=40.0, format="%.1f")
if st.button("π Run Calculation", use_container_width=True):
st.session_state.run_done = True
run_calculation = True
if st.session_state.run_done:
st.success("β
Calculations completed! See results in the tabs.")
else:
uploaded_file = st.file_uploader("Upload CSV File", type=["csv"])
if uploaded_file:
df = pd.read_csv(uploaded_file)
st.dataframe(df.head())
if st.button("π Run Calculation", use_container_width=True):
st.session_state.run_done = True
run_calculation = True
if st.session_state.run_done:
st.success("β
Calculations completed! See results in the tabs.")
# ========== TABS ==========
tabs = st.tabs(["Shell", "Head", "Nozzle", "PWHT", "Impact Test", "Summary", "AI Explanation"])
if st.session_state.run_done:
# --- SHELL TAB ---
with tabs[0]:
t_shell_calc = shell_thickness(P, R, S, E, corrosion)
st.metric("Required Shell Thickness (mm)", f"{t_shell_calc:.2f}")
# --- HEAD TAB ---
with tabs[1]:
t_head_calc = head_thickness(P, R, S, E, corrosion, head_type)
st.metric(f"Required {head_type} Head Thickness (mm)", f"{t_head_calc:.2f}")
# --- NOZZLE TAB ---
with tabs[2]:
safe_nozzle = nozzle_reinforcement(P, d_nozzle, t_shell, t_nozzle, S, E)
st.write("Nozzle Reinforcement Check:", "β
Safe" if safe_nozzle else "β Not Safe")
# --- PWHT TAB ---
with tabs[3]:
st.write("PWHT Required:", "β
Yes" if pwht_required(thickness) else "β No")
# --- IMPACT TEST TAB ---
with tabs[4]:
st.write("Impact Test Required:", "β
Yes" if impact_test_required(thickness) else "β No")
# --- SUMMARY TAB ---
with tabs[5]:
summary_data = {
"Shell Thickness": t_shell_calc,
"Head Thickness": t_head_calc,
"Nozzle Safe": safe_nozzle,
"PWHT Required": pwht_required(thickness),
"Impact Test Required": impact_test_required(thickness),
}
df_summary = pd.DataFrame([summary_data])
st.dataframe(df_summary)
# CSV export
csv = df_summary.to_csv(index=False).encode("utf-8")
st.download_button("π₯ Download Results (CSV)", csv, "results.csv")
# PDF export
pdf = PDF()
pdf.chapter_title("Calculation Summary")
pdf.chapter_body(str(summary_data))
pdf_file = "results.pdf"
pdf.output(pdf_file)
with open(pdf_file, "rb") as f:
st.download_button("π Download PDF Report", f, "results.pdf")
# --- AI EXPLANATION TAB ---
with tabs[6]:
st.markdown("### π€ Ask AI for Explanation")
if groq_client:
if st.button("β¨ Ask AI", use_container_width=True):
st.session_state.ai_done = True
with st.spinner("AI is preparing explanation..."):
prompt = f"Explain these ASME vessel design results in simple terms: {summary_data}"
chat_completion = groq_client.chat.completions.create(
messages=[{"role": "user", "content": prompt}],
model="llama-3.1-8b-instant",
)
explanation = chat_completion.choices[0].message.content
st.success("β
AI Explanation Generated Below")
st.write(explanation)
if st.session_state.ai_done:
st.info("β¨ AI explanation already generated. Rerun to refresh.")
else:
st.info("βΉοΈ Add your GROQ_API_KEY in Hugging Face secrets to enable AI explanations.")
else:
# Placeholders
for i, msg in enumerate([
"Shell results", "Head results", "Nozzle results",
"PWHT decision", "Impact Test decision",
"Summary", "AI explanation"
]):
with tabs[i]:
st.info(f"Run calculation to see {msg}.")
|