Navyia/Lighting_Needs
0
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)