File size: 4,504 Bytes
f121890
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3dde8a0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import os
from pymatgen.ext.matproj import MPRester
import crystal_toolkit.components as ctc
from crystal_toolkit.settings import SETTINGS

import dash
from dash import html, dcc
from dash.dependencies import Input, Output, State

# Set your Materials Project API key
MATERIALS_PROJECT_API_KEY = os.getenv('MATERIALS_PROJECT_API_KEY')

# Initialize the Dash app
app = dash.Dash(__name__, assets_folder=SETTINGS.ASSETS_PATH)
server = app.server  # Expose the server for deployment

# Define the app layout
layout = html.Div([
    dcc.Markdown("## Interactive Crystal Viewer"),
    html.Div([
        html.Div([
            html.Label("Search by Chemical System (e.g., 'Ac-Cd-Ge')"),
            dcc.Input(
                id='query-input',
                type='text',
                value='Ac-Cd-Ge',
                placeholder='Ac-Cd-Ge',
                style={'width': '100%'}
            ),
        ], style={'width': '70%', 'display': 'inline-block', 'verticalAlign': 'top'}),
        html.Div([
            html.Button('Search', id='search-button', n_clicks=0),
        ], style={'width': '28%', 'display': 'inline-block', 'paddingLeft': '2%', 'verticalAlign': 'top'}),
    ], style={'margin-bottom': '20px'}),
    html.Div([
        html.Label("Select Material"),
        dcc.Dropdown(
            id='material-dropdown',
            options=[],  # Empty options initially
            value=None
        ),
    ], style={'margin-bottom': '20px'}),
    html.Button('Display Material', id='display-button', n_clicks=0),
    html.Div([
        html.Div(id='structure-container', style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}),
        html.Div(id='properties-container',
                 style={'width': '48%', 'display': 'inline-block', 'paddingLeft': '4%', 'verticalAlign': 'top'}),
    ], style={'margin-top': '20px'}),
])


# Function to search for materials
def search_materials(query):
    with MPRester(MATERIALS_PROJECT_API_KEY) as mpr:
        results = mpr.summary.search(
            chemsys=query,
            fields=["material_id", "formula_pretty"]
        )
    options = [{'label': f"{res.formula_pretty} ({res.material_id})", 'value': res.material_id} for res in results]
    return options


# Callback to update the material dropdown based on search
@app.callback(
    [Output('material-dropdown', 'options'),
     Output('material-dropdown', 'value')],
    Input('search-button', 'n_clicks'),
    State('query-input', 'value'),
)
def update_material_dropdown(n_clicks, query):
    if n_clicks is None or not query:
        return [], None
    options = search_materials(query)
    if not options:
        return [], None
    return options, options[0]['value']


# Callback to display the selected material
@app.callback(
    [Output('structure-container', 'children'),
     Output('properties-container', 'children')],
    Input('display-button', 'n_clicks'),
    State('material-dropdown', 'value')
)
def display_material(n_clicks, material_id):
    if n_clicks is None or not material_id:
        return '', ''
    with MPRester(MATERIALS_PROJECT_API_KEY) as mpr:
        material = mpr.get_structure_by_material_id(material_id)
        summary = mpr.summary.get_data_by_id(material_id)

    # Create the StructureMoleculeComponent
    structure_component = ctc.StructureMoleculeComponent(material)

    # Extract key properties
    properties = {
        "Material ID": material_id,
        "Formula": summary.formula_pretty,
        "Energy Above Hull (eV/atom)": summary.energy_above_hull,
        "Space Group": summary.symmetry.symbol,
        "Band Gap (eV)": summary.band_gap,
        "Formation Energy (eV/atom)": summary.formation_energy_per_atom,
        "Magnetic Ordering": summary.ordering,
        "Total Magnetization (μB/f.u.)": summary.total_magnetization,
        "Is Stable": summary.is_stable,
        "Crystal System": summary.symmetry.crystal_system,
        "Density (g/cm³)": summary.density,
    }

    # Format properties as an HTML table
    properties_html = html.Table([
        html.Tbody([
            html.Tr([html.Th(key), html.Td(str(value))]) for key, value in properties.items()
        ])
    ], style={'border': '1px solid black', 'width': '100%', 'borderCollapse': 'collapse'})

    return structure_component.layout(), properties_html


# Register crystal toolkit with the app
ctc.register_crystal_toolkit(app, layout)

if __name__ == '__main__':
    app.run_server(debug=True, port=7860, host="0.0.0.0")