Bushra346 commited on
Commit
6bf0f73
·
verified ·
1 Parent(s): 6e62756

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +224 -0
app.py ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ import gradio as gr
3
+ import pandas as pd
4
+ import numpy as np
5
+ import matplotlib.pyplot as plt
6
+ import os
7
+ import base64
8
+ import requests # Assuming requests is used in query_gpt5
9
+
10
+ # Ensure matplotlib uses an inline backend (though not strictly needed for saving files)
11
+ # %matplotlib inline # This is a Colab magic command, not standard Python. Remove for app.py
12
+
13
+ # Assuming your API key is set as an environment variable in Hugging Face Spaces secrets
14
+ API_KEY = os.getenv("AIML_API_KEY")
15
+ API_URL = "https://api.aimlapi.com/v1/chat/completions"
16
+
17
+ def query_gpt5(user_input):
18
+ if not API_KEY:
19
+ return "API Key not set. Please set AIML_API_KEY environment variable."
20
+ headers = {"Authorization": f"Bearer {API_KEY}"}
21
+ data = {
22
+ "model": "openai/gpt-4o",
23
+ "messages": [
24
+ {"role": "system", "content": "You are an expert in battery materials and sustainable energy."},
25
+ {"role": "user", "content": user_input}
26
+ ]
27
+ }
28
+ try:
29
+ response = requests.post(API_URL, headers=headers, json=data)
30
+ response.raise_for_status() # Raise an HTTPError for bad responses (4xx or 5xx)
31
+ resp_json = response.json()
32
+ if "choices" in resp_json:
33
+ return resp_json["choices"][0]["message"]["content"]
34
+ elif "error" in resp_json:
35
+ return f"Error from API: {resp_json['error']}"
36
+ else:
37
+ return f"Unexpected API response structure: {resp_json}"
38
+ except requests.exceptions.RequestException as e:
39
+ return f"API request failed: {e}"
40
+ except Exception as e:
41
+ return f"An error occurred during API query: {e}"
42
+
43
+
44
+ # Define the analyze_eis function (modified to return file paths)
45
+ def analyze_eis_and_get_text(eis_file):
46
+ if eis_file is None:
47
+ return "Please upload an EIS data file.", None, None
48
+
49
+ try:
50
+ # Load the uploaded file into a pandas DataFrame
51
+ # For deployment, Gradio provides a file-like object, access path via .name
52
+ df_eis = pd.read_csv(eis_file.name)
53
+
54
+ # Ensure required columns exist
55
+ if 'Frequency (Hz)' not in df_eis.columns or 'Z_real (ohm)' not in df_eis.columns or 'Z_imag (ohm)' not in df_eis.columns:
56
+ return "Uploaded file must contain 'Frequency (Hz)', 'Z_real (ohm)', and 'Z_imag (ohm)' columns.", None, None
57
+
58
+ # Calculate magnitude and phase
59
+ df_eis['Impedance Magnitude (ohm)'] = np.sqrt(df_eis['Z_real (ohm)']**2 + df_eis['Z_imag (ohm)']**2)
60
+ df_eis['Impedance Phase (deg)'] = np.arctan2(df_eis['Z_imag (ohm)'], df_eis['Z_real (ohm)']) * 180 / np.pi
61
+
62
+ # Generate Nyquist Plot
63
+ plt.figure(figsize=(6, 5))
64
+ plt.plot(df_eis['Z_real (ohm)'], -df_eis['Z_imag (ohm)'], marker='o', markersize=4, linestyle='-')
65
+ plt.xlabel('Z_real (ohm)')
66
+ plt.ylabel('-Z_imag (ohm)')
67
+ plt.title('Nyquist Plot')
68
+ plt.gca().set_aspect('equal', adjustable='box')
69
+ plt.grid(True)
70
+ nyquist_plot_path = "nyquist_plot.png"
71
+ plt.savefig(nyquist_plot_path)
72
+ plt.close() # Close the plot figure to free memory
73
+
74
+ # Generate Bode Plots
75
+ plt.figure(figsize=(10, 7))
76
+
77
+ # Magnitude Plot
78
+ plt.subplot(2, 1, 1)
79
+ plt.loglog(df_eis['Frequency (Hz)'], df_eis['Impedance Magnitude (ohm)'], marker='o', markersize=4, linestyle='-')
80
+ plt.xlabel('Frequency (Hz)')
81
+ plt.ylabel('Impedance Magnitude (ohm)')
82
+ plt.title('Bode Plot - Magnitude')
83
+ plt.grid(True, which="both", ls="--")
84
+
85
+ # Phase Plot
86
+ plt.subplot(2, 1, 2)
87
+ plt.semilogx(df_eis['Frequency (Hz)'], df_eis['Impedance Phase (deg)'], marker='o', markersize=4, linestyle='-')
88
+ plt.xlabel('Frequency (Hz)')
89
+ plt.ylabel('Phase (deg)')
90
+ plt.title('Bode Plot - Phase')
91
+ plt.grid(True, which="both", ls="--")
92
+
93
+ plt.tight_layout()
94
+ bode_plots_path = "bode_plots.png"
95
+ plt.savefig(bode_plots_path)
96
+ plt.close() # Close the plot figure
97
+
98
+ # Generate EIS analysis text
99
+ nyquist_description = "Nyquist plot analysis:\n"
100
+ # Handle potential empty dataframe or single point data
101
+ if not df_eis.empty:
102
+ rs_high_freq = df_eis['Z_real (ohm)'].iloc[-1]
103
+ nyquist_description += f"- High-frequency intercept (series resistance): {rs_high_freq:.2f} ohm.\n"
104
+
105
+ # Simple approximation for charge transfer resistance
106
+ if len(df_eis) > 1:
107
+ mid_freq_real = df_eis['Z_real (ohm)'].iloc[len(df_eis)//2]
108
+ rct_approx = mid_freq_real - rs_high_freq
109
+ nyquist_description += f"- Semicircle observed in the mid-frequency range, indicating charge transfer processes. Approximate charge transfer resistance: {rct_approx:.2f} ohm.\n"
110
+ else:
111
+ nyquist_description += "- Not enough data points to approximate semicircle.\n"
112
+
113
+ # Describe low-frequency behavior (Warburg impedance)
114
+ if len(df_eis) > 1:
115
+ low_freq_z = df_eis.iloc[:5] # Look at the first few low frequency points
116
+ if len(low_freq_z) > 1:
117
+ slopes = (low_freq_z['Z_imag (ohm)'].diff() / low_freq_z['Z_real (ohm)'].diff()).dropna()
118
+ if any(abs(slope + 1) < 0.2 for slope in slopes): # Using a tolerance
119
+ nyquist_description += "- Low-frequency tail shows a Warburg impedance behavior, characteristic of diffusion processes.\n"
120
+ else:
121
+ nyquist_description += "- Low-frequency behavior observed.\n"
122
+ else:
123
+ nyquist_description += "- Not enough low-frequency data points to determine Warburg behavior.\n"
124
+ else:
125
+ nyquist_description += "- Not enough data points for low-frequency analysis.\n"
126
+
127
+
128
+ bode_mag_description = "Bode Magnitude plot analysis:\n"
129
+ bode_mag_description += f"- At high frequencies, the impedance magnitude is around {df_eis['Impedance Magnitude (ohm)'].iloc[-1]:.2f} ohm.\n"
130
+ bode_mag_description += f"- At low frequencies, the impedance magnitude increases significantly, reaching {df_eis['Impedance Magnitude (ohm)'].iloc[0]:.2f} ohm, indicating capacitive or diffusion-limited behavior.\n"
131
+ bode_mag_description += "- The magnitude plot shows a decrease with frequency in the mid-range, followed by an increase at low frequencies.\n"
132
+
133
+ bode_phase_description = "Bode Phase plot analysis:\n"
134
+ bode_phase_description += f"- At high frequencies, the phase angle is around {df_eis['Impedance Phase (deg)'].iloc[-1]:.2f} degrees, close to 0.\n"
135
+ if len(df_eis) > 1:
136
+ min_phase_idx = df_eis['Impedance Phase (deg)'].idxmin()
137
+ min_phase_freq = df_eis['Frequency (Hz)'].iloc[min_phase_idx]
138
+ min_phase_value = df_eis['Impedance Phase (deg)'].iloc[min_phase_idx]
139
+ bode_phase_description += f"- A phase minimum of {min_phase_value:.2f} degrees is observed around {min_phase_freq:.4f} Hz, corresponding to charge transfer processes.\n"
140
+ else:
141
+ bode_phase_description += "- Not enough data points to identify phase minimum.\n"
142
+ bode_phase_description += f"- At low frequencies, the phase angle approaches {df_eis['Impedance Phase (deg)'].iloc[0]:.2f} degrees, consistent with capacitive or diffusion behavior.\n"
143
+ else:
144
+ nyquist_description += "- No data available for analysis.\n"
145
+ bode_mag_description = "Bode Magnitude plot analysis:\n- No data available for analysis.\n"
146
+ bode_phase_description = "Bode Phase plot analysis:\n- No data available for analysis.\n"
147
+
148
+
149
+ eis_analysis_text = "Electrochemical Impedance Spectroscopy (EIS) Analysis:\n\n" + \
150
+ nyquist_description + "\n" + \
151
+ bode_mag_description + "\n" + \
152
+ bode_phase_description + "\n" + \
153
+ "Note: Parameters like charge transfer resistance and Warburg impedance are estimated from visual features; precise values would require equivalent circuit fitting."
154
+
155
+ # Return the analysis text and paths to the saved plot files
156
+ return eis_analysis_text, nyquist_plot_path, bode_plots_path
157
+
158
+ except Exception as e:
159
+ # Clean up generated files if an error occurs
160
+ if os.path.exists("nyquist_plot.png"):
161
+ os.remove("nyquist_plot.png")
162
+ if os.path.exists("bode_plots.png"):
163
+ os.remove("bode_plots.png")
164
+ return f"Error processing EIS data: {e}", None, None
165
+
166
+
167
+ # Modify the material_analysis function to accept EIS analysis text
168
+ def material_analysis(query, eis_analysis_text):
169
+ combined_query = query
170
+ if eis_analysis_text and eis_analysis_text != "Please upload an EIS data file.":
171
+ combined_query = query + "\n\nEIS Analysis:\n" + eis_analysis_text
172
+
173
+ answer = query_gpt5(combined_query)
174
+ return answer
175
+
176
+
177
+ # Define the integrated function that first analyzes EIS and then queries the AI
178
+ def process_eis_and_query_ai(eis_file, ai_query):
179
+ # Analyze EIS data - this will save plot files and return analysis text
180
+ eis_analysis_text, nyquist_plot_path, bode_plots_path = analyze_eis_and_get_text(eis_file)
181
+
182
+ # Query AI with the analysis text
183
+ # The AI query should only proceed if there's a valid AI query input
184
+ ai_response_text = ""
185
+ if ai_query:
186
+ ai_response_text = material_analysis(ai_query, eis_analysis_text)
187
+
188
+ # Return all outputs
189
+ # Ensure paths are returned even if AI query is empty
190
+ return eis_analysis_text, nyquist_plot_path, bode_plots_path, ai_response_text
191
+
192
+
193
+ # Define Gradio Interface components
194
+ eis_file_input = gr.File(label="Upload EIS Data (CSV)")
195
+ eis_analysis_output = gr.Textbox(label="EIS Analysis Summary", interactive=False)
196
+ # Use Image component for plot files - Gradio will handle displaying the file from the path
197
+ nyquist_plot_output = gr.Image(label="Nyquist Plot", type="filepath")
198
+ bode_plots_output = gr.Image(label="Bode Plots", type="filepath")
199
+ ai_query_input = gr.Textbox(label="Ask about Battery Materials or Recycling")
200
+ ai_response_output = gr.Textbox(label="AI Research Assistant")
201
+
202
+
203
+ # Create the integrated Gradio interface
204
+ integrated_interface = gr.Interface(
205
+ fn=process_eis_and_query_ai,
206
+ inputs=[
207
+ eis_file_input,
208
+ ai_query_input
209
+ ],
210
+ outputs=[
211
+ eis_analysis_output,
212
+ nyquist_plot_output,
213
+ bode_plots_output,
214
+ ai_response_output
215
+ ],
216
+ title="🔋 VoltAIon - Integrated EIS Analysis and AI Chat",
217
+ description="Upload EIS data (CSV with Frequency, Z_real, Z_imag) and ask the AI about battery materials, informed by the analysis."
218
+ )
219
+
220
+ # Launch the integrated interface
221
+ # For deployment on Hugging Face Spaces, use launch(share=True) or simply launch()
222
+ # Gradio handles the public URL for Spaces automatically.
223
+ if __name__ == "__main__":
224
+ integrated_interface.launch()