import math as m import gradio as gr import pandas as pd import numpy as np from math import log10, pi from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline # Assuming darcy_weisbach_calculation and explain_results functions are defined elsewhere or in this string # For this example, I'll include them for completeness. In a real scenario, # you might have these in separate files or ensure they are defined before this string. def darcy_weisbach_calculation( rho, # kg/m^3 mu, # Pa·s D, # m L, # m Q, # m^3/s eps=1.5e-6 # m, absolute roughness (default ~ smooth steel) ): """ Assumptions: - Steady, incompressible, Newtonian fluid - Fully developed internal flow in a straight, horizontal, circular pipe - Single-phase, isothermal - Darcy friction factor: laminar f=64/Re; turbulent via Swamee–Jain explicit fit Valid ranges (typical sanity): - 500 <= rho <= 2000 kg/m^3 - 0.2e-3 <= mu <= 5e-3 Pa·s - 0.005 <= D <= 1.0 m - 0.5 <= L <= 1000 m - 1e-5 <= Q <= 2.0 m^3/s - 0 <= eps <= 5e-3 m """ # Derived quantities A = pi*(D**2)/4.0 v = Q / A Re = rho * v * D / mu # Friction factor if Re < 2300: f = 64.0 / max(Re, 1e-9) # protect division regime = "laminar" else: # Swamee–Jain explicit approximation for turbulent flow # f = 0.25 / [log10( (eps/(3.7D)) + (5.74/Re^0.9) )]^2 term = (eps/(3.7*D)) + (5.74/(Re**0.9)) f = 0.25 / (log10(term)**2) regime = "turbulent" # Darcy–Weisbach head loss: h_f = f*(L/D)*(v^2/(2g)) g = 9.80665 # m/s^2 hf = f * (L/D) * (v**2/(2*g)) # Pressure drop: ΔP = rho*g*h_f dP = rho * g * hf # Structured, human-readable message message = { "title": "Darcy–Weisbach Head Loss Calculator", "scope": [ "Steady, incompressible, fully developed flow in a straight circular pipe", "Single-phase, isothermal, horizontal run" ], "assumptions": [ "Newtonian fluid", "Darcy friction factor: laminar f=64/Re; turbulent via Swamee–Jain explicit formula", "No fittings/minor losses included" ], "inputs": { "density_rho_kg_per_m3": rho, "viscosity_mu_Pa_s": mu, "diameter_D_m": D, "length_L_m": L, "flow_rate_Q_m3_per_s": Q, "roughness_eps_m": eps }, "derived": { "area_A_m2": A, "avg_velocity_v_m_per_s": v, "Reynolds_Re": Re, "friction_factor_f": f, "flow_regime": regime }, "outputs": { "head_loss_hf_m": hf, "pressure_drop_dP_Pa": dP }, "formulas": [ "A = π D^2 / 4", "v = Q / A", "Re = ρ v D / μ", "Laminar: f = 64 / Re", "Turbulent: f = 0.25 / [log10(ε/(3.7D) + 5.74/Re^0.9)]^2", "h_f = f (L/D) (v^2 / (2g))", "ΔP = ρ g h_f" ], "constants": { "g_m_per_s2": g }, "notes": [ "For turbulent flow with significant fittings, include minor losses separately.", "Check cavitation or NPSH if near pumps; this tool reports line loss only." ], "valid_ranges": { "rho_kg_per_m3": [500, 2000], "mu_Pa_s": [0.0002, 0.005], "D_m": [0.005, 1.0], "L_m": [0.5, 1000], "Q_m3_per_s": [1e-5, 2.0], "eps_m": [0.0, 0.005] } } return message # Small instruction-tuned model for concise, low-latency explanations. # Change model if desired; keep it tiny for Spaces CPU. model_id = "hf-internal-testing/tiny-random-LlamaForCausalLM" # placeholder tiny model # For practical explanations, replace with a small instruct model like: # model_id = "HuggingFaceH4/zephyr-7b-alpha" # requires more resources # or a tiny flan-t5: "google/flan-t5-small" def load_llm_pipeline(model_name=model_id, task="text-generation"): try: pipe = pipeline(task, model=model_name, device_map="auto") except Exception: # Fallback small model available in most environments pipe = pipeline(task, model="google/flan-t5-small") return pipe llm = load_llm_pipeline() def explain_results(structured_message): # Convert the structured dict into a readable block and prompt the LLM import json context = json.dumps(structured_message, indent=2) prompt = ( "You are an engineering tutor. Read the structured calculation JSON and produce a clear, concise explanation for a non-expert. " "Goals: " "- Briefly restate the problem and assumptions. " "- Highlight the formulas and what they mean physically. " "- Interpret the Reynolds number and friction factor. " "- Explain head loss and pressure drop magnitudes and practical implications. " "- If inputs look out of typical ranges, gently flag them. " "Stay under 180 words. Avoid equations in LaTeX; use plain words. " f"JSON: {context} " "Explanation:" ) out = llm(prompt, max_new_tokens=220) text = out[0]["generated_text"] if isinstance(out, list) else str(out) # In case the model echoes the prompt, try to extract the tail if "Explanation:" in text: text = text.split("Explanation:", 1)[-1].strip() return text def run_calc(rho, mu, D, L, Q, eps): msg = darcy_weisbach_calculation(rho, mu, D, L, Q, eps) # Numeric panel summary out_lines = [] out = msg["outputs"] der = msg["derived"] out_lines.append(f"Reynolds number: {der['Reynolds_Re']:.0f}") out_lines.append(f"Friction factor: {der['friction_factor_f']:.5f} ({der['flow_regime']})") out_lines.append(f"Head loss h_f: {out['head_loss_hf_m']:.4f} m") out_lines.append(f"Pressure drop ΔP: {out['pressure_drop_dP_Pa']:.1f} Pa") numeric_panel = " ".join(out_lines) # Explanation via LLM explanation = explain_results(msg) return numeric_panel, explanation with gr.Blocks() as demo: gr.Markdown("# Darcy–Weisbach Pipe Loss (Deterministic)") with gr.Row(): with gr.Column(): rho = gr.Slider(500, 2000, value=998.0, step=1.0, label="Density ρ (kg/m³)") mu = gr.Slider(0.0002, 0.005, value=0.0010, step=0.0001, label="Viscosity μ (Pa·s)") D = gr.Slider(0.005, 1.0, value=0.05, step=0.001, label="Diameter D (m)") L = gr.Slider(0.5, 1000.0, value=50.0, step=0.5, label="Length L (m)") Q = gr.Slider(1e-5, 2.0, value=0.005, step=1e-4, label="Flow rate Q (m³/s)") eps= gr.Slider(0.0, 0.005, value=1.5e-6, step=1e-6, label="Roughness ε (m)") run_btn = gr.Button("Compute") with gr.Column(): numeric = gr.Textbox(label="Numerical results", lines=6) # Assuming 'explain' is defined elsewhere, e.g., as a gr.Textbox # If not, you might need to define it here: explain = gr.Textbox(label="Explanation", lines=10) examples = gr.Examples( examples=[ [998.0, 0.0010, 0.05, 50.0, 0.005, 1.5e-6], # Water, smooth steel [870.0, 0.0015, 0.10, 200.0, 0.03, 4.5e-5], # Light oil, commercial steel [1000.0, 0.0008, 0.02, 10.0, 0.0002, 1.0e-6] # Small tube, low flow ], inputs=[rho, mu, D, L, Q, eps] ) run_btn.click(run_calc, inputs=[rho, mu, D, L, Q, eps], outputs=[numeric, explain]) # Comment out the demo.launch() call as it will be handled by the Hugging Face Space # demo.launch(share=True)