Spaces:
Running
Running
Upload 3 files
Browse files- Dockerfile +26 -0
- app.py +125 -0
- requirements.txt +9 -0
Dockerfile
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Use the official Python image from Docker Hub
|
2 |
+
FROM python:3.9-slim
|
3 |
+
|
4 |
+
# Install system dependencies
|
5 |
+
RUN apt-get update && \
|
6 |
+
apt-get install -y --no-install-recommends \
|
7 |
+
cmake \
|
8 |
+
build-essential \
|
9 |
+
libssl-dev \
|
10 |
+
&& rm -rf /var/lib/apt/lists/*
|
11 |
+
|
12 |
+
# Set the working directory
|
13 |
+
WORKDIR /app
|
14 |
+
|
15 |
+
# Copy requirements and install
|
16 |
+
COPY requirements.txt .
|
17 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
18 |
+
|
19 |
+
# Copy the application code
|
20 |
+
COPY app.py .
|
21 |
+
|
22 |
+
# Expose the port Dash will run on
|
23 |
+
EXPOSE 7860
|
24 |
+
|
25 |
+
# Run the app
|
26 |
+
CMD ["python", "app.py"]
|
app.py
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from pymatgen.ext.matproj import MPRester
|
3 |
+
import crystal_toolkit.components as ctc
|
4 |
+
from crystal_toolkit.settings import SETTINGS
|
5 |
+
|
6 |
+
import dash
|
7 |
+
from dash import html, dcc
|
8 |
+
from dash.dependencies import Input, Output, State
|
9 |
+
|
10 |
+
# Set your Materials Project API key
|
11 |
+
MATERIALS_PROJECT_API_KEY = os.getenv('MATERIALS_PROJECT_API_KEY')
|
12 |
+
|
13 |
+
# Initialize the Dash app
|
14 |
+
app = dash.Dash(__name__, assets_folder=SETTINGS.ASSETS_PATH)
|
15 |
+
server = app.server # Expose the server for deployment
|
16 |
+
|
17 |
+
# Define the app layout
|
18 |
+
layout = html.Div([
|
19 |
+
dcc.Markdown("## Interactive Crystal Viewer"),
|
20 |
+
html.Div([
|
21 |
+
html.Div([
|
22 |
+
html.Label("Search by Chemical System (e.g., 'Ac-Cd-Ge')"),
|
23 |
+
dcc.Input(
|
24 |
+
id='query-input',
|
25 |
+
type='text',
|
26 |
+
value='Ac-Cd-Ge',
|
27 |
+
placeholder='Ac-Cd-Ge',
|
28 |
+
style={'width': '100%'}
|
29 |
+
),
|
30 |
+
], style={'width': '70%', 'display': 'inline-block', 'verticalAlign': 'top'}),
|
31 |
+
html.Div([
|
32 |
+
html.Button('Search', id='search-button', n_clicks=0),
|
33 |
+
], style={'width': '28%', 'display': 'inline-block', 'paddingLeft': '2%', 'verticalAlign': 'top'}),
|
34 |
+
], style={'margin-bottom': '20px'}),
|
35 |
+
html.Div([
|
36 |
+
html.Label("Select Material"),
|
37 |
+
dcc.Dropdown(
|
38 |
+
id='material-dropdown',
|
39 |
+
options=[], # Empty options initially
|
40 |
+
value=None
|
41 |
+
),
|
42 |
+
], style={'margin-bottom': '20px'}),
|
43 |
+
html.Button('Display Material', id='display-button', n_clicks=0),
|
44 |
+
html.Div([
|
45 |
+
html.Div(id='structure-container', style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}),
|
46 |
+
html.Div(id='properties-container',
|
47 |
+
style={'width': '48%', 'display': 'inline-block', 'paddingLeft': '4%', 'verticalAlign': 'top'}),
|
48 |
+
], style={'margin-top': '20px'}),
|
49 |
+
])
|
50 |
+
|
51 |
+
|
52 |
+
# Function to search for materials
|
53 |
+
def search_materials(query):
|
54 |
+
with MPRester(MATERIALS_PROJECT_API_KEY) as mpr:
|
55 |
+
results = mpr.summary.search(
|
56 |
+
chemsys=query,
|
57 |
+
fields=["material_id", "formula_pretty"]
|
58 |
+
)
|
59 |
+
options = [{'label': f"{res.formula_pretty} ({res.material_id})", 'value': res.material_id} for res in results]
|
60 |
+
return options
|
61 |
+
|
62 |
+
|
63 |
+
# Callback to update the material dropdown based on search
|
64 |
+
@app.callback(
|
65 |
+
[Output('material-dropdown', 'options'),
|
66 |
+
Output('material-dropdown', 'value')],
|
67 |
+
Input('search-button', 'n_clicks'),
|
68 |
+
State('query-input', 'value'),
|
69 |
+
)
|
70 |
+
def update_material_dropdown(n_clicks, query):
|
71 |
+
if n_clicks is None or not query:
|
72 |
+
return [], None
|
73 |
+
options = search_materials(query)
|
74 |
+
if not options:
|
75 |
+
return [], None
|
76 |
+
return options, options[0]['value']
|
77 |
+
|
78 |
+
|
79 |
+
# Callback to display the selected material
|
80 |
+
@app.callback(
|
81 |
+
[Output('structure-container', 'children'),
|
82 |
+
Output('properties-container', 'children')],
|
83 |
+
Input('display-button', 'n_clicks'),
|
84 |
+
State('material-dropdown', 'value')
|
85 |
+
)
|
86 |
+
def display_material(n_clicks, material_id):
|
87 |
+
if n_clicks is None or not material_id:
|
88 |
+
return '', ''
|
89 |
+
with MPRester(MATERIALS_PROJECT_API_KEY) as mpr:
|
90 |
+
material = mpr.get_structure_by_material_id(material_id)
|
91 |
+
summary = mpr.summary.get_data_by_id(material_id)
|
92 |
+
|
93 |
+
# Create the StructureMoleculeComponent
|
94 |
+
structure_component = ctc.StructureMoleculeComponent(material)
|
95 |
+
|
96 |
+
# Extract key properties
|
97 |
+
properties = {
|
98 |
+
"Material ID": material_id,
|
99 |
+
"Formula": summary.formula_pretty,
|
100 |
+
"Energy Above Hull (eV/atom)": summary.energy_above_hull,
|
101 |
+
"Space Group": summary.symmetry.symbol,
|
102 |
+
"Band Gap (eV)": summary.band_gap,
|
103 |
+
"Formation Energy (eV/atom)": summary.formation_energy_per_atom,
|
104 |
+
"Magnetic Ordering": summary.ordering,
|
105 |
+
"Total Magnetization (μB/f.u.)": summary.total_magnetization,
|
106 |
+
"Is Stable": summary.is_stable,
|
107 |
+
"Crystal System": summary.symmetry.crystal_system,
|
108 |
+
"Density (g/cm³)": summary.density,
|
109 |
+
}
|
110 |
+
|
111 |
+
# Format properties as an HTML table
|
112 |
+
properties_html = html.Table([
|
113 |
+
html.Tbody([
|
114 |
+
html.Tr([html.Th(key), html.Td(str(value))]) for key, value in properties.items()
|
115 |
+
])
|
116 |
+
], style={'border': '1px solid black', 'width': '100%', 'borderCollapse': 'collapse'})
|
117 |
+
|
118 |
+
return structure_component.layout(), properties_html
|
119 |
+
|
120 |
+
|
121 |
+
# Register crystal toolkit with the app
|
122 |
+
ctc.register_crystal_toolkit(app, layout)
|
123 |
+
|
124 |
+
if __name__ == '__main__':
|
125 |
+
app.run_server(debug=True, port=7860)
|
requirements.txt
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
dash==2.11.1
|
2 |
+
crystal-toolkit[all]
|
3 |
+
pymatgen
|
4 |
+
numpy
|
5 |
+
scipy
|
6 |
+
matplotlib
|
7 |
+
plotly
|
8 |
+
pandas
|
9 |
+
dash-bootstrap-components
|