Spaces:
Sleeping
Sleeping
| # app.py | |
| # Busbar sizing Gradio app | |
| # Run locally: pip install -r requirements.txt ; python app.py | |
| # To deploy on Hugging Face Spaces: push this file + requirements.txt to your space repo. | |
| import math | |
| import gradio as gr | |
| import pandas as pd | |
| # Material properties and default current densities (A/mm^2) | |
| # These are conservative engineering defaults — adjust per your standards/practice. | |
| MATERIALS = { | |
| "Copper": { | |
| "resistivity": 1.724e-8, # ohm·m at 20°C | |
| "J_natural": 1.6, # A/mm^2 (natural convection) | |
| "J_forced": 2.5, # A/mm^2 (forced air / ventilated) | |
| }, | |
| "Aluminium": { | |
| "resistivity": 2.82e-8, # ohm·m at 20°C | |
| "J_natural": 0.9, | |
| "J_forced": 1.4, | |
| } | |
| } | |
| # Common commercial thicknesses (mm) and widths (mm) to propose (you can extend) | |
| COMMON_THICKNESSES = [3, 5, 6, 8, 10, 12, 15] # mm | |
| COMMON_WIDTHS = [10, 12, 15, 20, 25, 30, 40, 50, 60, 80, 100, 120, 150, 200] # mm | |
| def nearest_busbar_size(required_area_mm2, thicknesss=COMMON_THICKNESSES, widths=COMMON_WIDTHS): | |
| """ | |
| Find the smallest commercial thickness x width combination whose area >= required_area_mm2. | |
| Returns a list of candidate dicts sorted ascending by area. | |
| """ | |
| candidates = [] | |
| for t in thicknesss: | |
| for w in widths: | |
| area = t * w # mm^2 for rectangular busbar | |
| if area >= required_area_mm2: | |
| candidates.append({ | |
| "thickness_mm": t, | |
| "width_mm": w, | |
| "area_mm2": area | |
| }) | |
| # if none found (very large current), return a few top oversized combos | |
| if not candidates: | |
| # create a fallback by scaling up widths | |
| scale = 2 | |
| for t in thicknesss: | |
| w = widths[-1] * scale | |
| candidates.append({"thickness_mm": t, "width_mm": w, "area_mm2": t * w}) | |
| candidates.sort(key=lambda x: x["area_mm2"]) | |
| return candidates[:6] | |
| candidates.sort(key=lambda x: (x["area_mm2"], x["thickness_mm"], x["width_mm"])) | |
| return candidates[:6] # top 6 suggestions | |
| def size_busbar(current_A: float, | |
| material: str = "Copper", | |
| cooling: str = "Natural (no forced ventilation)", | |
| select_thickness: int = None, | |
| prefer_forced: bool = False, | |
| voltage_V: float = 415.0): | |
| """ | |
| Main calculation function for busbar sizing. | |
| """ | |
| # Input validation / fixups | |
| if current_A <= 0: | |
| return "Current must be positive.", None, None | |
| mat = MATERIALS.get(material) | |
| if mat is None: | |
| return f"Unknown material: {material}", None, None | |
| # Choose current density J (A/mm^2) | |
| if "forced" in cooling.lower() or prefer_forced: | |
| J = mat["J_forced"] | |
| cooling_label = "Forced ventilation" | |
| else: | |
| J = mat["J_natural"] | |
| cooling_label = "Natural convection" | |
| required_area_mm2 = current_A / J # mm^2 | |
| # If user selected a thickness, compute required width | |
| suggestions = [] | |
| if select_thickness in COMMON_THICKNESSES: | |
| t = select_thickness | |
| required_width = math.ceil(required_area_mm2 / t) | |
| # round up to nearest standard width | |
| std_width = next((w for w in COMMON_WIDTHS if w >= required_width), required_width) | |
| area_actual = t * std_width | |
| suggestions.append({ | |
| "thickness_mm": t, | |
| "width_mm": std_width, | |
| "area_mm2": area_actual | |
| }) | |
| else: | |
| suggestions = nearest_busbar_size(required_area_mm2) | |
| # pick the best suggestion (smallest area) | |
| best = suggestions[0] | |
| # Resistance per meter (DC approximation) | |
| area_m2 = best["area_mm2"] * 1e-6 # mm2 -> m2 | |
| resistivity = mat["resistivity"] | |
| R_per_m = resistivity / area_m2 # ohm per meter | |
| voltage_drop_per_m_V = R_per_m * current_A | |
| percent_drop_per_m = (voltage_drop_per_m_V / voltage_V) * 100 | |
| # Build outputs | |
| summary = ( | |
| f"Design current: {current_A:.1f} A\n" | |
| f"Material: {material}\n" | |
| f"Cooling: {cooling_label}\n" | |
| f"Design current density used: {J:.2f} A/mm²\n" | |
| f"Required cross-sectional area (ideal): {required_area_mm2:.2f} mm²\n\n" | |
| f"Recommended busbar (best match): {best['thickness_mm']} mm thick × {best['width_mm']} mm wide\n" | |
| f" -> Cross-sectional area: {best['area_mm2']:.1f} mm²\n\n" | |
| f"Estimated DC resistance: {R_per_m:.6f} Ω/m\n" | |
| f"Voltage drop at {current_A:.1f} A: {voltage_drop_per_m_V:.3f} V/m ({percent_drop_per_m:.4f}% of {voltage_V} V per meter)\n\n" | |
| "Note: This is a simplified design aid. Verify with thermal/mechanical checks, short-circuit stability, standards (IEC/NEC/IEC 61439 etc.), and local practice." | |
| ) | |
| # Prepare a DataFrame of the suggestions for display | |
| df = pd.DataFrame(suggestions) | |
| df = df[["thickness_mm", "width_mm", "area_mm2"]] | |
| df.columns = ["Thickness (mm)", "Width (mm)", "Area (mm²)"] | |
| return summary, df, { | |
| "required_area_mm2": round(required_area_mm2, 2), | |
| "chosen_thickness_mm": best["thickness_mm"], | |
| "chosen_width_mm": best["width_mm"], | |
| "chosen_area_mm2": best["area_mm2"], | |
| "resistance_ohm_per_m": R_per_m, | |
| "voltage_drop_V_per_m": voltage_drop_per_m_V | |
| } | |
| # ---- Gradio UI ---- | |
| with gr.Blocks(title="Busbar sizing calculator") as demo: | |
| gr.Markdown("# Busbar sizing — Gradio app\nEnter design current and choices; app suggests rectangular busbar sizes.") | |
| with gr.Row(): | |
| with gr.Column(): | |
| current_in = gr.Number(label="Design current (A)", value=400, precision=1) | |
| material_in = gr.Radio(choices=list(MATERIALS.keys()), value="Copper", label="Material") | |
| cooling_in = gr.Radio(choices=["Natural (no forced ventilation)", "Forced ventilation"], value="Natural (no forced ventilation)", label="Cooling") | |
| prefer_forced = gr.Checkbox(label="Prefer forced-ventilation values (override cooling)", value=False) | |
| thickness_in = gr.Dropdown(choices=["Auto (choose best)"] + [str(t) for t in COMMON_THICKNESSES], value="Auto (choose best)", label="Prefer thickness (mm) - optional") | |
| voltage_in = gr.Number(label="Nominal system voltage (V) — for % drop calc", value=415) | |
| run_btn = gr.Button("Calculate") | |
| with gr.Column(): | |
| output_text = gr.Textbox(label="Summary", interactive=False, lines=10) | |
| output_table = gr.Dataframe(label="Suggested commercial sizes", interactive=False) | |
| output_json = gr.JSON(label="Raw results (for engineering use)") | |
| def on_calculate(current, material, cooling, prefer_forced, thickness_choice, voltage): | |
| sel_thickness = None | |
| if thickness_choice and thickness_choice != "Auto (choose best)": | |
| try: | |
| sel_thickness = int(thickness_choice) | |
| except: | |
| sel_thickness = None | |
| summary, df, raw = size_busbar(current, material, cooling, sel_thickness, prefer_forced, voltage) | |
| return summary, df, raw | |
| run_btn.click( | |
| on_calculate, | |
| inputs=[current_in, material_in, cooling_in, prefer_forced, thickness_in, voltage_in], | |
| outputs=[output_text, output_table, output_json] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860) | |