ameen135/Cable_Size_Selection
1
1import streamlit as st2import math3import pandas as pd4import matplotlib.pyplot as plt5from enum import Enum6from datetime import datetime7from io import BytesIO8from fpdf import FPDF9from reportlab.lib.pagesizes import letter10from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle11from reportlab.lib.styles import getSampleStyleSheet12from reportlab.lib import colors13from reportlab.lib.units import inch14 15class ApplicationType(Enum):16 MOTORS = "Motors"17 LIGHTING = "Lighting"18 APPLIANCES = "Appliances"19 GENERAL = "General/Mixed"20 INDUSTRIAL = "Industrial Equipment"21 HVAC = "HVAC Systems"22 WELDING = "Welding Equipment"23 24def get_application_type_from_value(value):25 """Safely gets ApplicationType enum from string value"""26 for app_type in ApplicationType:27 if app_type.value == value:28 return app_type29 return ApplicationType.GENERAL30 31def get_application_parameters(app_type_value):32 """Returns recommended voltage drop limits and power factors"""33 app_type = get_application_type_from_value(app_type_value)34 params = {35 ApplicationType.MOTORS: {"max_vd_percent": 5, "rec_vd_percent": 3, "typical_pf": 0.85},36 ApplicationType.LIGHTING: {"max_vd_percent": 3, "rec_vd_percent": 2, "typical_pf": 0.95},37 ApplicationType.APPLIANCES: {"max_vd_percent": 5, "rec_vd_percent": 3, "typical_pf": 0.90},38 ApplicationType.GENERAL: {"max_vd_percent": 5, "rec_vd_percent": 3, "typical_pf": 0.85},39 ApplicationType.INDUSTRIAL: {"max_vd_percent": 5, "rec_vd_percent": 3, "typical_pf": 0.80},40 ApplicationType.HVAC: {"max_vd_percent": 5, "rec_vd_percent": 3, "typical_pf": 0.85},41 ApplicationType.WELDING: {"max_vd_percent": 7, "rec_vd_percent": 5, "typical_pf": 0.75}42 }43 return params.get(app_type, {"max_vd_percent": 5, "rec_vd_percent": 3, "typical_pf": 0.85})44 45def calculate_voltage_drop(system_code, current, length, r, x, power_factor):46 """Calculate voltage drop based on system type"""47 if system_code == '1': # Single-phase AC48 return 2 * current * length * (r * power_factor + x * math.sqrt(1 - power_factor**2))49 elif system_code == '3': # Three-phase AC50 return math.sqrt(3) * current * length * (r * power_factor + x * math.sqrt(1 - power_factor**2))51 else: # DC52 return 2 * current * length * r53 54def reset_calculations():55 """Reset all session state variables"""56 st.session_state.clear()57 st.session_state.calculate = False58 st.session_state.manual_mode = False59 60def generate_excel_report(data):61 """Generate Excel report from calculation data"""62 output = BytesIO()63 with pd.ExcelWriter(output, engine='xlsxwriter') as writer:64 # System Summary sheet65 sys_df = pd.DataFrame([66 ["Project Name", data['project_name']],67 ["Designer", data['designer_name']],68 ["Calculation Date", data['calculation_date']],69 ["Current Type", data['system_type']],70 ["Phase Configuration", data['phase_type']],71 ["Application", data['application']],72 ["Voltage (V)", data['voltage']],73 ["Current (A)", data['current']],74 ["Power (W)", data.get('power', 'N/A')],75 ["Power Factor", data['power_factor']],76 ["Cable Length (m)", data['length']],77 ["Duty Cycle (%)", data.get('duty_cycle', 'N/A')]78 ], columns=['Parameter', 'Value'])79 80 # Add parallel cables info if exists81 if 'parallel_cables' in data:82 sys_df = pd.concat([83 sys_df,84 pd.DataFrame([85 ["Parallel Cables Used", "Yes"],86 ["Number of Cables", data['parallel_cables']['number']],87 ["Size per Cable (mm²)", data['parallel_cables']['size']],88 ["Current per Cable (A)", data['parallel_cables']['current_per_cable']],89 ["Total Current Capacity (A)", data['parallel_cables']['total_current_rating']]90 ], columns=['Parameter', 'Value'])91 ])92 93 sys_df.to_excel(writer, sheet_name='System Summary', index=False)94 95 # Cable Selection sheet96 cable_df = pd.DataFrame([97 ["Material", data['material']],98 ["Selected Size (mm²)", data['selected_size']],99 ["Current Rating (A)", data['current_rating']],100 ["Required Current (A)", data['required_current']],101 ["Resistance (Ω/km)", data['resistance']],102 ["Reactance (Ω/km)", data['reactance']],103 ["Installation Method", data['install_method']],104 ["Ambient Temperature (°C)", data['ambient_temp']],105 ["Temperature Correction", data['temp_correction']],106 ["Tray Type", data.get('tray_type', 'N/A')],107 ["Fill Ratio (%)", data.get('fill_ratio', 'N/A')],108 ["Derating Factor", data.get('derating_factor', 'N/A')],109 ["User Size Limit (mm²)", data.get('user_size_limit', 'N/A')]110 ], columns=['Parameter', 'Value'])111 cable_df.to_excel(writer, sheet_name='Cable Selection', index=False)112 113 # Voltage Drop sheet114 vd_df = pd.DataFrame([115 ["Voltage Drop (V)", data['voltage_drop']],116 ["Percentage Drop (%)", data['percentage_drop']],117 ["Maximum Allowed (%)", data['max_vd_percent']],118 ["Recommended (%)", data['rec_vd_percent']],119 ["Status", data['status']]120 ], columns=['Parameter', 'Value'])121 vd_df.to_excel(writer, sheet_name='Voltage Drop', index=False)122 123 # Add charts if voltage drop is calculated124 if data['percentage_drop'] != "N/A":125 workbook = writer.book126 worksheet = writer.sheets['Voltage Drop']127 128 # Create a pie chart for voltage drop analysis129 chart = workbook.add_chart({'type': 'pie'})130 chart.add_series({131 'name': 'Voltage Drop Analysis',132 'categories': ['Voltage Drop', 1, 3, 1, 4],133 'values': ['Voltage Drop', 1, 0, 1, 1],134 'data_labels': {'percentage': True, 'position': 'outside_end'}135 })136 chart.set_title({'name': 'Voltage Drop Analysis'})137 worksheet.insert_chart('F2', chart)138 139 # Add some formatting140 workbook = writer.book141 for sheet in writer.sheets:142 worksheet = writer.sheets[sheet]143 worksheet.set_column('A:A', 30)144 worksheet.set_column('B:B', 20)145 146 return output.getvalue()147 148def generate_pdf_report(data):149 """Generate PDF report from calculation data"""150 buffer = BytesIO()151 doc = SimpleDocTemplate(buffer, pagesize=letter, rightMargin=72, leftMargin=72, topMargin=72, bottomMargin=18)152 styles = getSampleStyleSheet()153 elements = []154 155 # Title156 elements.append(Paragraph(f"Cable Calculation Report - {data['project_name']}", styles['Title']))157 elements.append(Spacer(1, 12))158 159 # System Summary160 elements.append(Paragraph("System Summary", styles['Heading2']))161 sys_data = [162 ["Project Name", data['project_name']],163 ["Designer", data['designer_name']],164 ["Calculation Date", data['calculation_date']],165 ["Current Type", data['system_type']],166 ["Phase Configuration", data['phase_type']],167 ["Application", data['application']],168 ["Voltage (V)", data['voltage']],169 ["Current (A)", data['current']],170 ["Power (W)", data.get('power', 'N/A')],171 ["Power Factor", data['power_factor']],172 ["Cable Length (m)", data['length']],173 ["Duty Cycle (%)", data.get('duty_cycle', 'N/A')]174 ]175 176 if 'parallel_cables' in data:177 sys_data.extend([178 ["Parallel Cables Used", "Yes"],179 ["Number of Cables", data['parallel_cables']['number']],180 ["Size per Cable (mm²)", data['parallel_cables']['size']],181 ["Current per Cable (A)", data['parallel_cables']['current_per_cable']],182 ["Total Current Capacity (A)", data['parallel_cables']['total_current_rating']]183 ])184 185 sys_table = Table(sys_data, colWidths=[2*inch, 3*inch])186 sys_table.setStyle(TableStyle([187 ('BACKGROUND', (0, 0), (-1, 0), colors.lightgrey),188 ('TEXTCOLOR', (0, 0), (-1, 0), colors.black),189 ('ALIGN', (0, 0), (-1, -1), 'LEFT'),190 ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),191 ('FONTSIZE', (0, 0), (-1, 0), 10),192 ('BOTTOMPADDING', (0, 0), (-1, 0), 12),193 ('BACKGROUND', (0, 1), (-1, -1), colors.white),194 ('GRID', (0, 0), (-1, -1), 1, colors.black)195 ]))196 elements.append(sys_table)197 elements.append(Spacer(1, 12))198 199 # Cable Selection200 elements.append(Paragraph("Cable Selection", styles['Heading2']))201 cable_data = [202 ["Material", data['material']],203 ["Selected Size (mm²)", data['selected_size']],204 ["Current Rating (A)", data['current_rating']],205 ["Required Current (A)", data['required_current']],206 ["Resistance (Ω/km)", data['resistance']],207 ["Reactance (Ω/km)", data['reactance']],208 ["Installation Method", data['install_method']],209 ["Ambient Temperature (°C)", data['ambient_temp']],210 ["Temperature Correction", data['temp_correction']],211 ["Tray Type", data.get('tray_type', 'N/A')],212 ["Fill Ratio (%)", data.get('fill_ratio', 'N/A')],213 ["Derating Factor", data.get('derating_factor', 'N/A')],214 ["User Size Limit (mm²)", data.get('user_size_limit', 'N/A')]215 ]216 217 cable_table = Table(cable_data, colWidths=[2*inch, 3*inch])218 cable_table.setStyle(TableStyle([219 ('BACKGROUND', (0, 0), (-1, 0), colors.lightgrey),220 ('TEXTCOLOR', (0, 0), (-1, 0), colors.black),221 ('ALIGN', (0, 0), (-1, -1), 'LEFT'),222 ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),223 ('FONTSIZE', (0, 0), (-1, 0), 10),224 ('BOTTOMPADDING', (0, 0), (-1, 0), 12),225 ('BACKGROUND', (0, 1), (-1, -1), colors.white),226 ('GRID', (0, 0), (-1, -1), 1, colors.black)227 ]))228 elements.append(cable_table)229 elements.append(Spacer(1, 12))230 231 # Voltage Drop Analysis232 elements.append(Paragraph("Voltage Drop Analysis", styles['Heading2']))233 vd_data = [234 ["Voltage Drop (V)", data['voltage_drop']],235 ["Percentage Drop (%)", data['percentage_drop']],236 ["Maximum Allowed (%)", data['max_vd_percent']],237 ["Recommended (%)", data['rec_vd_percent']],238 ["Status", data['status']]239 ]240 241 vd_table = Table(vd_data, colWidths=[2*inch, 3*inch])242 vd_table.setStyle(TableStyle([243 ('BACKGROUND', (0, 0), (-1, 0), colors.lightgrey),244 ('TEXTCOLOR', (0, 0), (-1, 0), colors.black),245 ('ALIGN', (0, 0), (-1, -1), 'LEFT'),246 ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),247 ('FONTSIZE', (0, 0), (-1, 0), 10),248 ('BOTTOMPADDING', (0, 0), (-1, 0), 12),249 ('BACKGROUND', (0, 1), (-1, -1), colors.white),250 ('GRID', (0, 0), (-1, -1), 1, colors.black)251 ]))252 elements.append(vd_table)253 254 # Add voltage drop gauge if data is available255 if data['percentage_drop'] != "N/A":256 try:257 # Create a simple gauge chart258 fig, ax = plt.subplots(figsize=(6, 1))259 max_vd = data['max_vd_percent']260 rec_vd = data['rec_vd_percent']261 vd_percent = float(data['percentage_drop'])262 263 # Create gradient background264 ax.barh(0, max_vd * 1.2, color='#ff4b4b', height=0.2)265 ax.barh(0, max_vd, color='#ffcc00', height=0.2)266 ax.barh(0, rec_vd, color='#00cc66', height=0.2)267 268 # Add indicator269 ax.plot(vd_percent, 0, 'ko', markersize=10)270 ax.text(vd_percent, 0.2, f'{vd_percent:.1f}%', ha='center', va='bottom')271 272 # Add reference lines and labels273 ax.axvline(rec_vd, color='black', linestyle='--', linewidth=0.5)274 ax.axvline(max_vd, color='black', linestyle='--', linewidth=0.5)275 ax.text(rec_vd/2, -0.3, 'Recommended', ha='center')276 ax.text((rec_vd + max_vd)/2, -0.3, 'Acceptable', ha='center')277 ax.text(max_vd * 1.1, -0.3, 'Excessive', ha='center')278 279 ax.set_xlim(0, max_vd * 1.2)280 ax.set_ylim(-0.5, 0.5)281 ax.axis('off')282 ax.set_title('Voltage Drop Gauge')283 284 # Save the plot to a buffer285 img_buffer = BytesIO()286 plt.savefig(img_buffer, format='png', bbox_inches='tight', dpi=150)287 plt.close()288 289 # Add image to PDF290 from reportlab.platypus import Image291 elements.append(Spacer(1, 12))292 elements.append(Paragraph("Voltage Drop Visualization", styles['Heading3']))293 elements.append(Image(img_buffer, width=4*inch, height=1*inch))294 except:295 pass296 297 doc.build(elements)298 return buffer.getvalue()299 300def create_voltage_drop_gauge(percentage_drop, max_vd_percent, rec_vd_percent):301 """Create a voltage drop gauge visualization"""302 fig, ax = plt.subplots(figsize=(8, 2))303 304 # Create gradient background305 max_gauge = max_vd_percent * 1.2306 ax.barh(0, max_gauge, color='#ff4b4b', height=0.2) # Red zone307 ax.barh(0, max_vd_percent, color='#ffcc00', height=0.2) # Yellow zone308 ax.barh(0, rec_vd_percent, color='#00cc66', height=0.2) # Green zone309 310 # Add indicator311 ax.plot(percentage_drop, 0, 'ko', markersize=12)312 ax.text(percentage_drop, 0.2, f'{percentage_drop:.1f}%', ha='center', va='bottom', fontsize=10)313 314 # Add reference lines and labels315 ax.axvline(rec_vd_percent, color='black', linestyle='--', linewidth=1)316 ax.axvline(max_vd_percent, color='black', linestyle='--', linewidth=1)317 ax.text(rec_vd_percent/2, -0.3, 'Recommended', ha='center', fontsize=9)318 ax.text((rec_vd_percent + max_vd_percent)/2, -0.3, 'Acceptable', ha='center', fontsize=9)319 ax.text(max_vd_percent + (max_gauge - max_vd_percent)/2, -0.3, 'Excessive', ha='center', fontsize=9)320 321 ax.set_xlim(0, max_gauge)322 ax.set_ylim(-0.5, 0.5)323 ax.axis('off')324 ax.set_title('Voltage Drop Analysis', pad=20)325 326 return fig327 328def calculate_parallel_cables(required_current, max_vd_percent, current_ratings, standard_sizes, 329 resistivity, length, voltage, system_code, power_factor, reactance_per_km,330 user_size_limit=None):331 """Calculate optimal parallel cable configuration"""332 # Filter available sizes based on user limit333 if user_size_limit:334 available_sizes = [size for size in standard_sizes if size <= user_size_limit]335 else:336 available_sizes = standard_sizes337 338 # Try different numbers of parallel cables (2 to 5)339 for num_cables in range(2, 6):340 current_per_cable = required_current / num_cables341 342 # Find smallest cable that can handle the divided current343 for size in available_sizes:344 idx = standard_sizes.index(size)345 if current_ratings[idx] >= current_per_cable:346 # Calculate voltage drop for this configuration347 csa = size348 r = resistivity / (csa * num_cables) # Resistance is divided by number of parallel cables349 x = reactance_per_km.get(str(csa), 0.07) / 1000 if system_code in ['1', '3'] else 0350 vd = calculate_voltage_drop(system_code, required_current, length, r, x, power_factor)351 percentage_drop = (vd / voltage) * 100352 353 if percentage_drop <= max_vd_percent:354 return {355 'number': num_cables,356 'size': size,357 'current_per_cable': current_per_cable,358 'voltage_drop': vd,359 'percentage_drop': percentage_drop,360 'total_current_rating': current_ratings[idx] * num_cables,361 'resistance': r,362 'reactance': x363 }364 365 return None366 367def main():368 st.set_page_config(369 page_title="Advanced Cable Calculator",370 page_icon="⚡",371 layout="wide",372 initial_sidebar_state="expanded"373 )374 375 # Add custom CSS for mobile responsiveness376 st.markdown("""377 <style>378 @media (max-width: 768px) {379 .sidebar .sidebar-content {380 width: 80% !important;381 }382 .stButton button {383 width: 100% !important;384 }385 .stSelectbox, .stTextInput, .stNumberInput {386 width: 100% !important;387 }388 .stRadio div {389 flex-direction: column !important;390 }391 .stRadio label {392 margin-right: 0 !important;393 margin-bottom: 5px !important;394 }395 }396 .stMarkdown h1, .stMarkdown h2, .stMarkdown h3 {397 color: #2c3e50;398 }399 .stAlert {400 border-radius: 10px;401 }402 .stSuccess {403 background-color: #d4edda;404 }405 .stWarning {406 background-color: #fff3cd;407 }408 .stError {409 background-color: #f8d7da;410 }411 .css-1aumxhk {412 background-color: #f0f2f6;413 border-radius: 10px;414 padding: 20px;415 }416 </style>417 """, unsafe_allow_html=True)418 419 # Initialize session state420 if 'calculate' not in st.session_state:421 st.session_state.calculate = False422 if 'manual_mode' not in st.session_state:423 st.session_state.manual_mode = False424 425 st.title("⚡ Advanced Cable Sizing Calculator")426 427 # Standard cable sizes (up to 630 mm²)428 standard_sizes = [429 1.0, 1.5, 2.5, 4, 6, 10, 16, 25, 35, 50, 70, 95, 120, 150, 185, 240,430 300, 400, 500, 630431 ]432 433 with st.sidebar:434 st.header("Project Information")435 project_name = st.text_input("Project Name:", "My Electrical Project", key='project_name')436 designer_name = st.text_input("Designer Name:", "Electrical Engineer", key='designer_name')437 438 st.header("🔌 System Configuration")439 system_type = st.radio(440 "Current Type:",441 ["AC", "DC"],442 index=0,443 horizontal=True,444 key='system_type'445 )446 447 phase_type = None448 if system_type == "AC":449 phase_type = st.radio(450 "Phase Configuration:",451 ["Single-phase", "Three-phase"],452 index=1,453 horizontal=True,454 key='phase_type'455 )456 457 application = st.selectbox(458 "Application Type:",459 [app.value for app in ApplicationType],460 index=6 if ApplicationType.WELDING.value in [app.value for app in ApplicationType] else 3,461 key='application'462 )463 464 if application == ApplicationType.WELDING.value:465 st.markdown("""466 <div class="welding-special">467 <strong>Note for Welding:</strong> Higher voltage drop allowances may be acceptable 468 for intermittent welding operations. Consider duty cycle in your calculations.469 </div>470 """, unsafe_allow_html=True)471 472 app_params = get_application_parameters(application)473 474 # Input method selection475 input_method = st.radio(476 "Input Method:",477 ["Current (A)", "Power (W)"],478 index=0,479 horizontal=True,480 key='input_method'481 )482 483 if input_method == "Current (A)":484 current = st.number_input(485 "Load Current (A):",486 min_value=0.1,487 value=50.0 if application != ApplicationType.WELDING.value else 150.0,488 step=1.0,489 key='current'490 )491 power = None492 else:493 power = st.number_input(494 "Power (W):",495 min_value=1.0,496 value=10000.0,497 step=100.0,498 key='power'499 )500 current = None501 502 voltage = st.number_input(503 "System Voltage (V):",504 min_value=12.0,505 value=400.0 if application != ApplicationType.WELDING.value else 220.0,506 step=1.0,507 key='voltage'508 )509 510 if system_type == "AC":511 power_factor = st.slider(512 "Power Factor:",513 min_value=0.5,514 max_value=1.0,515 value=float(app_params["typical_pf"]),516 step=0.01,517 key='power_factor'518 )519 else:520 power_factor = 1.0521 522 # Calculate current if power was provided523 if input_method == "Power (W)":524 if system_type == "AC":525 if phase_type == "Single-phase":526 current = power / (voltage * power_factor)527 else: # Three-phase528 current = power / (math.sqrt(3) * voltage * power_factor)529 else: # DC530 current = power / voltage531 st.info(f"Calculated Current: {current:.2f} A")532 533 length = st.number_input(534 "Cable Length (m):",535 min_value=0.1,536 value=100.0,537 step=1.0,538 key='length'539 )540 541 st.header("📏 Cable Specifications")542 material = st.radio(543 "Conductor Material:",544 ["Copper (cu)", "Aluminum (al)"],545 index=0,546 horizontal=True,547 key='material'548 )549 550 st.header("🏗️ Installation Method")551 install_method = st.selectbox(552 "Select Installation Method:",553 [554 "A1 - In conduit in insulated wall",555 "B1 - In conduit on wall or in floor",556 "B2 - Clipped direct",557 "C - In free air",558 "D - Buried in ground",559 "T1 - Perforated tray (single layer)",560 "T2 - Solid bottom tray (single layer)",561 "T3 - Ladder-type tray (single layer)",562 "T4 - Wire mesh tray (single layer)",563 "T5 - Multi-layer in any tray type"564 ],565 index=5,566 key='install_method'567 )568 569 # Handle multi-layer case570 total_derating = 1.0571 tray_type_code = None572 if "T5" in install_method:573 tray_type = st.selectbox(574 "Tray Type for Multi-layer:",575 [576 "T1 - Perforated tray",577 "T2 - Solid bottom tray",578 "T3 - Ladder-type tray",579 "T4 - Wire mesh tray"580 ],581 index=0,582 key='tray_type'583 )584 fill_ratio = st.slider(585 "Tray Fill Ratio:",586 min_value=0.1,587 max_value=0.5,588 value=0.3,589 step=0.05,590 key='fill_ratio'591 )592 593 tray_type_code = tray_type.split(" ")[0]594 base_derating = {'T1': 0.85, 'T2': 0.75, 'T3': 0.88, 'T4': 0.90}[tray_type_code]595 fill_derating = 0.7 + (0.3 * (0.5 - fill_ratio) / 0.4)596 total_derating = base_derating * fill_derating597 install_method_code = tray_type_code598 else:599 install_method_code = install_method.split(" ")[0]600 601 st.header("🌡️ Environmental Factors")602 ambient_temp = st.slider(603 "Ambient Temperature (°C):",604 min_value=10,605 max_value=60,606 value=30,607 key='ambient_temp'608 )609 610 if application == ApplicationType.WELDING.value:611 duty_cycle = st.slider(612 "Welding Duty Cycle (%):",613 min_value=10,614 max_value=100,615 value=60,616 key='duty_cycle'617 )618 619 st.header("⚙️ Calculation Mode")620 st.session_state.manual_mode = st.checkbox(621 "Manual Cable Size Selection",622 help="Check this to manually select cable size and see its effects",623 key='manual_mode_checkbox'624 )625 626 # User size limit option - only show standard sizes627 user_size_limit = st.selectbox(628 "Maximum Cable Size Limit (mm²):",629 ["No limit"] + standard_sizes,630 index=0, # Default to "No limit"631 help="Set maximum cable size to consider. If no single cable can handle the load, parallel cables will be calculated."632 )633 if user_size_limit == "No limit":634 user_size_limit = None635 else:636 user_size_limit = float(user_size_limit)637 638 col1, col2 = st.columns(2)639 with col1:640 if st.button("Calculate", type="primary"):641 st.session_state.calculate = True642 st.session_state.calculation_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")643 with col2:644 if st.button("Reset", type="secondary"):645 reset_calculations()646 st.rerun()647 648 if st.session_state.calculate:649 # Current ratings for each installation method (extended to 630 mm²)650 current_ratings = {651 'A1': [11,14.5,19.5,26,34,46,61,80,99,125,160,195,225,260,300,340,385,435,495,560],652 'B1': [13.5,17.5,24,32,41,57,76,99,125,160,205,250,285,340,385,440,500,565,635,715],653 'B2': [15.5,20,27,36,46,63,85,110,135,175,225,270,310,370,420,480,545,615,690,780],654 'C': [17.5,23,30,40,52,72,96,125,160,205,250,300,345,405,460,530,600,680,765,865],655 'D': [22,29,38,51,67,91,121,160,200,260,320,385,445,520,590,680,770,870,980,1100],656 'T1': [16,20,27,36,47,64,85,115,150,195,245,295,340,400,460,530,600,680,765,865],657 'T2': [14,18,24,32,42,57,76,105,135,175,220,265,305,360,415,480,545,615,690,780],658 'T3': [12,15,20,27,35,48,64,90,115,150,190,230,265,310,360,415,470,530,600,680],659 'T4': [13,17,23,30,40,54,72,100,130,170,215,260,300,350,405,465,530,600,675,765]660 }661 662 # Apply derating if multi-layer663 if "T5" in install_method:664 method_ratings = [r * total_derating for r in current_ratings[install_method_code]]665 else:666 method_ratings = current_ratings[install_method_code]667 668 # System codes669 system_code = "1" if phase_type == "Single-phase" else "3" if system_type == "AC" else "DC"670 material_code = "cu" if "Copper" in material else "al"671 672 # Temperature correction673 temp_correction_factors = {674 'A1': {10:1.22,15:1.17,20:1.12,25:1.06,30:1.00,35:0.94,40:0.87,45:0.79,50:0.71,55:0.61,60:0.50},675 'B1': {10:1.15,15:1.12,20:1.08,25:1.04,30:1.00,35:0.96,40:0.91,45:0.87,50:0.82,55:0.76,60:0.71},676 'B2': {10:1.15,15:1.12,20:1.08,25:1.04,30:1.00,35:0.96,40:0.91,45:0.87,50:0.82,55:0.76,60:0.71},677 'C': {10:1.15,15:1.12,20:1.08,25:1.04,30:1.00,35:0.96,40:0.91,45:0.87,50:0.82,55:0.76,60:0.71},678 'D': {10:1.10,15:1.07,20:1.04,25:1.02,30:1.00,35:0.98,40:0.95,45:0.93,50:0.90,55:0.88,60:0.85},679 'T1': {10:1.12,15:1.09,20:1.06,25:1.03,30:1.00,35:0.96,40:0.92,45:0.88,50:0.84,55:0.79,60:0.74},680 'T2': {10:1.10,15:1.07,20:1.04,25:1.02,30:1.00,35:0.97,40:0.93,45:0.89,50:0.85,55:0.81,60:0.76},681 'T3': {10:1.12,15:1.09,20:1.06,25:1.03,30:1.00,35:0.96,40:0.92,45:0.88,50:0.84,55:0.79,60:0.74},682 'T4': {10:1.11,15:1.08,20:1.05,25:1.02,30:1.00,35:0.97,40:0.93,45:0.89,50:0.85,55:0.80,60:0.75}683 }684 685 available_temps = sorted(temp_correction_factors[install_method_code].keys())686 closest_temp = min(available_temps, key=lambda x: abs(x - ambient_temp))687 temp_correction = temp_correction_factors[install_method_code][closest_temp]688 689 # Calculate temperature-adjusted resistivity690 resistivity_20c = {'cu': 0.0172, 'al': 0.0283}691 temp_coefficient = {'cu': 0.00393, 'al': 0.00403}692 operating_temp = 70 # Typical max for PVC cables693 resistivity = resistivity_20c[material_code] * (1 + temp_coefficient[material_code] * (operating_temp - 20))694 695 # Cable selection696 required_current = current / temp_correction697 698 # Special adjustment for welding duty cycle699 if application == ApplicationType.WELDING.value:700 duty_cycle = st.session_state.get('duty_cycle', 60)701 duty_factor = math.sqrt(duty_cycle/100.0)702 required_current *= duty_factor703 704 # Reactance values (Ω/km) - extended for larger cables705 reactance_per_km = {706 '1.0':0.16,'1.5':0.15,'2.5':0.14,'4':0.13,'6':0.12,707 '10':0.11,'16':0.10,'25':0.09,'35':0.085,'50':0.08,708 '70':0.075,'95':0.07,'120':0.068,'150':0.066,'185':0.064,709 '240':0.062,'300':0.060,'400':0.058,'500':0.056,'630':0.054710 }711 712 # Manual cable size selection mode713 if st.session_state.manual_mode:714 st.header("🔧 Manual Cable Size Selection")715 716 # Filter sizes based on user limit717 if user_size_limit:718 available_sizes = [size for size in standard_sizes if size <= user_size_limit]719 else:720 available_sizes = standard_sizes721 722 selected_size = st.selectbox(723 "Select Cable Size (mm²):",724 available_sizes,725 index=available_sizes.index(10.0) if 10.0 in available_sizes else 0726 )727 728 size_index = standard_sizes.index(selected_size)729 current_rating = method_ratings[size_index]730 731 # Calculate voltage drop for selected size732 csa = selected_size733 r = resistivity / csa734 x = reactance_per_km.get(str(csa), 0.07) / 1000 if system_type == "AC" else 0735 vd = calculate_voltage_drop(system_code, current, length, r, x, power_factor)736 percentage_drop = (vd / voltage) * 100737 738 # Display manual selection results739 st.markdown(f"""740 ### Manual Selection Results741 - **Selected Size:** {selected_size} mm²742 - **Current Rating:** {current_rating:.1f} A743 - **Required Current:** {required_current:.1f} A744 - **Voltage Drop:** {vd:.2f} V ({percentage_drop:.2f}%)745 """)746 747 # Check current rating748 if current_rating < required_current:749 st.error(f"⚠️ Warning: Selected cable ({selected_size}mm²) current rating ({current_rating:.1f}A) is below required ({required_current:.1f}A)")750 751 # Offer parallel cable solution752 parallel_config = calculate_parallel_cables(753 required_current, app_params['max_vd_percent'], method_ratings, standard_sizes,754 resistivity, length, voltage, system_code, power_factor, reactance_per_km,755 user_size_limit756 )757 758 if parallel_config:759 st.success(f"Solution: Use {parallel_config['number']} parallel {parallel_config['size']}mm² cables")760 st.markdown(f"""761 - Current per cable: {parallel_config['current_per_cable']:.1f} A762 - Total current capacity: {parallel_config['total_current_rating']:.1f} A763 - Voltage drop: {parallel_config['voltage_drop']:.2f} V ({parallel_config['percentage_drop']:.2f}%)764 """)765 else:766 st.error("No parallel cable configuration found within limits")767 else:768 st.success(f"✓ Selected cable ({selected_size}mm²) current rating ({current_rating:.1f}A) meets requirement")769 770 # Check voltage drop771 if percentage_drop > app_params['max_vd_percent']:772 st.error(f"⚠️ Warning: Voltage drop ({percentage_drop:.2f}%) exceeds maximum allowed ({app_params['max_vd_percent']}%)")773 elif percentage_drop > app_params['rec_vd_percent']:774 st.warning(f"ℹ️ Note: Voltage drop ({percentage_drop:.2f}%) exceeds recommended ({app_params['rec_vd_percent']}%)")775 else:776 st.success(f"✓ Voltage drop ({percentage_drop:.2f}%) is within recommended limits")777 778 # Show voltage drop gauge779 fig = create_voltage_drop_gauge(percentage_drop, app_params['max_vd_percent'], app_params['rec_vd_percent'])780 st.pyplot(fig)781 plt.close()782 783 # Show calculation details784 with st.expander("Show Calculation Details"):785 st.markdown(f"""786 **Calculation Parameters:**787 - Resistance: {r*1000:.3f} Ω/km788 - Reactance: {x*1000:.3f} Ω/km (AC only)789 - Length: {length} m790 - Power Factor: {power_factor}791 """)792 793 # Prepare data for reports794 report_data = {795 'project_name': project_name,796 'designer_name': designer_name,797 'calculation_date': st.session_state.calculation_date,798 'system_type': system_type,799 'phase_type': phase_type if system_type == "AC" else "N/A",800 'application': application,801 'voltage': voltage,802 'current': current,803 'power': power if input_method == "Power (W)" else "N/A",804 'power_factor': power_factor,805 'length': length,806 'duty_cycle': duty_cycle if application == ApplicationType.WELDING.value else "N/A",807 'material': 'Copper' if material_code == 'cu' else 'Aluminum',808 'selected_size': selected_size,809 'current_rating': current_rating,810 'required_current': required_current,811 'resistance': r*1000,812 'reactance': x*1000,813 'install_method': install_method,814 'ambient_temp': ambient_temp,815 'temp_correction': temp_correction,816 'tray_type': tray_type if "T5" in install_method else "N/A",817 'fill_ratio': fill_ratio*100 if "T5" in install_method else "N/A",818 'derating_factor': total_derating if "T5" in install_method else "N/A",819 'voltage_drop': vd,820 'percentage_drop': percentage_drop,821 'max_vd_percent': app_params['max_vd_percent'],822 'rec_vd_percent': app_params['rec_vd_percent'],823 'status': "Within limits" if percentage_drop <= app_params['max_vd_percent'] else "Exceeds limits",824 'user_size_limit': user_size_limit if user_size_limit else "N/A"825 }826 827 # Add parallel cables info if calculated828 if 'parallel_config' in locals():829 report_data['parallel_cables'] = parallel_config830 831 # Generate reports832 col1, col2 = st.columns(2)833 with col1:834 excel_data = generate_excel_report(report_data)835 st.download_button(836 label="📊 Download Excel Report",837 data=excel_data,838 file_name=f"cable_calculation_{project_name.replace(' ', '_')}.xlsx",839 mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"840 )841 with col2:842 pdf_data = generate_pdf_report(report_data)843 st.download_button(844 label="📄 Download PDF Report",845 data=pdf_data,846 file_name=f"cable_calculation_{project_name.replace(' ', '_')}.pdf",847 mime="application/pdf"848 )849 850 st.markdown("---")851 if st.button("Switch to Automatic Cable Size Selection"):852 st.session_state.manual_mode = False853 st.rerun()854 855 st.stop()856 857 # Automatic cable size selection858 selected_size = None859 parallel_config = None860 861 # Filter available sizes based on user limit862 if user_size_limit:863 available_sizes = [size for size in standard_sizes if size <= user_size_limit]864 else:865 available_sizes = standard_sizes866 867 # First try to find a single cable that meets both current and voltage drop requirements868 for size in available_sizes:869 idx = standard_sizes.index(size)870 if method_ratings[idx] >= required_current:871 csa = size872 r = resistivity / csa873 x = reactance_per_km.get(str(csa), 0.07) / 1000 if system_type == "AC" else 0874 vd = calculate_voltage_drop(system_code, current, length, r, x, power_factor)875 percentage_drop = (vd / voltage) * 100876 877 if percentage_drop <= app_params['max_vd_percent']:878 selected_size = size879 break880 881 # If no single cable found, try parallel cables882 if not selected_size:883 # Find smallest cable that meets current requirement (if any)884 for size in available_sizes:885 idx = standard_sizes.index(size)886 if method_ratings[idx] >= required_current:887 selected_size = size888 csa = size889 r = resistivity / csa890 x = reactance_per_km.get(str(csa), 0.07) / 1000 if system_type == "AC" else 0891 vd = calculate_voltage_drop(system_code, current, length, r, x, power_factor)892 percentage_drop = (vd / voltage) * 100893 break894 895 # Calculate parallel cables if:896 # 1. No single cable meets current requirement, or897 # 2. Single cable meets current but exceeds voltage drop898 if (not selected_size) or (selected_size and percentage_drop > app_params['max_vd_percent']):899 parallel_config = calculate_parallel_cables(900 required_current, app_params['max_vd_percent'], method_ratings, standard_sizes,901 resistivity, length, voltage, system_code, power_factor, reactance_per_km,902 user_size_limit903 )904 905 # Display results906 st.header("📋 Calculation Results")907 st.subheader(f"Project: {project_name}")908 st.caption(f"Designed by: {designer_name} | Calculated on: {st.session_state.calculation_date}")909 910 # Create tabs for different result sections911 tab1, tab2, tab3 = st.tabs(["System Summary", "Cable Selection", "Voltage Drop Analysis"])912 913 with tab1:914 col1, col2 = st.columns(2)915 with col1:916 st.markdown("### System Configuration")917 st.markdown(f"""918 - **Current Type:** {system_type}919 - **Phase Configuration:** {phase_type if system_type == "AC" else "N/A"}920 - **Application:** {application}921 - **Voltage:** {voltage} V922 - **Current:** {current:.2f} A923 - **Power:** {power if input_method == 'Power (W)' else current * voltage * (power_factor if system_type == 'AC' else 1):.2f} W924 - **Power Factor:** {power_factor}925 """)926 if application == ApplicationType.WELDING.value:927 st.markdown(f"""928 - **Duty Cycle:** {duty_cycle}%929 - **Effective Current:** {required_current:.1f} A (adjusted for duty cycle)930 """)931 932 with col2:933 st.markdown("### Installation Details")934 if "T5" in install_method:935 st.markdown(f"""936 - **Method:** Multi-layer in {tray_type}937 - **Fill Ratio:** {fill_ratio*100:.0f}%938 - **Derating Factor:** {total_derating:.3f}939 """)940 else:941 st.markdown(f"""942 - **Method:** {install_method}943 """)944 945 st.markdown(f"""946 - **Ambient Temp:** {ambient_temp}°C947 - **Temp Correction:** {temp_correction:.3f}948 """)949 950 # Show parallel cable info if applicable951 if parallel_config:952 st.markdown("### Parallel Cable Configuration")953 st.markdown(f"""954 - **Number of Cables:** {parallel_config['number']}955 - **Size per Cable:** {parallel_config['size']} mm²956 - **Current per Cable:** {parallel_config['current_per_cable']:.1f} A957 - **Total Current Capacity:** {parallel_config['total_current_rating']:.1f} A958 """)959 960 with tab2:961 col1, col2 = st.columns(2)962 with col1:963 st.markdown("### Cable Specifications")964 if parallel_config:965 st.markdown(f"""966 - **Material:** {'Copper' if material_code == 'cu' else 'Aluminum'}967 - **Configuration:** {parallel_config['number']} parallel {parallel_config['size']}mm² cables968 - **Current Rating per Cable:** {method_ratings[standard_sizes.index(parallel_config['size'])]:.1f} A969 - **Total Current Rating:** {parallel_config['total_current_rating']:.1f} A970 - **Required Current:** {required_current:.1f} A971 """)972 elif selected_size:973 st.markdown(f"""974 - **Material:** {'Copper' if material_code == 'cu' else 'Aluminum'}975 - **Selected Size:** {selected_size} mm²976 - **Current Rating:** {method_ratings[standard_sizes.index(selected_size)]:.1f} A977 - **Required Current:** {required_current:.1f} A978 """)979 else:980 st.error("No suitable cable configuration found")981 982 with col2:983 st.markdown("### Cable Parameters")984 if parallel_config:985 st.markdown(f"""986 - **Resistance per Cable:** {parallel_config['resistance']*1000:.3f} Ω/km987 - **Effective Resistance:** {parallel_config['resistance']*1000/parallel_config['number']:.3f} Ω/km988 - **Reactance:** {parallel_config['reactance']*1000:.3f} Ω/km (AC only)989 - **Length:** {length} m990 """)991 elif selected_size:992 st.markdown(f"""993 - **Resistance:** {r*1000:.3f} Ω/km994 - **Reactance:** {x*1000:.3f} Ω/km (AC only)995 - **Length:** {length} m996 """)997 998 with tab3:999 if parallel_config:1000 vd = parallel_config['voltage_drop']1001 percentage_drop = parallel_config['percentage_drop']1002 elif selected_size:1003 vd = calculate_voltage_drop(system_code, current, length, r, x, power_factor)1004 percentage_drop = (vd / voltage) * 1001005 else:1006 vd = "N/A"1007 percentage_drop = "N/A"1008 1009 st.markdown(f"""1010 ### Voltage Drop Calculation1011 - **Voltage Drop:** {vd if vd != "N/A" else "N/A"} {"" if vd == "N/A" else "V"}1012 - **Percentage Drop:** {percentage_drop if percentage_drop != "N/A" else "N/A"} {"" if percentage_drop == "N/A" else "%"}1013 - **Application Limits:** 1014 - Maximum allowed: {app_params['max_vd_percent']}%1015 - Recommended: {app_params['rec_vd_percent']}%1016 """)1017 1018 # Visual indicator1019 if percentage_drop != "N/A":1020 vd_percent = float(percentage_drop)1021 max_vd = app_params['max_vd_percent']1022 rec_vd = app_params['rec_vd_percent']1023 1024 if vd_percent > max_vd:1025 status = "⚠️ **Warning:** Voltage drop exceeds maximum allowed limit for this application!"1026 elif vd_percent > rec_vd:1027 status = "ℹ️ **Note:** Voltage drop exceeds recommended limit for this application."1028 else:1029 status = "✓ **Acceptable:** Voltage drop is within recommended limits for this application."1030 1031 st.markdown(status)1032 1033 # Create and display voltage drop gauge1034 fig = create_voltage_drop_gauge(vd_percent, max_vd, rec_vd)1035 st.pyplot(fig)1036 plt.close()1037 1038 # Special note for welding applications1039 if application == ApplicationType.WELDING.value:1040 st.markdown("""1041 <div class="welding-special" style="margin-top: 20px;">1042 <strong>Welding Application Note:</strong> The higher voltage drop allowance (7%) 1043 accounts for the intermittent nature of welding operations. For critical welding 1044 applications, consider staying within the 5% recommended limit.1045 </div>1046 """, unsafe_allow_html=True)1047 1048 # Prepare report data1049 if parallel_config:1050 report_data = {1051 'project_name': project_name,1052 'designer_name': designer_name,1053 'calculation_date': st.session_state.calculation_date,1054 'system_type': system_type,1055 'phase_type': phase_type if system_type == "AC" else "N/A",1056 'application': application,1057 'voltage': voltage,1058 'current': current,1059 'power': power if input_method == "Power (W)" else current * voltage * (power_factor if system_type == "AC" else 1),1060 'power_factor': power_factor,1061 'length': length,1062 'duty_cycle': duty_cycle if application == ApplicationType.WELDING.value else "N/A",1063 'material': 'Copper' if material_code == 'cu' else 'Aluminum',1064 'selected_size': f"{parallel_config['number']}x{parallel_config['size']}mm²",1065 'current_rating': parallel_config['total_current_rating'],1066 'required_current': required_current,1067 'resistance': parallel_config['resistance']*1000,1068 'reactance': parallel_config['reactance']*1000,1069 'install_method': install_method,1070 'ambient_temp': ambient_temp,1071 'temp_correction': temp_correction,1072 'tray_type': tray_type if "T5" in install_method else "N/A",1073 'fill_ratio': fill_ratio*100 if "T5" in install_method else "N/A",1074 'derating_factor': total_derating if "T5" in install_method else "N/A",1075 'voltage_drop': parallel_config['voltage_drop'],1076 'percentage_drop': parallel_config['percentage_drop'],1077 'max_vd_percent': app_params['max_vd_percent'],1078 'rec_vd_percent': app_params['rec_vd_percent'],1079 'status': "Within limits" if parallel_config['percentage_drop'] <= app_params['max_vd_percent'] else "Exceeds limits",1080 'user_size_limit': user_size_limit if user_size_limit else "N/A",1081 'parallel_cables': parallel_config1082 }1083 elif selected_size:1084 report_data = {1085 'project_name': project_name,1086 'designer_name': designer_name,1087 'calculation_date': st.session_state.calculation_date,1088 'system_type': system_type,1089 'phase_type': phase_type if system_type == "AC" else "N/A",1090 'application': application,1091 'voltage': voltage,1092 'current': current,1093 'power': power if input_method == "Power (W)" else current * voltage * (power_factor if system_type == "AC" else 1),1094 'power_factor': power_factor,1095 'length': length,1096 'duty_cycle': duty_cycle if application == ApplicationType.WELDING.value else "N/A",1097 'material': 'Copper' if material_code == 'cu' else 'Aluminum',1098 'selected_size': selected_size,1099 'current_rating': method_ratings[standard_sizes.index(selected_size)],1100 'required_current': required_current,1101 'resistance': r*1000,1102 'reactance': x*1000,1103 'install_method': install_method,1104 'ambient_temp': ambient_temp,1105 'temp_correction': temp_correction,1106 'tray_type': tray_type if "T5" in install_method else "N/A",1107 'fill_ratio': fill_ratio*100 if "T5" in install_method else "N/A",1108 'derating_factor': total_derating if "T5" in install_method else "N/A",1109 'voltage_drop': vd,1110 'percentage_drop': percentage_drop,1111 'max_vd_percent': app_params['max_vd_percent'],1112 'rec_vd_percent': app_params['rec_vd_percent'],1113 'status': "Within limits" if percentage_drop <= app_params['max_vd_percent'] else "Exceeds limits",1114 'user_size_limit': user_size_limit if user_size_limit else "N/A"1115 }1116 else:1117 report_data = {1118 'project_name': project_name,1119 'designer_name': designer_name,1120 'calculation_date': st.session_state.calculation_date,1121 'system_type': system_type,1122 'phase_type': phase_type if system_type == "AC" else "N/A",1123 'application': application,1124 'voltage': voltage,1125 'current': current,1126 'power': power if input_method == "Power (W)" else "N/A",1127 'power_factor': power_factor,1128 'length': length,1129 'duty_cycle': duty_cycle if application == ApplicationType.WELDING.value else "N/A",1130 'material': 'Copper' if material_code == 'cu' else 'Aluminum',1131 'selected_size': "N/A",1132 'current_rating': "N/A",1133 'required_current': required_current,1134 'resistance': "N/A",1135 'reactance': "N/A",1136 'install_method': install_method,1137 'ambient_temp': ambient_temp,1138 'temp_correction': temp_correction,1139 'tray_type': tray_type if "T5" in install_method else "N/A",1140 'fill_ratio': fill_ratio*100 if "T5" in install_method else "N/A",1141 'derating_factor': total_derating if "T5" in install_method else "N/A",1142 'voltage_drop': "N/A",1143 'percentage_drop': "N/A",1144 'max_vd_percent': app_params['max_vd_percent'],1145 'rec_vd_percent': app_params['rec_vd_percent'],1146 'status': "No suitable cable found",1147 'user_size_limit': user_size_limit if user_size_limit else "N/A"1148 }1149 1150 # Generate reports1151 col1, col2 = st.columns(2)1152 with col1:1153 excel_data = generate_excel_report(report_data)1154 st.download_button(1155 label="📊 Download Excel Report",1156 data=excel_data,1157 file_name=f"cable_calculation_{project_name.replace(' ', '_')}.xlsx",1158 mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"1159 )1160 with col2:1161 pdf_data = generate_pdf_report(report_data)1162 st.download_button(1163 label="📄 Download PDF Report",1164 data=pdf_data,1165 file_name=f"cable_calculation_{project_name.replace(' ', '_')}.pdf",1166 mime="application/pdf"1167 )1168 1169if __name__ == "__main__":1170 main()