import math # For access to infinity import gradio # For building the interface import pandas # For working with tables from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline # For LLMS # Instantiate the model that we'll be calling. This is a tiny one! MODEL_ID = "HuggingFaceTB/SmolLM2-135M-Instruct" tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) pipe = pipeline( task="text-generation", model=AutoModelForCausalLM.from_pretrained( MODEL_ID, ), tokenizer=tokenizer ) import math import gradio as gr import pandas as pd # --- IMPORTANT: Model and Tokenizer Setup (User must provide this) --- # To run this script, you must load a Hugging Face model and tokenizer. # For example: # # from transformers import pipeline, AutoTokenizer # import torch # # model_id = "microsoft/phi-2" # tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True) # pipe = pipeline( # "text-generation", # model=model_id, # tokenizer=tokenizer, # torch_dtype=torch.bfloat16, # device_map="auto", # trust_remote_code=True # ) # # If you do not define 'pipe' and 'tokenizer', this script will raise a NameError. # --- Core Calculation Logic --- def size_ac_motor_complex( required_power_hp: float = None, required_torque_lb_ft: float = None, speed_rpm: float = None, voltage_v: float = None, efficiency: float = 0.85, service_factor: float = 1.15, power_factor: float = 0.8, motor_type: str = "induction" ) -> dict: """ Calculates the required motor nameplate power based on load requirements. """ if required_power_hp is not None: required_load_power_watts = required_power_hp * 745.7 elif required_torque_lb_ft is not None and speed_rpm is not None: hp_from_torque = (required_torque_lb_ft * speed_rpm) / 5252.0 required_load_power_watts = hp_from_torque * 745.7 required_power_hp = hp_from_torque else: return {"error": "Either 'Required Power (HP)' or both 'Required Torque (lb-ft)' and 'Speed (rpm)' must be provided."} if efficiency <= 0 or power_factor <= 0 or service_factor <= 0: return {"error": "Efficiency, Power Factor, and Service Factor must be positive values."} sized_mechanical_power_watts = required_load_power_watts * service_factor electrical_power_drawn_watts = required_load_power_watts / efficiency sized_mechanical_power_kw = sized_mechanical_power_watts / 1000 electrical_power_drawn_kw = electrical_power_drawn_watts / 1000 notes = f"Sizing calculation for a standard {motor_type} motor." return { "calculated_load_hp": required_power_hp, "required_torque_lb_ft": required_torque_lb_ft, "speed_rpm": speed_rpm, "voltage_v": voltage_v, "sized_mechanical_power_kw": sized_mechanical_power_kw, "electrical_power_drawn_kw": electrical_power_drawn_kw, "efficiency": efficiency, "service_factor": service_factor, "power_factor": power_factor, "motor_type": motor_type, "notes": notes } # --- LLM Helper Functions (Now require a live model) --- def _format_chat(system_prompt: str, user_prompt: str) -> str: """This helper function applies a chat format to help the LLM understand.""" messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ] # This function now assumes a 'tokenizer' object is available globally. return tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=True ) def _llm_generate(prompt: str, max_tokens: int) -> str: """This function uses the LLM to generate a response.""" # This function now assumes a 'pipe' object (pipeline) is available globally. out = pipe( prompt, max_new_tokens=max_tokens, do_sample=True, temperature=0.5, return_full_text=False, ) return out[0]["generated_text"] def llm_explain(results: dict) -> str: """This function generates an explanation of the results using the LLM structure.""" if "error" in results: return f"Error: {results['error']}" system_prompt = ( "You explain engineering to a smart 5-year-old. " "Use food-based analogies to support the explanation. " "You always return CONCISE responses, only one sentence." ) user_prompt = ( f"My machine needs {results['calculated_load_hp']:.2f} horsepower to run. " f"Considering a service factor of {results['service_factor']}, the calculated sized motor power is {results['sized_mechanical_power_kw']:.2f} kW. " f"Explain what the 'sized motor power' means in one friendly sentence using a food-based analogy for a non-expert." ) formatted_prompt = _format_chat(system_prompt, user_prompt) return _llm_generate(formatted_prompt, max_tokens=128) # --- Gradio Interface Function --- def run_calculation_interface(required_power_hp, required_torque_lb_ft, speed_rpm, voltage_v, efficiency, service_factor, power_factor, motor_type): """Connects the Gradio UI to the backend calculation logic.""" required_power_hp = float(required_power_hp) if required_power_hp else None required_torque_lb_ft = float(required_torque_lb_ft) if required_torque_lb_ft else None speed_rpm = float(speed_rpm) if speed_rpm else None voltage_v = float(voltage_v) if voltage_v else None results = size_ac_motor_complex( required_power_hp=required_power_hp, required_torque_lb_ft=required_torque_lb_ft, speed_rpm=speed_rpm, voltage_v=voltage_v, efficiency=float(efficiency), service_factor=float(service_factor), power_factor=float(power_factor), motor_type=motor_type ) # Wrap the LLM call in a try-except block to handle potential errors try: narrative = llm_explain(results) except NameError: narrative = "LLM Error: The 'pipe' and 'tokenizer' objects are not defined. Please load a model." except Exception as e: narrative = f"An unexpected error occurred with the LLM: {e}" if "error" in results: df = pd.DataFrame() else: df = pd.DataFrame([{ "Required Load [HP]": f"{results['calculated_load_hp']:.2f}", "Sized Motor Rating [kW]": f"{results['sized_mechanical_power_kw']:.2f}", "Estimated Electrical Draw [kW]": f"{results['electrical_power_drawn_kw']:.2f}", "Service Factor": results["service_factor"], "Efficiency": results["efficiency"], }]) return df, narrative # --- Gradio User Interface Definition --- with gr.Blocks(theme=gr.themes.Soft()) as demo: gr.Markdown("# Advanced AC Motor Sizing Calculator") gr.Markdown("Size an AC motor based on your load requirements. Provide either the required power directly, or the torque and speed for the application.") with gr.Row(): with gr.Column(scale=2): gr.Markdown("### Load Requirements") with gr.Row(): required_power_hp = gr.Number(label="Required Power [HP] (Option 1)") required_torque_lb_ft = gr.Number(label="Required Torque [lb-ft] (Option 2)") speed_rpm = gr.Number(label="Speed [rpm] (with Torque)") with gr.Column(scale=1): gr.Markdown("### Electrical & Motor Parameters") voltage_v = gr.Number(label="Voltage [V] (Optional)") motor_type = gr.Dropdown(choices=["induction", "synchronous", "servo"], value="induction", label="Motor Type") with gr.Row(): efficiency = gr.Slider(minimum=0.5, maximum=0.99, value=0.85, step=0.01, label="Motor Efficiency") service_factor = gr.Slider(minimum=1.0, maximum=2.0, value=1.15, step=0.05, label="Service Factor") power_factor = gr.Slider(minimum=0.5, maximum=1.0, value=0.80, step=0.01, label="Power Factor") run_btn = gr.Button("Calculate Motor Size", variant="primary") gr.Markdown("---") gr.Markdown("### Results") results_df = gr.Dataframe(label="Summary", interactive=False) explain_md = gr.Markdown(label="Explanation") run_btn.click( fn=run_calculation_interface, inputs=[required_power_hp, required_torque_lb_ft, speed_rpm, voltage_v, efficiency, service_factor, power_factor, motor_type], outputs=[results_df, explain_md] ) gr.Examples( examples=[ [5.0, None, None, 480, 0.90, 1.25, 0.85, "induction"], [None, 10.0, 1750, 230, 0.88, 1.15, 0.82, "induction"], [1.0, None, None, 208, 0.85, 1.0, 0.75, "synchronous"], ], inputs=[required_power_hp, required_torque_lb_ft, speed_rpm, voltage_v, efficiency, service_factor, power_factor, motor_type], label="Example Scenarios", ) if __name__ == "__main__": # --- IMPORTANT --- # You must define 'pipe' and 'tokenizer' before this line for the app to work. # For example, place the model loading code from the top of the script here. demo.launch()