CoolFace
Apppublic

Navyia/Lighting_Needs

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
app.py408 linesDownload Raw Back to root
1import gradio as gr2import matplotlib3matplotlib.use('Agg') # Non-interactive backend for Matplotlib for server environments4import matplotlib.pyplot as plt5from fpdf import FPDF # This typically uses the fpdf2 library if installed6import csv7import tempfile8import os9import datetime10 11# --- Constants and Configuration ---12RECOMMENDED_LEVELS = {13    "General Office Work": (300, 500), "Detailed Office Work / Drafting": (500, 750),14    "Corridors / Lobbies": (100, 200), "Classrooms": (300, 500),15    "Retail (General)": (500, 750), "Warehousing (Storage)": (100, 200),16    "Residential Living Room (General)": (50, 150),17    "Futsal Court (Recreational/Training)": (200, 300),18    "Futsal Court (Regional Competition)": (300, 500),19    "Futsal Court (National/International)": (500, 750),20    "Stadium (Class III - Local Club, Training)": (150, 300),21    "Stadium (Class II - Regional, Spectators)": (300, 700),22    "Stadium (Class I - National/Intl. Broadcast)": (1000, 2500),23    "Streetlight (Pedestrian/Cycle Path, Low Risk)": (3, 10),24    "Streetlight (Residential Road, Low Speed)": (5, 15),25    "Streetlight (Collector Road, Medium Speed)": (10, 30),26    "Streetlight (Main Road/Arterial, Higher Speed)": (15, 50),27    "Industrial Assembly (Medium Detail)": (300, 500),28    "Hospital General Areas": (200, 300),29}30AGE_GROUPS = ["Under 40", "40 to 55", "Over 55"]31 32# --- Helper Functions ---33 34def get_ies_adjusted_target(base_min_lux, base_max_lux, occupant_age_group, avg_reflectance_percent):35    target_lux = (base_min_lux + base_max_lux) / 236    range_descriptor = "Intermediate"37    if base_min_lux == 0 and base_max_lux == 0:38        return None, "N/A (Invalid base range)", "N/A"39 40    if occupant_age_group == "Under 40" and avg_reflectance_percent > 70:41        target_lux = base_min_lux42        range_descriptor = "Lower"43    elif occupant_age_group == "Over 55" and avg_reflectance_percent < 30:44        target_lux = base_max_lux45        range_descriptor = "Higher"46    elif (occupant_age_group == "40 to 55" and 30 <= avg_reflectance_percent <= 70) or \47         (occupant_age_group == "Under 40" and avg_reflectance_percent < 30) or \48         (occupant_age_group == "Over 55" and avg_reflectance_percent > 70):49        target_lux = (base_min_lux + base_max_lux) / 250        range_descriptor = "Intermediate"51 52    if range_descriptor == "Lower":53        refined_range_str = f"Targeting lower end: ~{base_min_lux:,.0f} lux (Base: {base_min_lux}-{base_max_lux})"54    elif range_descriptor == "Higher":55        refined_range_str = f"Targeting higher end: ~{base_max_lux:,.0f} lux (Base: {base_min_lux}-{base_max_lux})"56    else: # Intermediate57        mid_point = (base_min_lux + base_max_lux) / 258        refined_range_str = f"Targeting intermediate: ~{mid_point:,.0f} lux (Base: {base_min_lux}-{base_max_lux})"59    return target_lux, refined_range_str, range_descriptor60 61def generate_plot(avg_illuminance, adjusted_target_lux, base_range_lux, space_type):62    fig, ax = plt.subplots(figsize=(8, 4))63    plt.rcParams['font.family'] = 'Times New Roman' # Set default font for this plot64 65    if adjusted_target_lux is None or base_range_lux == (0,0) or avg_illuminance is None:66        ax.text(0.5, 0.5, "Not enough data for a meaningful plot.\nCheck inputs or space type.",67                ha='center', va='center', fontsize=10, color='red', fontname="Times New Roman")68        ax.set_xticks([])69        ax.set_yticks([])70        plt.tight_layout()71        return fig72 73    labels = ['Calculated Illuminance', 'IES Adjusted Target']74    values = [avg_illuminance, adjusted_target_lux]75    base_min, base_max = base_range_lux76 77    bars = ax.barh(labels, values, color=['skyblue', 'lightgreen'], zorder=3)78    ax.set_xlabel('Illuminance (lux)')79    ax.set_title(f'Illuminance Comparison for {space_type}')80 81    ax.axvline(base_min, color='gray', linestyle='--', linewidth=1, label=f'Base Min ({base_min:,.0f} lux)', zorder=1)82    ax.axvline(base_max, color='gray', linestyle='--', linewidth=1, label=f'Base Max ({base_max:,.0f} lux)', zorder=1)83    ax.fill_betweenx(y=[-0.5, len(labels)-0.5], x1=base_min, x2=base_max, color='whitesmoke', alpha=0.7, label='Base Recommended Range', zorder=2)84 85    for i, bar in enumerate(bars):86        width = bar.get_width()87        text_x_pos = width + (ax.get_xlim()[1] * 0.01)88        if width < (ax.get_xlim()[1] * 0.1):89             text_x_pos = width + (ax.get_xlim()[1] * 0.01)90        ax.text(text_x_pos, bar.get_y() + bar.get_height()/2., f'{width:,.1f}',91                ha='left', va='center', zorder=4)92 93    ax.legend(loc='lower right')94    plt.gca().invert_yaxis()95    plt.tight_layout()96    return fig97 98def generate_pdf_report(inputs_dict, results_dict, space_type, occupant_age, avg_reflectance, plot_fig):99    pdf = FPDF()100    pdf.add_page()101    font_name = "Times"102    try:103        pdf.set_font(font_name, "B", 16)104    except RuntimeError:105        font_name = "Arial" # Fallback106        pdf.set_font(font_name, "B", 16)107        print("FPDF Warning: Times font not found, falling back to Arial for PDF.")108 109    pdf.cell(0, 10, "Lighting Calculation Report", 0, 1, "C")110    pdf.set_font(font_name, "", 10)111    pdf.cell(0, 6, f"Date: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}", 0, 1, "C")112    pdf.ln(5)113 114    pdf.set_font(font_name, "B", 12)115    pdf.cell(0, 10, "Input Parameters:", 0, 1, "L")116    pdf.set_font(font_name, "", 10)117    for key, value in inputs_dict.items():118        pdf.cell(0, 6, f"  {key}: {value}", 0, 1, "L")119    pdf.cell(0, 6, f"  Selected Space Type: {space_type}", 0, 1, "L")120    pdf.cell(0, 6, f"  Occupant Age Group: {occupant_age}", 0, 1, "L")121    pdf.cell(0, 6, f"  Calculated Avg. Room Reflectance: {avg_reflectance:.1f}% (for IES adjustment)", 0, 1, "L")122    pdf.ln(5)123 124    pdf.set_font(font_name, "B", 12)125    pdf.cell(0, 10, "Calculated Results:", 0, 1, "L")126    pdf.set_font(font_name, "", 10)127    for key, value in results_dict.items():128        if key == "Comparison to IES Adjusted Target":129            pdf.multi_cell(0, 6, f"  {key}:\n  {value.replace('.', '. ').replace('  ', ' ')}")130        else:131            pdf.cell(0, 6, f"  {key}: {value}", 0, 1, "L")132    pdf.ln(5)133 134    if plot_fig:135        with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as tmp_plot:136            plot_fig.savefig(tmp_plot.name, dpi=150, bbox_inches='tight')137            plot_path = tmp_plot.name138        139        page_width = pdf.w - 2 * pdf.l_margin140        img_width = page_width * 0.9141        img_aspect_ratio = plot_fig.get_figheight() / plot_fig.get_figwidth() if plot_fig.get_figwidth() > 0 else 1142        img_render_height = img_width * img_aspect_ratio143        if pdf.get_y() + 10 + img_render_height > pdf.page_break_trigger:144            pdf.add_page()145 146        pdf.set_font(font_name, "B", 12)147        pdf.cell(0, 10, "Illuminance Graph:", 0, 1, "L")148        pdf.image(plot_path, x=pdf.l_margin, w=img_width)149        os.remove(plot_path)150 151    with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_pdf:152        pdf.output(tmp_pdf.name, "F")153        return tmp_pdf.name154 155def generate_csv_file(inputs_dict, results_dict, space_type, occupant_age, avg_reflectance):156    fieldnames = list(inputs_dict.keys()) + \157                 ["Selected Space Type", "Occupant Age Group", "Avg. Room Reflectance (%)"] + \158                 list(results_dict.keys())159    with tempfile.NamedTemporaryFile(mode='w+', delete=False, suffix=".csv", newline='', encoding='utf-8') as tmp_csv:160        writer = csv.DictWriter(tmp_csv, fieldnames=fieldnames)161        writer.writeheader()162        row_data = inputs_dict.copy()163        row_data["Selected Space Type"] = space_type164        row_data["Occupant Age Group"] = occupant_age165        row_data["Avg. Room Reflectance (%)"] = f"{avg_reflectance:.1f}"166        for r_key, r_val in results_dict.items():167            cleaned_val = str(r_val)168            if " lumens" in cleaned_val: cleaned_val = cleaned_val.replace(" lumens", "")169            if " lux" in cleaned_val: cleaned_val = cleaned_val.replace(" lux", "")170            cleaned_val = cleaned_val.replace(",", "")171            if r_key == "Comparison to IES Adjusted Target":172                 cleaned_val = ' '.join(cleaned_val.splitlines()).strip()173            row_data[r_key] = cleaned_val174        writer.writerow(row_data)175        return tmp_csv.name176 177# --- Main Calculation Function ---178def calculate_lighting(179    room_length, room_width, mounting_height,180    num_fixtures, spacing_fixtures,181    lumens_per_lamp, lamps_per_fixture,182    cu, ldd, lld, bf,183    reflect_ceiling, reflect_walls, reflect_floor,184    space_type, occupant_age_group185):186    error_messages = []187    if not isinstance(room_length, (int, float)) or room_length <= 0: error_messages.append("Room Length must be > 0.")188    if not isinstance(room_width, (int, float)) or room_width <= 0: error_messages.append("Room Width must be > 0.")189    if not isinstance(mounting_height, (int, float)) or mounting_height <= 0: error_messages.append("Mounting Height must be > 0.")190    if not isinstance(num_fixtures, (int, float)) or num_fixtures <= 0: error_messages.append("Number of Fixtures must be > 0.")191    if not isinstance(spacing_fixtures, (int, float)) or spacing_fixtures <= 0: error_messages.append("Spacing must be > 0.")192    if not isinstance(lumens_per_lamp, (int, float)) or lumens_per_lamp <= 0: error_messages.append("Lumens per Lamp must be > 0.")193    if not isinstance(lamps_per_fixture, (int, float)) or lamps_per_fixture <= 0: error_messages.append("Lamps per Fixture must be > 0.")194    if not (0 < cu <= 1): error_messages.append("CU must be > 0 and <= 1.")195    if not (0 < ldd <= 1): error_messages.append("LDD must be > 0 and <= 1.")196    if not (0 < lld <= 1): error_messages.append("LLD must be > 0 and <= 1.")197    if not (0 < bf <= 1): error_messages.append("BF must be > 0 and <= 1.")198    if not (0 <= reflect_ceiling <= 100): error_messages.append("Ceiling Reflectance: 0-100%.")199    if not (0 <= reflect_walls <= 100): error_messages.append("Wall Reflectance: 0-100%.")200    if not (0 <= reflect_floor <= 100): error_messages.append("Floor Reflectance: 0-100%.")201 202    error_plot = generate_plot(None, None, (0,0), "Error in Inputs")203    204    if error_messages:205        error_text = "Error:\n" + "\n".join(error_messages)206        return (error_text, "-", "-", "-", "-", "-", error_plot, None, None)207 208    room_area = room_length * room_width209    if room_area == 0:210         error_text = "Error: Room Area cannot be zero (check dimensions)."211         return (error_text, "-", "-", "-", "-", "-", error_plot, None, None)212 213    total_initial_lumens = num_fixtures * lamps_per_fixture * lumens_per_lamp214    effective_lumens_delivered = total_initial_lumens * cu * lld * ldd * bf215    average_illuminance = effective_lumens_delivered / room_area216 217    base_target_range_lux = RECOMMENDED_LEVELS.get(space_type, (0, 0))218    avg_reflectance = (reflect_ceiling + reflect_walls + reflect_floor) / 3219    220    adjusted_target_lux, ies_adjusted_target_str, _ = get_ies_adjusted_target(221        base_target_range_lux[0], base_target_range_lux[1], occupant_age_group, avg_reflectance222    )223 224    base_target_range_str = f"{base_target_range_lux[0]:,}–{base_target_range_lux[1]:,} lux (Base for {space_type})" \225                            if base_target_range_lux != (0,0) else "N/A (Select valid space type)"226 227    comparison_str = "N/A (Cannot compare - check target or calculated values)"228    comparison_text_for_report = comparison_str229    if adjusted_target_lux is not None and average_illuminance is not None:230        tolerance = 0.20 * adjusted_target_lux231        if average_illuminance < adjusted_target_lux * (1 - 0.20) :232            comparison_str = f"BELOW IES Adjusted Target (~{adjusted_target_lux:,.0f} lux)."233        elif average_illuminance > adjusted_target_lux * (1 + 0.20) :234            comparison_str = f"ABOVE IES Adjusted Target (~{adjusted_target_lux:,.0f} lux)."235        else:236            comparison_str = f"WITHIN IES Adjusted Target (~{adjusted_target_lux:,.0f} lux)."237        comparison_text_for_report = comparison_str238        comparison_str += ("\nNote: Higher speed/accuracy needs for tasks generally demand more light. "239                           "This factor is not explicitly quantified in this calculation but should be considered in detailed design.")240 241    inputs_data = {242        "Room Length (m)": room_length, "Room Width (m)": room_width, "Mounting Height (m)": mounting_height,243        "Number of Fixtures": num_fixtures, "Avg. Spacing (m)": spacing_fixtures,244        "Lumen Output per Lamp": lumens_per_lamp, "Lamps per Fixture": lamps_per_fixture,245        "Coefficient of Utilization (CU)": cu, "Lamp Lumen Depreciation (LLD)": lld,246        "Luminaire Dirt Depreciation (LDD)": ldd, "Ballast Factor (BF)": bf,247        "Ceiling Reflectance (%)": reflect_ceiling, "Wall Reflectance (%)": reflect_walls,248        "Floor Reflectance (%)": reflect_floor249    }250    results_data_for_report = {251        "Total Initial Lumens": f"{total_initial_lumens:,.0f}",252        "Total Effective Lumens to Workplane": f"{effective_lumens_delivered:,.0f}",253        "Calculated Average Illuminance (lux)": f"{average_illuminance:,.2f}",254        "Base Recommended Range for Space Type (lux)": base_target_range_str.replace(f" lux (Base for {space_type})","").strip(),255        "IES Adjusted Target": ies_adjusted_target_str,256        "Comparison to IES Adjusted Target": comparison_text_for_report257    }258 259    plot_object = generate_plot(average_illuminance, adjusted_target_lux, base_target_range_lux, space_type)260    pdf_filepath = None261    csv_filepath = None262    try:263        pdf_filepath = generate_pdf_report(inputs_data, results_data_for_report, space_type, occupant_age_group, avg_reflectance, plot_object)264        csv_filepath = generate_csv_file(inputs_data, results_data_for_report, space_type, occupant_age_group, avg_reflectance)265    except Exception as e:266        print(f"Error generating report files: {e}")267        comparison_str += "\nWARNING: Could not generate report files."268    269    plt.close(plot_object)270 271    return (272        f"{total_initial_lumens:,.0f} lumens",273        f"{effective_lumens_delivered:,.0f} lumens",274        f"{average_illuminance:,.2f} lux",275        base_target_range_str,276        ies_adjusted_target_str,277        comparison_str,278        plot_object,279        pdf_filepath,280        csv_filepath281    )282 283# --- Gradio Interface ---284 285custom_css = """286body, .gradio-container, button, input, select, textarea, label, .gr-label, .gr-button, .gr-input, .gr-dropdown, .gr-textbox, .gr-slider, .markdown-body {287    font-family: "Times New Roman", Times, serif !important;288}289h1, h2, h3, h4, h5, h6, .gr-title, .gr-subtitle {290    font-family: "Times New Roman", Times, serif !important;291}292"""293 294with gr.Blocks(theme=gr.themes.Soft(), css=custom_css, title="Lighting Calculator") as app:295    gr.Markdown(296        """297        # Advanced Indoor/Outdoor Lighting Calculator298        Calculates average illuminance using the IES method, with adjustments for occupant age and room/area reflectances.299        Formula: `Illuminance (lux) = (N × n × Φ × CU × LLD × LDD × BF) / A`300        """301    )302 303    with gr.Row():304        with gr.Column(scale=1):305            gr.Markdown("### 1. Room/Area & Fixture Geometry")306            room_length = gr.Number(label="Length (m)", value=10.0, minimum=0.001)307            room_width = gr.Number(label="Width (m)", value=8.0, minimum=0.001)308            mounting_height = gr.Number(label="Mounting Height (m) - Fixture to Workplane/Target", value=2.5, minimum=0.001)309            num_fixtures = gr.Number(label="Number of Fixtures", value=12, minimum=1, step=1, precision=0)310            spacing_fixtures = gr.Number(label="Avg. Spacing (m) (Informational)", value=2.0, minimum=0.001,311                                         info="Relevant for uniformity, not avg. illuminance formula.")312 313        with gr.Column(scale=1):314            gr.Markdown("### 2. Lamp & Fixture Details")315            lumens_per_lamp = gr.Number(label="Lumen Output per Lamp", value=3200, minimum=1)316            lamps_per_fixture = gr.Number(label="Lamps per Fixture", value=2, minimum=1, step=1, precision=0)317            cu = gr.Slider(minimum=0.01, maximum=1.0, value=0.65, step=0.01, label="Coefficient of Utilization (CU)",318                           info="Fixture & room/area efficiency. For outdoor, this is a simplification.")319            320            gr.Markdown("### 3. Light Loss Factors")321            lld = gr.Slider(minimum=0.01, maximum=1.0, value=0.85, step=0.01, label="Lamp Lumen Depreciation (LLD)")322            ldd = gr.Slider(minimum=0.01, maximum=1.0, value=0.90, step=0.01, label="Luminaire Dirt Depreciation (LDD)")323            bf = gr.Slider(minimum=0.01, maximum=1.0, value=0.95, step=0.01, label="Ballast Factor (BF) / Driver Factor")324 325    with gr.Row():326         with gr.Column(scale=1):327            gr.Markdown("### 4. Surface Reflectances (%) (Indoor CU & IES Adjustment)")328            reflect_ceiling = gr.Slider(minimum=0, maximum=100, value=70, step=1, label="Ceiling Reflectance (%)", info="0 for open sky/outdoor.")329            reflect_walls = gr.Slider(minimum=0, maximum=100, value=50, step=1, label="Avg. Wall Reflectance (%)", info="0 if no significant vertical surfaces.")330            reflect_floor = gr.Slider(minimum=0, maximum=100, value=20, step=1, label="Floor/Ground Cavity Reflectance (%)")331         with gr.Column(scale=1):332            gr.Markdown("### 5. Application & Occupant Details")333            space_type_dropdown = gr.Dropdown(334                choices=sorted(list(RECOMMENDED_LEVELS.keys())),335                value="General Office Work",336                label="Select Space Type"337            )338            occupant_age_group_dropdown = gr.Dropdown(339                choices=AGE_GROUPS,340                value="40 to 55",341                label="Dominant Occupant Age Group"342            )343 344    calculate_button = gr.Button("Calculate Illuminance & Generate Report", variant="primary")345 346    gr.Markdown("---")347    gr.Markdown("## Results")348    349    out_total_initial_lumens = gr.Textbox(label="Total Initial Lumens (Raw Output)")350    out_effective_lumens = gr.Textbox(label="Total Effective Lumens to Workplane (Inc. CU & Loss Factors)")351    out_avg_illuminance = gr.Textbox(label="Calculated Average Illuminance (lux)")352    out_base_target_range = gr.Textbox(label="Base Recommended Range for Space Type (lux)")353    out_ies_adjusted_target = gr.Textbox(label="IES Adjusted Target (based on age/reflectance)")354    out_comparison = gr.Textbox(label="Comparison to IES Adjusted Target", lines=4)355    356    gr.Markdown("### Visual Comparison")357    out_plot = gr.Plot(label="Illuminance Graph")358 359    gr.Markdown("### Downloads")360    with gr.Row():361        out_pdf_report = gr.File(label="Download PDF Report")362        out_csv_data = gr.File(label="Download CSV Data")363 364    inputs_list = [365        room_length, room_width, mounting_height,366        num_fixtures, spacing_fixtures,367        lumens_per_lamp, lamps_per_fixture,368        cu, ldd, lld, bf,369        reflect_ceiling, reflect_walls, reflect_floor,370        space_type_dropdown, occupant_age_group_dropdown371    ]372    outputs_list = [373        out_total_initial_lumens,374        out_effective_lumens,375        out_avg_illuminance,376        out_base_target_range,377        out_ies_adjusted_target,378        out_comparison,379        out_plot,380        out_pdf_report,381        out_csv_data382    ]383 384    calculate_button.click(385        fn=calculate_lighting,386        inputs=inputs_list,387        outputs=outputs_list388    )389    390    gr.Markdown(391        """392        ### Important Considerations:393        - **Coefficient of Utilization (CU):** For indoor spaces, CU depends on luminaire distribution, Room Cavity Ratio (RCR), and surface reflectances. This tool uses a direct CU input. For outdoor/street lighting, CU is not typically used in this simplified way; photometric data (IES files) and specialized software (e.g., DIALux, AGi32) are used to calculate illuminance distribution, average, and uniformity. The "CU" here for outdoor can be seen as a very rough overall efficiency factor.394        - **Reflectances for Outdoor:** For streetlights or large open stadiums, "Ceiling" reflectance is effectively 0 (sky). "Wall" reflectances might be negligible unless there are significant nearby buildings. Ground reflectance is key.395        - **Uniformity:** This calculator provides *average* illuminance. It does **not** assess illuminance uniformity (min/avg or min/max ratios), which is critical for visual comfort and task performance, especially for sports and road lighting.396        - **Task Specifics & Glare:** The "Speed and Accuracy" note from IES is qualitative. Critical tasks or those requiring high visual acuity need higher light levels. Glare control is also paramount and not assessed here.397        - **Vertical Illuminance:** For sports (especially broadcast) and some other applications, vertical illuminance is as important, or more so, than horizontal. This tool only calculates average horizontal illuminance.398        - **Standards:** The recommended levels are illustrative. Always consult the latest IES Lighting Handbook, CIBSE codes, EN standards (e.g., EN 12464, EN 12193, EN 13201), or relevant national/local standards for definitive requirements.399        - **Font Availability:** This app attempts to use "Times New Roman". If not available on your system, a default serif font will be used. For plots and PDFs, the font must be available to Matplotlib/FPDF on the server where this code runs.400        - **Temporary Files:** Downloaded reports are generated as temporary files. Ensure your environment has permissions to write temporary files.401        """402    )403 404if __name__ == "__main__":405    # Ensure the environment has the required packages:406    # pip install gradio matplotlib fpdf2407    # If in Docker, ensure these are in requirements.txt and installed during build.408    app.launch(debug=True)