Spaces:
Running
Running
Interface
Browse files
app.py
CHANGED
@@ -8,7 +8,8 @@ import numpy as np
|
|
8 |
import periodictable
|
9 |
from crystal_toolkit.settings import SETTINGS
|
10 |
from dash import dcc, html
|
11 |
-
from dash.dependencies import Input, Output
|
|
|
12 |
from datasets import load_dataset
|
13 |
from pymatgen.analysis.structure_analyzer import SpacegroupAnalyzer
|
14 |
from pymatgen.core import Structure
|
@@ -18,7 +19,7 @@ top_k = 500
|
|
18 |
|
19 |
# Load only the train split of the dataset
|
20 |
dataset = load_dataset(
|
21 |
-
"LeMaterial/
|
22 |
token=HF_TOKEN,
|
23 |
split="train",
|
24 |
columns=[
|
@@ -42,7 +43,7 @@ dataset = load_dataset(
|
|
42 |
"chemical_formula_descriptive",
|
43 |
"total_magnetization",
|
44 |
],
|
45 |
-
)
|
46 |
|
47 |
display_columns = [
|
48 |
"chemical_formula_descriptive",
|
@@ -97,6 +98,11 @@ server = app.server # Expose the server for deployment
|
|
97 |
# Define the app layout
|
98 |
layout = html.Div(
|
99 |
[
|
|
|
|
|
|
|
|
|
|
|
100 |
html.H1(
|
101 |
html.B("Interactive Crystal Viewer"),
|
102 |
style={"textAlign": "center", "margin-top": "20px"},
|
@@ -104,24 +110,56 @@ layout = html.Div(
|
|
104 |
html.Div(
|
105 |
[
|
106 |
html.Div(
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
id="structure-container",
|
108 |
style={
|
109 |
-
"width": "
|
110 |
-
"display": "inline-block",
|
111 |
"verticalAlign": "top",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
},
|
113 |
),
|
114 |
html.Div(
|
115 |
id="properties-container",
|
116 |
style={
|
117 |
-
"width": "
|
118 |
-
"display": "inline-block",
|
119 |
"paddingLeft": "4%",
|
120 |
"verticalAlign": "top",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
122 |
),
|
123 |
],
|
124 |
-
style={
|
|
|
|
|
|
|
|
|
|
|
125 |
),
|
126 |
html.Div(
|
127 |
[
|
@@ -143,8 +181,9 @@ layout = html.Div(
|
|
143 |
id="materials-input",
|
144 |
),
|
145 |
],
|
|
|
146 |
style={
|
147 |
-
"width": "
|
148 |
},
|
149 |
),
|
150 |
],
|
@@ -156,48 +195,90 @@ layout = html.Div(
|
|
156 |
),
|
157 |
],
|
158 |
style={
|
159 |
-
"width": "
|
160 |
"verticalAlign": "top",
|
161 |
},
|
162 |
),
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
188 |
],
|
189 |
-
|
190 |
-
|
191 |
-
"
|
192 |
-
"
|
193 |
-
"
|
|
|
|
|
|
|
194 |
},
|
195 |
-
style_header={"fontWeight": "bold", "backgroundColor": "lightgrey"},
|
196 |
-
style_cell={"textAlign": "center"},
|
197 |
-
style_as_list_view=True,
|
198 |
),
|
199 |
],
|
200 |
-
style={
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
),
|
202 |
# html.Button("Display Material", id="display-button", n_clicks=0),
|
203 |
],
|
@@ -259,12 +340,25 @@ def on_submit_materials_input(n_clicks, query):
|
|
259 |
],
|
260 |
# Input("display-button", "n_clicks"),
|
261 |
Input("table", "active_cell"),
|
|
|
262 |
)
|
263 |
-
def display_material(active_cell):
|
264 |
-
if not active_cell:
|
265 |
-
return
|
266 |
-
|
267 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
268 |
row = dataset[mapping_table_idx_dataset_idx[idx_active]]
|
269 |
|
270 |
structure = Structure(
|
@@ -291,7 +385,7 @@ def display_material(active_cell):
|
|
291 |
"Crystal system": sga.get_crystal_system(),
|
292 |
"International Spacegroup": sga.get_symmetry_dataset().international,
|
293 |
"Magnetic moments (μB/f.u.)": row["magnetic_moments"],
|
294 |
-
"Stress tensor (kB)": row["stress_tensor"],
|
295 |
"Forces on atoms (eV/A)": row["forces"],
|
296 |
"Bader charges (e-)": row["charges"],
|
297 |
"DFT Functional": row["functional"],
|
@@ -302,21 +396,71 @@ def display_material(active_cell):
|
|
302 |
[
|
303 |
html.Tbody(
|
304 |
[
|
305 |
-
html.Tr(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
306 |
for key, value in properties.items()
|
307 |
-
]
|
308 |
)
|
309 |
],
|
310 |
style={
|
311 |
-
"border": "1px solid black",
|
312 |
"width": "100%",
|
313 |
"borderCollapse": "collapse",
|
|
|
|
|
|
|
314 |
},
|
315 |
)
|
316 |
|
317 |
return structure_component.layout(), properties_html
|
318 |
|
319 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
320 |
# Register crystal toolkit with the app
|
321 |
ctc.register_crystal_toolkit(app, layout)
|
322 |
|
|
|
8 |
import periodictable
|
9 |
from crystal_toolkit.settings import SETTINGS
|
10 |
from dash import dcc, html
|
11 |
+
from dash.dependencies import Input, Output, State
|
12 |
+
from dash_breakpoints import WindowBreakpoints
|
13 |
from datasets import load_dataset
|
14 |
from pymatgen.analysis.structure_analyzer import SpacegroupAnalyzer
|
15 |
from pymatgen.core import Structure
|
|
|
19 |
|
20 |
# Load only the train split of the dataset
|
21 |
dataset = load_dataset(
|
22 |
+
"LeMaterial/leMat1",
|
23 |
token=HF_TOKEN,
|
24 |
split="train",
|
25 |
columns=[
|
|
|
43 |
"chemical_formula_descriptive",
|
44 |
"total_magnetization",
|
45 |
],
|
46 |
+
).select(range(1000))
|
47 |
|
48 |
display_columns = [
|
49 |
"chemical_formula_descriptive",
|
|
|
98 |
# Define the app layout
|
99 |
layout = html.Div(
|
100 |
[
|
101 |
+
WindowBreakpoints(
|
102 |
+
id="breakpoints",
|
103 |
+
widthBreakpointThresholdsPx=[800, 1200],
|
104 |
+
widthBreakpointNames=["sm", "md", "lg"],
|
105 |
+
),
|
106 |
html.H1(
|
107 |
html.B("Interactive Crystal Viewer"),
|
108 |
style={"textAlign": "center", "margin-top": "20px"},
|
|
|
110 |
html.Div(
|
111 |
[
|
112 |
html.Div(
|
113 |
+
[
|
114 |
+
html.Div(
|
115 |
+
"Search a material to display its structure and properties",
|
116 |
+
style={"textAlign": "center"},
|
117 |
+
),
|
118 |
+
],
|
119 |
id="structure-container",
|
120 |
style={
|
121 |
+
"width": "44%",
|
|
|
122 |
"verticalAlign": "top",
|
123 |
+
"boxShadow": "0px 4px 8px rgba(0, 0, 0, 0.1)",
|
124 |
+
"borderRadius": "10px",
|
125 |
+
"backgroundColor": "#f9f9f9",
|
126 |
+
"padding": "20px",
|
127 |
+
"textAlign": "center",
|
128 |
+
"display": "flex",
|
129 |
+
"justifyContent": "center",
|
130 |
+
"alignItems": "center",
|
131 |
},
|
132 |
),
|
133 |
html.Div(
|
134 |
id="properties-container",
|
135 |
style={
|
136 |
+
"width": "55%",
|
|
|
137 |
"paddingLeft": "4%",
|
138 |
"verticalAlign": "top",
|
139 |
+
"boxShadow": "0px 4px 8px rgba(0, 0, 0, 0.1)",
|
140 |
+
"borderRadius": "10px",
|
141 |
+
"backgroundColor": "#f9f9f9",
|
142 |
+
"padding": "20px",
|
143 |
+
"overflow": "auto",
|
144 |
+
"maxHeight": "600px",
|
145 |
+
"display": "flex",
|
146 |
+
"justifyContent": "center",
|
147 |
+
"wordWrap": "break-word",
|
148 |
},
|
149 |
+
children=[
|
150 |
+
html.Div(
|
151 |
+
"Properties will be displayed here",
|
152 |
+
style={"textAlign": "center"},
|
153 |
+
),
|
154 |
+
],
|
155 |
),
|
156 |
],
|
157 |
+
style={
|
158 |
+
"marginTop": "20px",
|
159 |
+
"display": "flex",
|
160 |
+
"justifyContent": "space-between", # Ensure the two sections are responsive
|
161 |
+
"flexWrap": "wrap",
|
162 |
+
},
|
163 |
),
|
164 |
html.Div(
|
165 |
[
|
|
|
181 |
id="materials-input",
|
182 |
),
|
183 |
],
|
184 |
+
id="materials-input-container",
|
185 |
style={
|
186 |
+
"width": "100%",
|
187 |
},
|
188 |
),
|
189 |
],
|
|
|
195 |
),
|
196 |
],
|
197 |
style={
|
198 |
+
"width": "48%",
|
199 |
"verticalAlign": "top",
|
200 |
},
|
201 |
),
|
202 |
+
html.Div(
|
203 |
+
[
|
204 |
+
html.Label(
|
205 |
+
"Select a row to display the material's structure and properties",
|
206 |
+
style={"margin-bottom": "20px"},
|
207 |
+
),
|
208 |
+
# dcc.Dropdown(
|
209 |
+
# id="material-dropdown",
|
210 |
+
# options=[], # Empty options initially
|
211 |
+
# value=None,
|
212 |
+
# ),
|
213 |
+
dash.dash_table.DataTable(
|
214 |
+
id="table",
|
215 |
+
columns=[
|
216 |
+
(
|
217 |
+
{"name": display_names[col], "id": col}
|
218 |
+
if col != "energy"
|
219 |
+
else {
|
220 |
+
"name": display_names[col],
|
221 |
+
"id": col,
|
222 |
+
"type": "numeric",
|
223 |
+
"format": {"specifier": ".2f"},
|
224 |
+
}
|
225 |
+
)
|
226 |
+
for col in display_columns
|
227 |
+
],
|
228 |
+
data=[{}],
|
229 |
+
style_cell={
|
230 |
+
"fontFamily": "Arial",
|
231 |
+
"padding": "10px",
|
232 |
+
"border": "1px solid #ddd", # Subtle border for elegance
|
233 |
+
"textAlign": "left",
|
234 |
+
"fontSize": "14px",
|
235 |
+
},
|
236 |
+
style_header={
|
237 |
+
"backgroundColor": "#f5f5f5", # Light grey header
|
238 |
+
"fontWeight": "bold",
|
239 |
+
"textAlign": "left",
|
240 |
+
"borderBottom": "2px solid #ddd",
|
241 |
+
},
|
242 |
+
style_data={
|
243 |
+
"backgroundColor": "#ffffff",
|
244 |
+
"color": "#333333",
|
245 |
+
"borderBottom": "1px solid #ddd",
|
246 |
+
},
|
247 |
+
style_data_conditional=[
|
248 |
+
{
|
249 |
+
"if": {"state": "active"},
|
250 |
+
"backgroundColor": "#e6f7ff",
|
251 |
+
"border": "1px solid #1890ff",
|
252 |
+
},
|
253 |
+
],
|
254 |
+
style_table={
|
255 |
+
"maxHeight": "400px",
|
256 |
+
"overflowX": "auto",
|
257 |
+
"overflowY": "auto",
|
258 |
+
},
|
259 |
+
style_as_list_view=True,
|
260 |
+
row_selectable="single",
|
261 |
+
selected_rows=[],
|
262 |
+
),
|
263 |
],
|
264 |
+
style={
|
265 |
+
"width": "48%",
|
266 |
+
# "maxWidth": "800px",
|
267 |
+
"margin": "0 auto",
|
268 |
+
"padding": "20px",
|
269 |
+
"backgroundColor": "#ffffff",
|
270 |
+
"borderRadius": "10px",
|
271 |
+
"boxShadow": "0px 4px 8px rgba(0, 0, 0, 0.1)",
|
272 |
},
|
|
|
|
|
|
|
273 |
),
|
274 |
],
|
275 |
+
style={
|
276 |
+
"margin-top": "20px",
|
277 |
+
"margin-bottom": "20px",
|
278 |
+
"display": "flex",
|
279 |
+
"flexDirection": "row",
|
280 |
+
"alignItems": "center",
|
281 |
+
},
|
282 |
),
|
283 |
# html.Button("Display Material", id="display-button", n_clicks=0),
|
284 |
],
|
|
|
340 |
],
|
341 |
# Input("display-button", "n_clicks"),
|
342 |
Input("table", "active_cell"),
|
343 |
+
Input("table", "derived_virtual_selected_rows"),
|
344 |
)
|
345 |
+
def display_material(active_cell, selected_rows):
|
346 |
+
if not active_cell and not selected_rows:
|
347 |
+
return (
|
348 |
+
html.Div(
|
349 |
+
"Search a material to display its structure and properties",
|
350 |
+
style={"textAlign": "center"},
|
351 |
+
),
|
352 |
+
html.Div(
|
353 |
+
"Properties will be displayed here",
|
354 |
+
style={"textAlign": "center"},
|
355 |
+
),
|
356 |
+
)
|
357 |
+
|
358 |
+
if len(selected_rows) > 0:
|
359 |
+
idx_active = selected_rows[0]
|
360 |
+
else:
|
361 |
+
idx_active = active_cell["row"]
|
362 |
row = dataset[mapping_table_idx_dataset_idx[idx_active]]
|
363 |
|
364 |
structure = Structure(
|
|
|
385 |
"Crystal system": sga.get_crystal_system(),
|
386 |
"International Spacegroup": sga.get_symmetry_dataset().international,
|
387 |
"Magnetic moments (μB/f.u.)": row["magnetic_moments"],
|
388 |
+
# "Stress tensor (kB)": row["stress_tensor"], # not available in LeMat1
|
389 |
"Forces on atoms (eV/A)": row["forces"],
|
390 |
"Bader charges (e-)": row["charges"],
|
391 |
"DFT Functional": row["functional"],
|
|
|
396 |
[
|
397 |
html.Tbody(
|
398 |
[
|
399 |
+
html.Tr(
|
400 |
+
[
|
401 |
+
html.Th(
|
402 |
+
key,
|
403 |
+
style={
|
404 |
+
"padding": "10px",
|
405 |
+
"verticalAlign": "middle",
|
406 |
+
},
|
407 |
+
),
|
408 |
+
html.Td(
|
409 |
+
str(value),
|
410 |
+
style={
|
411 |
+
"padding": "10px",
|
412 |
+
"borderBottom": "1px solid #ddd",
|
413 |
+
},
|
414 |
+
),
|
415 |
+
],
|
416 |
+
)
|
417 |
for key, value in properties.items()
|
418 |
+
],
|
419 |
)
|
420 |
],
|
421 |
style={
|
|
|
422 |
"width": "100%",
|
423 |
"borderCollapse": "collapse",
|
424 |
+
"fontFamily": "'Arial', sans-serif",
|
425 |
+
"fontSize": "14px",
|
426 |
+
"color": "#333333",
|
427 |
},
|
428 |
)
|
429 |
|
430 |
return structure_component.layout(), properties_html
|
431 |
|
432 |
|
433 |
+
@app.callback(
|
434 |
+
Output("materials-input-container", "children"),
|
435 |
+
Input("breakpoints", "widthBreakpoint"),
|
436 |
+
State("breakpoints", "width"),
|
437 |
+
)
|
438 |
+
def update_materials_input_layout(breakpoint_name, width):
|
439 |
+
if breakpoint_name in ["lg", "md"]:
|
440 |
+
# Default layout if no page size is detected
|
441 |
+
return dmp.MaterialsInput(
|
442 |
+
allowedInputTypes=["elements", "formula"],
|
443 |
+
hidePeriodicTable=False,
|
444 |
+
periodicTableMode="toggle",
|
445 |
+
hideWildcardButton=True,
|
446 |
+
showSubmitButton=True,
|
447 |
+
submitButtonText="Search",
|
448 |
+
type="elements",
|
449 |
+
id="materials-input",
|
450 |
+
)
|
451 |
+
elif breakpoint_name == "sm":
|
452 |
+
return dmp.MaterialsInput(
|
453 |
+
allowedInputTypes=["elements", "formula"],
|
454 |
+
hidePeriodicTable=True,
|
455 |
+
periodicTableMode="none",
|
456 |
+
hideWildcardButton=False,
|
457 |
+
showSubmitButton=False,
|
458 |
+
# submitButtonText="Search",
|
459 |
+
type="elements",
|
460 |
+
id="materials-input",
|
461 |
+
)
|
462 |
+
|
463 |
+
|
464 |
# Register crystal toolkit with the app
|
465 |
ctc.register_crystal_toolkit(app, layout)
|
466 |
|