Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# app.py
|
| 2 |
+
# Busbar sizing Gradio app
|
| 3 |
+
# Run locally: pip install -r requirements.txt ; python app.py
|
| 4 |
+
# To deploy on Hugging Face Spaces: push this file + requirements.txt to your space repo.
|
| 5 |
+
|
| 6 |
+
import math
|
| 7 |
+
import gradio as gr
|
| 8 |
+
import pandas as pd
|
| 9 |
+
|
| 10 |
+
# Material properties and default current densities (A/mm^2)
|
| 11 |
+
# These are conservative engineering defaults — adjust per your standards/practice.
|
| 12 |
+
MATERIALS = {
|
| 13 |
+
"Copper": {
|
| 14 |
+
"resistivity": 1.724e-8, # ohm·m at 20°C
|
| 15 |
+
"J_natural": 1.6, # A/mm^2 (natural convection)
|
| 16 |
+
"J_forced": 2.5, # A/mm^2 (forced air / ventilated)
|
| 17 |
+
},
|
| 18 |
+
"Aluminium": {
|
| 19 |
+
"resistivity": 2.82e-8, # ohm·m at 20°C
|
| 20 |
+
"J_natural": 0.9,
|
| 21 |
+
"J_forced": 1.4,
|
| 22 |
+
}
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
# Common commercial thicknesses (mm) and widths (mm) to propose (you can extend)
|
| 26 |
+
COMMON_THICKNESSES = [3, 5, 6, 8, 10, 12, 15] # mm
|
| 27 |
+
COMMON_WIDTHS = [10, 12, 15, 20, 25, 30, 40, 50, 60, 80, 100, 120, 150, 200] # mm
|
| 28 |
+
|
| 29 |
+
def nearest_busbar_size(required_area_mm2, thicknesss=COMMON_THICKNESSES, widths=COMMON_WIDTHS):
|
| 30 |
+
"""
|
| 31 |
+
Find the smallest commercial thickness x width combination whose area >= required_area_mm2.
|
| 32 |
+
Returns a list of candidate dicts sorted ascending by area.
|
| 33 |
+
"""
|
| 34 |
+
candidates = []
|
| 35 |
+
for t in thicknesss:
|
| 36 |
+
for w in widths:
|
| 37 |
+
area = t * w # mm^2 for rectangular busbar
|
| 38 |
+
if area >= required_area_mm2:
|
| 39 |
+
candidates.append({
|
| 40 |
+
"thickness_mm": t,
|
| 41 |
+
"width_mm": w,
|
| 42 |
+
"area_mm2": area
|
| 43 |
+
})
|
| 44 |
+
# if none found (very large current), return a few top oversized combos
|
| 45 |
+
if not candidates:
|
| 46 |
+
# create a fallback by scaling up widths
|
| 47 |
+
scale = 2
|
| 48 |
+
for t in thicknesss:
|
| 49 |
+
w = widths[-1] * scale
|
| 50 |
+
candidates.append({"thickness_mm": t, "width_mm": w, "area_mm2": t * w})
|
| 51 |
+
candidates.sort(key=lambda x: x["area_mm2"])
|
| 52 |
+
return candidates[:6]
|
| 53 |
+
candidates.sort(key=lambda x: (x["area_mm2"], x["thickness_mm"], x["width_mm"]))
|
| 54 |
+
return candidates[:6] # top 6 suggestions
|
| 55 |
+
|
| 56 |
+
def size_busbar(current_A: float,
|
| 57 |
+
material: str = "Copper",
|
| 58 |
+
cooling: str = "Natural (no forced ventilation)",
|
| 59 |
+
select_thickness: int = None,
|
| 60 |
+
prefer_forced: bool = False,
|
| 61 |
+
voltage_V: float = 415.0):
|
| 62 |
+
"""
|
| 63 |
+
Main calculation function for busbar sizing.
|
| 64 |
+
"""
|
| 65 |
+
# Input validation / fixups
|
| 66 |
+
if current_A <= 0:
|
| 67 |
+
return "Current must be positive.", None, None
|
| 68 |
+
|
| 69 |
+
mat = MATERIALS.get(material)
|
| 70 |
+
if mat is None:
|
| 71 |
+
return f"Unknown material: {material}", None, None
|
| 72 |
+
|
| 73 |
+
# Choose current density J (A/mm^2)
|
| 74 |
+
if "forced" in cooling.lower() or prefer_forced:
|
| 75 |
+
J = mat["J_forced"]
|
| 76 |
+
cooling_label = "Forced ventilation"
|
| 77 |
+
else:
|
| 78 |
+
J = mat["J_natural"]
|
| 79 |
+
cooling_label = "Natural convection"
|
| 80 |
+
|
| 81 |
+
required_area_mm2 = current_A / J # mm^2
|
| 82 |
+
|
| 83 |
+
# If user selected a thickness, compute required width
|
| 84 |
+
suggestions = []
|
| 85 |
+
if select_thickness in COMMON_THICKNESSES:
|
| 86 |
+
t = select_thickness
|
| 87 |
+
required_width = math.ceil(required_area_mm2 / t)
|
| 88 |
+
# round up to nearest standard width
|
| 89 |
+
std_width = next((w for w in COMMON_WIDTHS if w >= required_width), required_width)
|
| 90 |
+
area_actual = t * std_width
|
| 91 |
+
suggestions.append({
|
| 92 |
+
"thickness_mm": t,
|
| 93 |
+
"width_mm": std_width,
|
| 94 |
+
"area_mm2": area_actual
|
| 95 |
+
})
|
| 96 |
+
else:
|
| 97 |
+
suggestions = nearest_busbar_size(required_area_mm2)
|
| 98 |
+
|
| 99 |
+
# pick the best suggestion (smallest area)
|
| 100 |
+
best = suggestions[0]
|
| 101 |
+
|
| 102 |
+
# Resistance per meter (DC approximation)
|
| 103 |
+
area_m2 = best["area_mm2"] * 1e-6 # mm2 -> m2
|
| 104 |
+
resistivity = mat["resistivity"]
|
| 105 |
+
R_per_m = resistivity / area_m2 # ohm per meter
|
| 106 |
+
voltage_drop_per_m_V = R_per_m * current_A
|
| 107 |
+
percent_drop_per_m = (voltage_drop_per_m_V / voltage_V) * 100
|
| 108 |
+
|
| 109 |
+
# Build outputs
|
| 110 |
+
summary = (
|
| 111 |
+
f"Design current: {current_A:.1f} A\n"
|
| 112 |
+
f"Material: {material}\n"
|
| 113 |
+
f"Cooling: {cooling_label}\n"
|
| 114 |
+
f"Design current density used: {J:.2f} A/mm²\n"
|
| 115 |
+
f"Required cross-sectional area (ideal): {required_area_mm2:.2f} mm²\n\n"
|
| 116 |
+
f"Recommended busbar (best match): {best['thickness_mm']} mm thick × {best['width_mm']} mm wide\n"
|
| 117 |
+
f" -> Cross-sectional area: {best['area_mm2']:.1f} mm²\n\n"
|
| 118 |
+
f"Estimated DC resistance: {R_per_m:.6f} Ω/m\n"
|
| 119 |
+
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"
|
| 120 |
+
"Note: This is a simplified design aid. Verify with thermal/mechanical checks, short-circuit stability, standards (IEC/NEC/IEC 61439 etc.), and local practice."
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
# Prepare a DataFrame of the suggestions for display
|
| 124 |
+
df = pd.DataFrame(suggestions)
|
| 125 |
+
df = df[["thickness_mm", "width_mm", "area_mm2"]]
|
| 126 |
+
df.columns = ["Thickness (mm)", "Width (mm)", "Area (mm²)"]
|
| 127 |
+
|
| 128 |
+
return summary, df, {
|
| 129 |
+
"required_area_mm2": round(required_area_mm2, 2),
|
| 130 |
+
"chosen_thickness_mm": best["thickness_mm"],
|
| 131 |
+
"chosen_width_mm": best["width_mm"],
|
| 132 |
+
"chosen_area_mm2": best["area_mm2"],
|
| 133 |
+
"resistance_ohm_per_m": R_per_m,
|
| 134 |
+
"voltage_drop_V_per_m": voltage_drop_per_m_V
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
# ---- Gradio UI ----
|
| 139 |
+
with gr.Blocks(title="Busbar sizing calculator") as demo:
|
| 140 |
+
gr.Markdown("# Busbar sizing — Gradio app\nEnter design current and choices; app suggests rectangular busbar sizes.")
|
| 141 |
+
with gr.Row():
|
| 142 |
+
with gr.Column():
|
| 143 |
+
current_in = gr.Number(label="Design current (A)", value=400, precision=1)
|
| 144 |
+
material_in = gr.Radio(choices=list(MATERIALS.keys()), value="Copper", label="Material")
|
| 145 |
+
cooling_in = gr.Radio(choices=["Natural (no forced ventilation)", "Forced ventilation"], value="Natural (no forced ventilation)", label="Cooling")
|
| 146 |
+
prefer_forced = gr.Checkbox(label="Prefer forced-ventilation values (override cooling)", value=False)
|
| 147 |
+
thickness_in = gr.Dropdown(choices=["Auto (choose best)"] + [str(t) for t in COMMON_THICKNESSES], value="Auto (choose best)", label="Prefer thickness (mm) - optional")
|
| 148 |
+
voltage_in = gr.Number(label="Nominal system voltage (V) — for % drop calc", value=415)
|
| 149 |
+
|
| 150 |
+
run_btn = gr.Button("Calculate")
|
| 151 |
+
with gr.Column():
|
| 152 |
+
output_text = gr.Textbox(label="Summary", interactive=False, lines=10)
|
| 153 |
+
output_table = gr.Dataframe(label="Suggested commercial sizes", interactive=False)
|
| 154 |
+
output_json = gr.JSON(label="Raw results (for engineering use)")
|
| 155 |
+
|
| 156 |
+
def on_calculate(current, material, cooling, prefer_forced, thickness_choice, voltage):
|
| 157 |
+
sel_thickness = None
|
| 158 |
+
if thickness_choice and thickness_choice != "Auto (choose best)":
|
| 159 |
+
try:
|
| 160 |
+
sel_thickness = int(thickness_choice)
|
| 161 |
+
except:
|
| 162 |
+
sel_thickness = None
|
| 163 |
+
summary, df, raw = size_busbar(current, material, cooling, sel_thickness, prefer_forced, voltage)
|
| 164 |
+
return summary, df, raw
|
| 165 |
+
|
| 166 |
+
run_btn.click(
|
| 167 |
+
on_calculate,
|
| 168 |
+
inputs=[current_in, material_in, cooling_in, prefer_forced, thickness_in, voltage_in],
|
| 169 |
+
outputs=[output_text, output_table, output_json]
|
| 170 |
+
)
|
| 171 |
+
|
| 172 |
+
if __name__ == "__main__":
|
| 173 |
+
demo.launch(server_name="0.0.0.0", server_port=7860)
|