shivrajkarewar commited on
Commit
294266d
·
verified ·
1 Parent(s): 7d5d94f

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +340 -0
app.py ADDED
@@ -0,0 +1,340 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import gradio as gr
4
+ import groq
5
+ import numpy as np
6
+ import matplotlib.pyplot as plt
7
+ import py3Dmol
8
+ from pymatgen.core.structure import Structure
9
+ from pymatgen.io.cif import CifWriter
10
+ from pymatgen.io.xyz import XYZ
11
+ from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
12
+ import requests
13
+ from dotenv import load_dotenv
14
+ import tempfile
15
+ import base64
16
+ from IPython.display import HTML
17
+
18
+ # Load environment variables
19
+ load_dotenv()
20
+
21
+ # Retrieve the API key from the environment variable
22
+ groq_api_key = os.getenv("GROQ_API_KEY")
23
+ if not groq_api_key:
24
+ raise ValueError("GROQ_API_KEY environment variable not set")
25
+
26
+ # Initialize Groq client
27
+ client = groq.Groq(api_key=groq_api_key)
28
+
29
+ # Function to generate crystal structures
30
+ def generate_crystal_structure(material_name):
31
+ """Generate a crystal structure for a given material."""
32
+ # This is a simplified version - in a real application, you would use a database or API
33
+ # to get real crystal structure data for materials
34
+
35
+ # Dictionary of common materials and their crystal structures
36
+ material_structures = {
37
+ "Silicon": {
38
+ "spacegroup": "Fd-3m",
39
+ "lattice": [[0, 5.431/2, 5.431/2], [5.431/2, 0, 5.431/2], [5.431/2, 5.431/2, 0]],
40
+ "species": ["Si"],
41
+ "coords": [[0, 0, 0]]
42
+ },
43
+ "Titanium Dioxide": {
44
+ "spacegroup": "P42/mnm",
45
+ "lattice": [[4.594, 0, 0], [0, 4.594, 0], [0, 0, 2.959]],
46
+ "species": ["Ti", "O", "O"],
47
+ "coords": [[0, 0, 0], [0.3053, 0.3053, 0], [0.3053, 0.6947, 0.5]]
48
+ },
49
+ "Graphene": {
50
+ "spacegroup": "P6/mmm",
51
+ "lattice": [[2.46, 0, 0], [-2.46/2, 2.46*np.sqrt(3)/2, 0], [0, 0, 15]],
52
+ "species": ["C", "C"],
53
+ "coords": [[0, 0, 0], [1/3, 2/3, 0]]
54
+ },
55
+ "Copper": {
56
+ "spacegroup": "Fm-3m",
57
+ "lattice": [[3.615, 0, 0], [0, 3.615, 0], [0, 0, 3.615]],
58
+ "species": ["Cu"],
59
+ "coords": [[0, 0, 0]]
60
+ },
61
+ "Aluminum": {
62
+ "spacegroup": "Fm-3m",
63
+ "lattice": [[4.05, 0, 0], [0, 4.05, 0], [0, 0, 4.05]],
64
+ "species": ["Al"],
65
+ "coords": [[0, 0, 0]]
66
+ },
67
+ "Gold": {
68
+ "spacegroup": "Fm-3m",
69
+ "lattice": [[4.078, 0, 0], [0, 4.078, 0], [0, 0, 4.078]],
70
+ "species": ["Au"],
71
+ "coords": [[0, 0, 0]]
72
+ },
73
+ "Diamond": {
74
+ "spacegroup": "Fd-3m",
75
+ "lattice": [[0, 3.567/2, 3.567/2], [3.567/2, 0, 3.567/2], [3.567/2, 3.567/2, 0]],
76
+ "species": ["C"],
77
+ "coords": [[0, 0, 0]]
78
+ },
79
+ "Graphite": {
80
+ "spacegroup": "P63/mmc",
81
+ "lattice": [[2.46, 0, 0], [-1.23, 2.13, 0], [0, 0, 6.71]],
82
+ "species": ["C", "C", "C", "C"],
83
+ "coords": [[0, 0, 0], [0, 0, 0.5], [1/3, 2/3, 0], [2/3, 1/3, 0.5]]
84
+ }
85
+ }
86
+
87
+ # Try to match the material name with our database (case insensitive)
88
+ for known_material, structure_data in material_structures.items():
89
+ if material_name.lower() in known_material.lower():
90
+ structure = Structure.from_spacegroup(
91
+ structure_data["spacegroup"],
92
+ lattice=structure_data["lattice"],
93
+ species=structure_data["species"],
94
+ coords=structure_data["coords"]
95
+ )
96
+ return structure
97
+
98
+ # If material not found, create a generic structure
99
+ return Structure.from_spacegroup(
100
+ "Pm-3m",
101
+ lattice=[[4.0, 0, 0], [0, 4.0, 0], [0, 0, 4.0]],
102
+ species=["X"],
103
+ coords=[[0, 0, 0]]
104
+ )
105
+
106
+ # Function to get material recommendations from LLM
107
+ def get_material_recommendations(query):
108
+ """Get material recommendations from the LLM based on user query."""
109
+ system_prompt = """You are a materials science expert. Your task is to recommend the 3 best materials for a specific application or with certain properties based on the user's query.
110
+
111
+ For each material, provide:
112
+ 1. Material name
113
+ 2. Chemical formula
114
+ 3. Key properties relevant to the application
115
+ 4. Why it's suitable for the application
116
+ 5. Any limitations or considerations
117
+
118
+ Format your response as a JSON object with the following structure:
119
+ {
120
+ "materials": [
121
+ {
122
+ "name": "Material Name",
123
+ "formula": "Chemical Formula",
124
+ "properties": "Key properties relevant to the application",
125
+ "suitability": "Why it's suitable for the application",
126
+ "limitations": "Any limitations or considerations"
127
+ },
128
+ // Second material
129
+ // Third material
130
+ ]
131
+ }
132
+
133
+ Ensure your response is strictly in this JSON format with no additional text."""
134
+
135
+ try:
136
+ completion = client.chat.completions.create(
137
+ model="deepseek-r1",
138
+ messages=[
139
+ {"role": "system", "content": system_prompt},
140
+ {"role": "user", "content": query}
141
+ ],
142
+ temperature=0.2,
143
+ max_tokens=1000
144
+ )
145
+
146
+ response_text = completion.choices[0].message.content
147
+
148
+ # Extract JSON from the response
149
+ try:
150
+ # Try to parse the entire response as JSON
151
+ recommendations = json.loads(response_text)
152
+ except json.JSONDecodeError:
153
+ # If that fails, try to extract JSON using string manipulation
154
+ json_start = response_text.find('{')
155
+ json_end = response_text.rfind('}') + 1
156
+ if json_start >= 0 and json_end > json_start:
157
+ json_str = response_text[json_start:json_end]
158
+ recommendations = json.loads(json_str)
159
+ else:
160
+ raise ValueError("Could not extract valid JSON from LLM response")
161
+
162
+ return recommendations
163
+ except Exception as e:
164
+ return {"error": str(e)}
165
+
166
+ # Function to get crystal structure information
167
+ def get_crystal_structure_info(material_name):
168
+ """Get crystal structure information for a given material."""
169
+ try:
170
+ # Generate the crystal structure
171
+ structure = generate_crystal_structure(material_name)
172
+
173
+ # Create a CIF file
174
+ cif_writer = CifWriter(structure)
175
+ with tempfile.NamedTemporaryFile(suffix='.cif', delete=False) as temp_cif:
176
+ cif_writer.write_file(temp_cif.name)
177
+ cif_path = temp_cif.name
178
+
179
+ with open(cif_path, 'r') as f:
180
+ cif_content = f.read()
181
+
182
+ # Create an XYZ file
183
+ with tempfile.NamedTemporaryFile(suffix='.xyz', delete=False) as temp_xyz:
184
+ xyz_path = temp_xyz.name
185
+
186
+ # Convert structure to XYZ format
187
+ ase_atoms = structure.to_ase()
188
+ from ase.io import write as ase_write
189
+ ase_write(xyz_path, ase_atoms, format='xyz')
190
+
191
+ with open(xyz_path, 'r') as f:
192
+ xyz_content = f.read()
193
+
194
+ # Get space group information
195
+ analyzer = SpacegroupAnalyzer(structure)
196
+ spacegroup = analyzer.get_space_group_symbol()
197
+
198
+ # Generate 3D visualization
199
+ view = py3Dmol.view(width=500, height=400)
200
+ view.addModel(xyz_content, 'xyz')
201
+ view.setStyle({'sphere': {'colorscheme': 'Jmol', 'scale': 0.3},
202
+ 'stick': {'radius': 0.2}})
203
+ view.zoomTo()
204
+ view.spin(True)
205
+ view.setBackgroundColor('white')
206
+ view.render()
207
+
208
+ # Convert the view to HTML
209
+ html_str = view._make_html()
210
+
211
+ # Clean up temporary files
212
+ os.unlink(cif_path)
213
+ os.unlink(xyz_path)
214
+
215
+ return {
216
+ "material_name": material_name,
217
+ "formula": structure.composition.reduced_formula,
218
+ "space_group": spacegroup,
219
+ "num_atoms": len(structure),
220
+ "lattice_parameters": {
221
+ "a": structure.lattice.a,
222
+ "b": structure.lattice.b,
223
+ "c": structure.lattice.c,
224
+ "alpha": structure.lattice.alpha,
225
+ "beta": structure.lattice.beta,
226
+ "gamma": structure.lattice.gamma
227
+ },
228
+ "cif_content": cif_content,
229
+ "xyz_content": xyz_content,
230
+ "visualization_html": html_str
231
+ }
232
+ except Exception as e:
233
+ return {"error": str(e)}
234
+
235
+ # Function to create a downloadable file
236
+ def create_downloadable_file(content, filename):
237
+ """Create a downloadable file with the given content."""
238
+ with open(filename, 'w') as f:
239
+ f.write(content)
240
+ return filename
241
+
242
+ # Gradio interface
243
+ def process_query(query):
244
+ """Process the user query and return material recommendations and crystal structure visualization."""
245
+ try:
246
+ # Get material recommendations from LLM
247
+ recommendations = get_material_recommendations(query)
248
+
249
+ if "error" in recommendations:
250
+ return f"Error getting recommendations: {recommendations['error']}", None, None, None
251
+
252
+ # Format the recommendations as text
253
+ recommendation_text = "# Material Recommendations\n\n"
254
+
255
+ for i, material in enumerate(recommendations.get("materials", [])):
256
+ recommendation_text += f"## {i+1}. {material.get('name', 'Unknown')}\n"
257
+ recommendation_text += f"**Formula:** {material.get('formula', 'N/A')}\n\n"
258
+ recommendation_text += f"**Properties:** {material.get('properties', 'N/A')}\n\n"
259
+ recommendation_text += f"**Suitability:** {material.get('suitability', 'N/A')}\n\n"
260
+ recommendation_text += f"**Limitations:** {material.get('limitations', 'N/A')}\n\n"
261
+
262
+ # Get crystal structure information for the first recommended material
263
+ cif_file = None
264
+ xyz_file = None
265
+
266
+ if recommendations.get("materials") and len(recommendations.get("materials")) > 0:
267
+ first_material = recommendations["materials"][0]["name"]
268
+ structure_info = get_crystal_structure_info(first_material)
269
+
270
+ if "error" in structure_info:
271
+ structure_html = f"<p>Error getting crystal structure: {structure_info['error']}</p>"
272
+ else:
273
+ # Add crystal structure information to the recommendation text
274
+ recommendation_text += f"# Crystal Structure of {structure_info['material_name']}\n\n"
275
+ recommendation_text += f"**Formula:** {structure_info['formula']}\n\n"
276
+ recommendation_text += f"**Space Group:** {structure_info['space_group']}\n\n"
277
+ recommendation_text += f"**Number of Atoms:** {structure_info['num_atoms']}\n\n"
278
+ recommendation_text += "**Lattice Parameters:**\n"
279
+ recommendation_text += f"a = {structure_info['lattice_parameters']['a']:.4f} Å, "
280
+ recommendation_text += f"b = {structure_info['lattice_parameters']['b']:.4f} Å, "
281
+ recommendation_text += f"c = {structure_info['lattice_parameters']['c']:.4f} Å\n"
282
+ recommendation_text += f"α = {structure_info['lattice_parameters']['alpha']:.2f}°, "
283
+ recommendation_text += f"β = {structure_info['lattice_parameters']['beta']:.2f}°, "
284
+ recommendation_text += f"γ = {structure_info['lattice_parameters']['gamma']:.2f}°\n\n"
285
+
286
+ # Create HTML for the 3D visualization
287
+ structure_html = structure_info['visualization_html']
288
+
289
+ # Create downloadable files
290
+ cif_file = create_downloadable_file(structure_info['cif_content'], f"{structure_info['material_name'].replace(' ', '_')}.cif")
291
+ xyz_file = create_downloadable_file(structure_info['xyz_content'], f"{structure_info['material_name'].replace(' ', '_')}.xyz")
292
+ else:
293
+ structure_html = "<p>No materials recommended to visualize.</p>"
294
+
295
+ return recommendation_text, structure_html, cif_file, xyz_file
296
+ except Exception as e:
297
+ return f"Error processing query: {str(e)}", None, None, None
298
+
299
+ # Create the Gradio interface
300
+ with gr.Blocks(title="Material Science Expert") as demo:
301
+ gr.Markdown("# Material Science Expert")
302
+ gr.Markdown("Ask for a material for a specific application or with certain properties.")
303
+
304
+ with gr.Row():
305
+ with gr.Column():
306
+ query_input = gr.Textbox(
307
+ label="Your Query",
308
+ placeholder="I need a material with high thermal conductivity for electronics cooling.",
309
+ lines=3
310
+ )
311
+ submit_btn = gr.Button("Get Recommendations")
312
+
313
+ with gr.Row():
314
+ with gr.Column(scale=1):
315
+ recommendations_output = gr.Markdown(label="Material Recommendations")
316
+ with gr.Column(scale=1):
317
+ structure_output = gr.HTML(label="Crystal Structure Visualization")
318
+
319
+ with gr.Row():
320
+ with gr.Column():
321
+ cif_file_output = gr.File(label="Download CIF File")
322
+ xyz_file_output = gr.File(label="Download XYZ File")
323
+
324
+ submit_btn.click(
325
+ fn=process_query,
326
+ inputs=[query_input],
327
+ outputs=[recommendations_output, structure_output, cif_file_output, xyz_file_output]
328
+ )
329
+
330
+ gr.Markdown("""
331
+ ## Example Queries:
332
+ - I need a material with high thermal conductivity for electronics cooling.
333
+ - What are the best materials for solar cell applications?
334
+ - Recommend materials with high strength-to-weight ratio for aerospace applications.
335
+ - I need a transparent conductive material for touchscreens.
336
+ """)
337
+
338
+ # Launch the app
339
+ if __name__ == "__main__":
340
+ demo.launch()