Ramlaoui commited on
Commit
32ed837
1 Parent(s): 59575d2
Files changed (1) hide show
  1. app.py +197 -53
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/leDataset",
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": "48%",
110
- "display": "inline-block",
111
  "verticalAlign": "top",
 
 
 
 
 
 
 
 
112
  },
113
  ),
114
  html.Div(
115
  id="properties-container",
116
  style={
117
- "width": "48%",
118
- "display": "inline-block",
119
  "paddingLeft": "4%",
120
  "verticalAlign": "top",
 
 
 
 
 
 
 
 
 
121
  },
 
 
 
 
 
 
122
  ),
123
  ],
124
- style={"margin-top": "20px"},
 
 
 
 
 
125
  ),
126
  html.Div(
127
  [
@@ -143,8 +181,9 @@ layout = html.Div(
143
  id="materials-input",
144
  ),
145
  ],
 
146
  style={
147
- "width": "48%",
148
  },
149
  ),
150
  ],
@@ -156,48 +195,90 @@ layout = html.Div(
156
  ),
157
  ],
158
  style={
159
- "width": "100%",
160
  "verticalAlign": "top",
161
  },
162
  ),
163
- ],
164
- style={"margin-top": "20px", "margin-bottom": "20px"},
165
- ),
166
- html.Div(
167
- [
168
- html.Label("Select Material to Display"),
169
- # dcc.Dropdown(
170
- # id="material-dropdown",
171
- # options=[], # Empty options initially
172
- # value=None,
173
- # ),
174
- dash.dash_table.DataTable(
175
- id="table",
176
- columns=[
177
- (
178
- {"name": display_names[col], "id": col}
179
- if col != "energy"
180
- else {
181
- "name": display_names[col],
182
- "id": col,
183
- "type": "numeric",
184
- "format": {"specifier": ".2f"},
185
- }
186
- )
187
- for col in display_columns
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  ],
189
- data=[{}],
190
- style_table={
191
- "overflowX": "auto",
192
- "height": "220px",
193
- "overflowY": "auto",
 
 
 
194
  },
195
- style_header={"fontWeight": "bold", "backgroundColor": "lightgrey"},
196
- style_cell={"textAlign": "center"},
197
- style_as_list_view=True,
198
  ),
199
  ],
200
- style={"margin-top": "30px"},
 
 
 
 
 
 
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
- idx_active = active_cell["row"]
 
 
 
 
 
 
 
 
 
 
 
 
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([html.Th(key), html.Td(str(value))])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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