Spaces:
Sleeping
Sleeping
Download app.py from cmuchancel/hw3-fin-array-explainer: direct link, hf CLI and curl.
- Browser
- Download file 7.93 kB
-
https://huggingface.co/spaces/cmuchancel/hw3-fin-array-explainer/resolve/main/app.py
- Command line
-
hf download hf://spaces/cmuchancel/hw3-fin-array-explainer/app.py
-
curl -L -o app.py https://huggingface.co/spaces/cmuchancel/hw3-fin-array-explainer/resolve/main/app.py
7.93 kB
| import math | |
| def fin_array_calc(N: int, T_inf_C: float, T_b_C: float, L_m: float, w_m: float, t_m: float, h_W_m2K: float, k_W_mK: float) -> dict: | |
| """ | |
| Straight rectangular fin array, adiabatic tip. | |
| Returns fin geometry, fin parameter, heat transfer per fin, and total heat transfer. | |
| """ | |
| values = [N, T_inf_C, T_b_C, L_m, w_m, t_m, h_W_m2K, k_W_mK] | |
| if not all(math.isfinite(v) for v in values): | |
| raise ValueError("All inputs must be finite.") | |
| if N <= 0 or min(L_m, w_m, t_m, h_W_m2K, k_W_mK) <= 0: | |
| raise ValueError("Number of fins, dimensions, convection coefficient, and conductivity must be positive.") | |
| if T_b_C <= T_inf_C: | |
| raise ValueError("Base temperature must be greater than ambient temperature.") | |
| if N != int(N): | |
| raise ValueError("Number of fins must be an integer.") | |
| # Calculate fin geometry | |
| A_c = w_m * t_m | |
| P = 2.0 * (w_m + t_m) | |
| # Base excess temperature | |
| theta_b = T_b_C - T_inf_C | |
| # Fin parameter | |
| m = math.sqrt(h_W_m2K * P / (k_W_mK * A_c)) | |
| M = math.sqrt(h_W_m2K * P * k_W_mK * A_c) * theta_b | |
| # Heat transfer for adiabatic tip | |
| q_fin_W = M * math.tanh(m * L_m) | |
| q_total_W = N * q_fin_W | |
| return dict( | |
| results={ | |
| "A_c_m2": A_c, | |
| "P_m": P, | |
| "theta_b_C": theta_b, | |
| "m_1_per_m": m, | |
| "mL": m * L_m, | |
| "q_fin_W": q_fin_W, | |
| "q_total_W": q_total_W, | |
| }, | |
| assumptions={ | |
| "adiabatic_tip": True, | |
| "steady_state": True, | |
| "one_dimensional": True, | |
| "constant_h": True, | |
| "constant_T_inf": True, | |
| "constant_T_b": True, | |
| "constant_k": True, | |
| "uniform_cross_section": True, | |
| "identical_fins": True, | |
| "perfect_base_contact": True, | |
| "negligible_radiation": True, | |
| "no_fin_interaction": True, | |
| }, | |
| ) | |
| import torch | |
| from transformers import AutoTokenizer, AutoModelForCausalLM, GenerationConfig | |
| MODEL_ID = "Qwen/Qwen3-0.6B" | |
| MODEL_REVISION = "c1899de289a04d12100db370d81485cdf75e47ca" | |
| tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, revision=MODEL_REVISION) | |
| model = AutoModelForCausalLM.from_pretrained( | |
| MODEL_ID, revision=MODEL_REVISION, dtype=torch.float32 | |
| ).eval() # CPU: works without a GPU runtime or a separate serving package. | |
| def generate(messages, max_tokens=96): | |
| inputs = tokenizer.apply_chat_template( | |
| messages, tokenize=True, add_generation_prompt=True, | |
| enable_thinking=False, return_dict=True, return_tensors="pt", | |
| ) | |
| config = GenerationConfig( | |
| max_new_tokens=max_tokens, do_sample=False, | |
| eos_token_id=tokenizer.eos_token_id, pad_token_id=tokenizer.eos_token_id, | |
| ) | |
| model.generation_config = config # Keep sampling defaults from overriding this classroom configuration. | |
| with torch.inference_mode(): | |
| output = model.generate(**inputs, generation_config=config) | |
| # Decode only the new tokens, not the prompt. | |
| new_tokens = output[0, inputs["input_ids"].shape[-1]:] | |
| text = tokenizer.decode(new_tokens, skip_special_tokens=True).strip() | |
| if new_tokens[-1].item() != tokenizer.eos_token_id: | |
| text += "\n[Generation reached its length limit; this explanation may be incomplete.]" | |
| return text | |
| import pandas as pd | |
| import gradio as gr | |
| TABLE_COLUMNS = ["Quantity", "Calculated", "Units"] | |
| # Two examples teach the model how to describe the fin-array calculation. | |
| FEW_SHOT_MESSAGES = [ | |
| {"role": "user", "content": "Heat transfer per fin: 8.000 W. Number of fins: 10. Total heat transfer: 80.000 W. Base temperature: 100.000 C. Ambient temperature: 25.000 C."}, | |
| {"role": "assistant", "content": "Each fin transfers 8 W from the base to the surroundings. With 10 identical fins, the total heat transfer is 80 W."}, | |
| {"role": "user", "content": "Heat transfer per fin: 4.000 W. Number of fins: 20. Total heat transfer: 80.000 W. Base temperature: 80.000 C. Ambient temperature: 30.000 C."}, | |
| {"role": "assistant", "content": "Each fin transfers 4 W from the base to the surroundings. With 20 identical fins, the total heat transfer is 80 W."}, | |
| ] | |
| def run_once(num_fins, ambient, base, length, width, thickness, h, conductivity): | |
| try: | |
| vals = [float(x) for x in [num_fins, ambient, base, length, width, thickness, h, conductivity]] | |
| result = fin_array_calc(int(vals[0]), vals[1], vals[2], vals[3], vals[4], vals[5], vals[6], vals[7]) | |
| except (TypeError, ValueError, ArithmeticError) as exc: | |
| # Clear stale evidence and prose together when an input is invalid. | |
| return pd.DataFrame(columns=TABLE_COLUMNS), "Correct the inputs: " + str(exc) | |
| r = result["results"] | |
| table = pd.DataFrame([ | |
| ["Cross-sectional area", f"{r['A_c_m2']:.6g}", "m²"], | |
| ["Fin perimeter", f"{r['P_m']:.6g}", "m"], | |
| ["Base excess temperature", f"{r['theta_b_C']:.3f}", "°C"], | |
| ["Fin parameter, m", f"{r['m_1_per_m']:.3f}", "1/m"], | |
| ["mL", f"{r['mL']:.3f}", "—"], | |
| ["Heat transfer per fin", f"{r['q_fin_W']:.3f}", "W"], | |
| ["Total array heat transfer", f"{r['q_total_W']:.3f}", "W"], | |
| ], columns=TABLE_COLUMNS) | |
| facts = ( | |
| f"Heat transfer per fin: {r['q_fin_W']:.3f} W. Number of fins: {int(vals[0])}. Total heat transfer: {r['q_total_W']:.3f} W. " | |
| f"Base temperature: {vals[2]:.3f} C. Ambient temperature: {vals[1]:.3f} C. Fin parameter m: {r['m_1_per_m']:.3f} 1/m. mL: {r['mL']:.3f}." | |
| ) | |
| explanation = generate([ | |
| {"role": "system", "content": "Write exactly two short sentences, at most 35 words total. Sentence 1: state heat transfer per fin. Sentence 2: state total array heat transfer. Copy the supplied numbers exactly. Do not repeat values, recalculate, invent causes, or recommend design changes."}, | |
| *FEW_SHOT_MESSAGES, | |
| {"role": "user", "content": facts}, | |
| ]) | |
| return table, explanation | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# Fin array calculation and explanation\nIdealized rectangular fins with adiabatic tips. Compare the generated description against the calculated values.") | |
| gr.Markdown( | |
| """ | |
| **Assumptions** | |
| - Steady-state, one-dimensional conduction | |
| - Adiabatic fin tips | |
| - Constant `h`, `k`, `T∞`, and `Tb` | |
| - Uniform rectangular cross-section | |
| - Identical fins with perfect base contact | |
| - Negligible radiation and fin-to-fin interaction | |
| """ | |
| ) | |
| gr.Markdown( | |
| """ | |
| **Valid inputs** | |
| - Number of fins: positive integer | |
| - Fin length, width, and thickness: greater than 0 m | |
| - Convection coefficient `h`: greater than 0 W/m²-K | |
| - Thermal conductivity `k`: greater than 0 W/m-K | |
| - Base temperature `Tb` must be greater than ambient temperature `T∞` | |
| """ | |
| ) | |
| with gr.Row(): | |
| num_fins = gr.Number(value=10, precision=0, label="Number of fins") | |
| ambient = gr.Number(value=25, label="Ambient temperature (°C)") | |
| base = gr.Number(value=100, label="Base temperature (°C)") | |
| h = gr.Number(value=25, label="Convection coefficient (W/m²-K)") | |
| with gr.Row(): | |
| length = gr.Number(value=0.05, label="Fin length (m)") | |
| width = gr.Number(value=0.03, label="Fin width (m)") | |
| thickness = gr.Number(value=0.002, label="Fin thickness (m)") | |
| conductivity = gr.Number(value=205, label="Thermal conductivity (W/m-K)") | |
| table = gr.Dataframe(headers=TABLE_COLUMNS, datatype="str", label="Calculated evidence — source of the results", interactive=False) | |
| prose = gr.Textbox(label="Generated explanation — verify against the table", lines=4, interactive=False) | |
| gr.Button("Calculate and explain").click( | |
| run_once, [num_fins, ambient, base, length, width, thickness, h, conductivity], [table, prose], | |
| concurrency_limit=1, api_name="run_once", | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |