import math import gradio import pandas from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline # 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 ) # Create a function to do the Young's Modulus calculation def youngs_modulus_calc(F_N: float, A_m2: float, L0_m: float, delta_L_m: float) -> float: """ Calculates Young's Modulus (E) in GPa. F_N: Force in Newtons A_m2: Area in square meters L0_m: Original length in meters delta_L_m: Change in length in meters """ if A_m2 == 0 or L0_m == 0 or delta_L_m == 0: return math.inf # Avoid division by zero stress_Pa = F_N / A_m2 strain = delta_L_m / L0_m E_Pa = stress_Pa / strain E_GPa = E_Pa / 1e9 return E_GPa # This helper function applies a chat format to help the LLM understand what # is going on def _format_chat(system_prompt: str, user_prompt: str) -> str: messages = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ] template = getattr(tokenizer, "chat_template", None) return tokenizer.apply_chat_template( messages, tokenize=False, add_generation_prompt=True ) # This functoin uses hte LLM to generate a response. def _llm_generate(prompt: str, max_tokens: int) -> str: out = pipe( prompt, max_new_tokens=max_tokens, do_sample=True, temperature=0.5, return_full_text=False, ) return out[0]["generated_text"] # This function generates an explanation of the results def llm_explain(E_GPa: float, inputs: list) -> str: system_prompt = ( "Explain engineering concepts to a smart 5-year-old using food analogies. " "Keep responses concise, only one sentence." ) F_N, A_m2, L0_m, delta_L_m = inputs aluminum_E_GPa = 70.0 comparison = "more flexible" if E_GPa < aluminum_E_GPa else "stiffer" if E_GPa > aluminum_E_GPa else "about as stiff" user_prompt = ( f"An unknown material was tested with a force of {F_N:g} N, area of {A_m2:g} m², original length of {L0_m:g} m, and change in length of {delta_L_m:g} m.\n" f"The unknown material's Young's Modulus is {E_GPa:.2f} GPa.\n" f"Aluminum's Young's Modulus is {aluminum_E_GPa} GPa.\n" f"Explain if this material is {comparison} than aluminum." "Explain the Young's modulus of the unknown material and the compare it to aluminum in ONE friendly sentence for a non-expert." ) formatted = _format_chat(system_prompt, user_prompt) return _llm_generate(formatted, max_tokens=128) # This function ties everythign together (evaluation, LLM explanaation, output) # And will be out main entry point for teh GUI def run_once(F_N, A_m2, L0_m, delta_L_m): # Young's Modulus calculation part youngs_modulus_inputs = [F_N, A_m2, L0_m, delta_L_m] youngs_modulus_result = youngs_modulus_calc( F_N=float(F_N), A_m2=float(A_m2), L0_m=float(L0_m), delta_L_m=float(delta_L_m) ) aluminum_E_GPa = 70.0 stiffness_comparison = "stiff" if youngs_modulus_result > aluminum_E_GPa else "flexible" if youngs_modulus_result < aluminum_E_GPa else "similar stiffness" youngs_modulus_df = pandas.DataFrame([{ "Young's Modulus [GPa]": round(youngs_modulus_result, 3), "Compared to Aluminum": stiffness_comparison }]) youngs_modulus_narrative = llm_explain(youngs_modulus_result, youngs_modulus_inputs).split("\n")[0] return youngs_modulus_df, youngs_modulus_narrative # Last but not least, here's the UI! with gradio.Blocks() as demo: # Let's start by adding a title and introduction gradio.Markdown( "# Run and Explain Young's Modulus Calculation" ) gradio.Markdown( "This app calculates Young's Modulus and provides a natural language description of the result, comparing it to aluminum." ) # This row contains all of the physical parameters for Young's Modulus with gradio.Row(): F_N = gradio.Number(value=1000.0, label="Force [N]") A_m2 = gradio.Number(value=0.01, label="Area [m²]") L0_m = gradio.Number(value=1.0, label="Original Length [m]") delta_L_m = gradio.Number(value=0.001, label="Change in Length [m]") # Add a button to click to run the interface youngs_modulus_run_btn = gradio.Button("Compute Young's Modulus") # These are the outputs for Young's Modulus youngs_modulus_results_df = gradio.Dataframe(label="Numerical results (deterministic)", interactive=False) youngs_modulus_explain_md = gradio.Markdown(label="Explanation") # Run the calculations when the button is clicked youngs_modulus_run_btn.click( fn=run_once, inputs=[F_N, A_m2, L0_m, delta_L_m], outputs=[youngs_modulus_results_df, youngs_modulus_explain_md] ) # Finally, add a few examples gradio.Examples( examples=[ [1000000.0, 0.001, 1.0, 0.01], # Example 1 (should be around 100 GPa - stiffer than aluminum) [50000.0, 0.01, 1.0, 0.0002], # Example 2 (should be around 25 GPa - more flexible than aluminum) [700000.0, 0.01, 1.0, 0.001], # Example 3 (should be around 70 GPa - about as stiff as aluminum) ], inputs=[F_N, A_m2, L0_m, delta_L_m], label="Representative Young's Modulus Cases", examples_per_page=3, cache_examples=False, ) if __name__ == "__main__": demo.launch(debug=False)