basbars / app.py
windamir123's picture
Update app.py
fe5061b verified
# 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)