File size: 8,121 Bytes
6ed39bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbb9ee4
6ed39bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a28aa68
6ed39bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dbb9ee4
 
0ad8067
dbb9ee4
 
 
 
 
 
6ed39bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304cac9
6ed39bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a28aa68
a698771
 
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

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鈥揓ain 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鈥揓ain 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鈥揥eisbach 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鈥揥eisbach 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鈥揓ain 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="text2text-generation"): # Changed task to text2text-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.\n"
        "Goals:\n"
        "- This tool calculates pressure loss in a straight pipe using the Darcy-Weisbach equation. It assumes steady, incompressible, Newtonian fluid flow in a horizontal pipe and calculates friction based on flow regime (laminar or turbulent).\n\n" # Fixed escaping
        "- Highlight the formulas and what they mean physically.\n"
        "- Interpret the Reynolds number and friction factor.\n"
        "- Explain head loss and pressure drop magnitudes and practical implications.\n"
        "- If inputs look out of typical ranges, gently flag them.\n"
        "Stay under 180 words. Avoid equations in LaTeX; use plain words.\n\n"
        f"JSON:\n{{context}}\n\n"
        "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 = "\n".join(out_lines)

    # Explanation via LLM
    explanation = explain_results(msg)
    return numeric_panel, explanation

with gr.Blocks() as demo:
    gr.Markdown("# Darcy鈥揥eisbach 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 Hugio Face Space
demo.launch(share=False, server_name="0.0.0.0") # Uncomment and add arguments for Space