CoolFace
Apppublic

ameen135/Cable_Size_Selection

sourceHugging Facemitupdated 1y agoView on Hugging Face
1likes
app.py1170 linesDownload Raw Back to root
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()