Skip to content

Commit 61bca5c

Browse files
Chiu PeterChiu Peter
authored andcommitted
redo composition input and alarming message
1 parent 45c9975 commit 61bca5c

File tree

1 file changed

+109
-26
lines changed

1 file changed

+109
-26
lines changed

crystal_toolkit/components/pourbaix.py

Lines changed: 109 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
from __future__ import annotations
22

3+
import logging
34
import re
45

56
import numpy as np
67
import plotly.graph_objects as go
78
from dash import dcc, html
8-
from dash.dependencies import Component, Input, Output
9+
from dash.dependencies import Component, Input, Output, State
910
from dash.exceptions import PreventUpdate
1011
from frozendict import frozendict
1112
from pymatgen.analysis.pourbaix_diagram import PREFAC, PourbaixDiagram
@@ -21,6 +22,8 @@
2122
except ImportError:
2223
ELEMENTS_HO = {Element("H"), Element("O")}
2324

25+
26+
logger = logging.getLogger(__name__)
2427
__author__ = "Joseph Montoya"
2528
__email__ = "[email protected]"
2629

@@ -33,6 +36,10 @@
3336
WIDTH = 700 # in px
3437
MIN_CONCENTRATION = 1e-8
3538
MAX_CONCENTRATION = 5
39+
MIN_PH = -2
40+
MAX_PH = 16
41+
MIN_V = -4
42+
MAX_V = 4
3643

3744

3845
class PourbaixDiagramComponent(MPComponent):
@@ -51,13 +58,13 @@ class PourbaixDiagramComponent(MPComponent):
5158
"titlefont": {"color": "#000000", "size": 24.0},
5259
"type": "linear",
5360
"zeroline": False,
54-
"range": [-2, 16],
61+
"range": [MIN_PH, MAX_PH],
5562
},
5663
yaxis={
5764
"title": "Applied Potential (V vs. SHE)",
5865
"anchor": "x",
5966
"mirror": "ticks",
60-
"range": [-2, 4],
67+
"range": [MIN_V, MAX_V],
6168
"showgrid": False,
6269
"showline": True,
6370
"side": "left",
@@ -454,8 +461,8 @@ def get_figure(
454461

455462
# Get data for heatmap
456463
if heatmap_entry is not None:
457-
ph_range = np.arange(-2, 16.001, 0.1)
458-
v_range = np.arange(-2, 4.001, 0.1)
464+
ph_range = np.arange(MIN_PH, MAX_PH + 0.001, 0.1)
465+
v_range = np.arange(MIN_V, MAX_V + 0.001, 0.1)
459466
ph_mesh, v_mesh = np.meshgrid(ph_range, v_range)
460467
decomposition_e = pourbaix_diagram.get_decomposition_energy(
461468
heatmap_entry, ph_mesh, v_mesh
@@ -514,11 +521,15 @@ def get_figure(
514521
hoverinfo="text",
515522
name=f"{heatmap_formula} ({heatmap_entry.entry_id}) Heatmap",
516523
showlegend=True,
524+
contours=dict(
525+
start=0,
526+
end=1,
527+
),
517528
)
518529
data.append(h_map)
519530

520531
if show_water_lines:
521-
ph_range = [-2, 16]
532+
ph_range = [MIN_PH, MAX_PH]
522533
# hydrogen line
523534
data.append(
524535
go.Scatter(
@@ -576,6 +587,33 @@ def _sub_layouts(self) -> dict[str, Component]:
576587
),
577588
html.Div(
578589
[
590+
html.Div(
591+
[
592+
dcc.ConfirmDialog(
593+
id=self.id("invalid-comp-alarm"),
594+
message="Illegal composition entry!",
595+
),
596+
html.H5(
597+
"Composition",
598+
id=self.id("composition-title"),
599+
style={"fontWeight": "bold"},
600+
),
601+
dcc.Input(
602+
id=self.id("comp-text"),
603+
type="text",
604+
# placeholder="composition e.g. 1:1:1",
605+
),
606+
html.Button(
607+
"Update",
608+
id=self.id("comp-btn"),
609+
),
610+
html.Br(),
611+
html.Br(),
612+
dcc.Store(id=self.id("elements-store")),
613+
],
614+
id=self.id("comp-panel"),
615+
style={"display": "none"},
616+
),
579617
html.Div(id=self.id("element_specific_controls")),
580618
ctl.Block(html.Div(id=self.id("display-composition"))),
581619
]
@@ -625,7 +663,10 @@ def _sub_layouts(self) -> dict[str, Component]:
625663

626664
def layout(self) -> html.Div:
627665
return html.Div(
628-
children=[self._sub_layouts["options"], self._sub_layouts["graph"]]
666+
children=[
667+
self._sub_layouts["options"],
668+
self._sub_layouts["graph"],
669+
]
629670
)
630671

631672
def generate_callbacks(self, app, cache) -> None:
@@ -638,8 +679,6 @@ def update_heatmap_choices(entries, mat_detials):
638679
if not entries:
639680
raise PreventUpdate
640681

641-
print("should be 4")
642-
643682
options = []
644683
for entry in entries:
645684
if entry["entry_id"].startswith("mp"):
@@ -695,6 +734,10 @@ def update_heatmap_choices(entries, mat_detials):
695734

696735
@app.callback(
697736
Output(self.id("element_specific_controls"), "children"),
737+
Output(self.id("comp-panel"), "style"),
738+
Output(self.id("elements-store"), "data"),
739+
Output(self.id("comp-text"), "value"),
740+
Output(self.id("composition-title"), "children"),
698741
Input(self.id(), "data"),
699742
# Input(self.get_kwarg_id("heatmap_choice"), "value"),
700743
# State(self.get_kwarg_id("show_heatmap"), "value"),
@@ -709,7 +752,6 @@ def update_element_specific_sliders(
709752
if not entries:
710753
raise PreventUpdate
711754

712-
print("should be 1")
713755
elements = set()
714756

715757
# kwargs = self.reconstruct_kwargs_from_state()
@@ -727,12 +769,13 @@ def update_element_specific_sliders(
727769
# exclude O and H
728770
elements = elements - ELEMENTS_HO
729771

730-
comp_defaults = {element: 1 / len(elements) for element in elements}
772+
# comp_defaults = {element: 1 / len(elements) for element in elements}
731773

732774
comp_inputs = []
733775
conc_inputs = []
734776
for element in sorted(elements):
735777
if len(elements) > 1:
778+
"""
736779
comp_input = html.Div(
737780
[
738781
self.get_slider_input(
@@ -745,6 +788,7 @@ def update_element_specific_sliders(
745788
]
746789
)
747790
comp_inputs.append(comp_input)
791+
"""
748792

749793
conc_input = html.Div(
750794
[
@@ -778,7 +822,25 @@ def update_element_specific_sliders(
778822

779823
comp_conc_controls += conc_inputs
780824

781-
return html.Div(comp_conc_controls)
825+
#
826+
comp_panel_style = {"display": "block"}
827+
828+
#
829+
elements = [element.symbol for element in elements]
830+
831+
#
832+
default_comp = ":".join(["1" for _ in elements])
833+
834+
#
835+
title = "Composition of " + ":".join(elements)
836+
837+
return (
838+
html.Div(comp_conc_controls),
839+
comp_panel_style,
840+
elements,
841+
default_comp,
842+
title,
843+
)
782844

783845
"""
784846
@app.callback(
@@ -873,15 +935,12 @@ def update_element_specific_sliders(entries, heatmap_choice, show_heatmap):
873935
def update_displayed_composition(dependency): # **kwargs):
874936
kwargs = self.reconstruct_kwargs_from_state()
875937

876-
print("should be 2")
877-
878938
comp_dict = {}
879939
for key, val in kwargs.items():
880940
if "comp" in key: # keys are encoded like "comp-Ag"
881941
el = key.split("-")[1]
882942
comp_dict[el] = val
883943
comp_dict = comp_dict or None
884-
print(comp_dict)
885944
if not comp_dict:
886945
return ""
887946

@@ -899,25 +958,48 @@ def update_displayed_composition(dependency): # **kwargs):
899958

900959
@cache.memoize(timeout=5 * 60)
901960
def get_pourbaix_diagram(pourbaix_entries, **kwargs):
902-
print("yeee")
903-
print(kwargs)
904961
return PourbaixDiagram(pourbaix_entries, **kwargs)
905962

906963
@app.callback(
907964
Output(self.id("graph"), "figure"),
965+
Output(self.id("invalid-comp-alarm"), "displayed"),
908966
Input(self.id(), "data"),
909967
Input(self.id("display-composition"), "children"),
910968
Input(self.get_all_kwargs_id(), "value"),
969+
Input(self.id("comp-btn"), "n_clicks"),
970+
State(self.id("elements-store"), "data"),
971+
State(self.id("comp-text"), "value"),
972+
prevent_initial_call=True,
911973
)
912-
def make_figure(pourbaix_entries, dependency, kwargs) -> go.Figure:
974+
def make_figure(
975+
pourbaix_entries, dependency, kwargs, n_clicks, elements, comp_text
976+
) -> go.Figure:
913977
# show_heatmap, heatmap_choice, filter_solids
978+
914979
if pourbaix_entries is None:
915980
raise PreventUpdate
916981

917-
print("should be 3")
982+
# check if composition input
983+
if n_clicks:
984+
raw_comp_list = comp_text.split(":")
985+
else:
986+
raw_comp_list = [1 / len(elements) for _ in elements]
987+
988+
if len(raw_comp_list) != len(elements):
989+
logger.error("Invalid composition input!")
990+
return go.Figure(
991+
layout={**PourbaixDiagramComponent.empty_plot_style}
992+
), True
993+
try:
994+
# avoid direct type casting because string inputs may raise errors
995+
comp_list = [float(t) for t in raw_comp_list]
996+
except Exception:
997+
logger.error("Invalid composition input!")
998+
return go.Figure(
999+
layout={**PourbaixDiagramComponent.empty_plot_style}
1000+
), True
9181001

9191002
kwargs = self.reconstruct_kwargs_from_state()
920-
print(kwargs)
9211003

9221004
pourbaix_entries = self.from_data(pourbaix_entries)
9231005

@@ -943,13 +1025,17 @@ def make_figure(pourbaix_entries, dependency, kwargs) -> go.Figure:
9431025
comp_dict = {}
9441026
# e.g. kwargs contains {"comp-Ag": 0.5, "comp-Fe": 0.5},
9451027
# essentially {slider_name: slider_value}
1028+
"""
9461029
for key, val in kwargs.items():
9471030
if "comp" in key: # keys are encoded like "comp-Ag"
9481031
el = key.split("-")[1]
9491032
comp_dict[el] = val
1033+
"""
1034+
comp_dict = {}
1035+
for comp_val, element in zip(comp_list, elements):
1036+
comp_dict[element] = comp_val
1037+
9501038
comp_dict = comp_dict or None
951-
print("yee2")
952-
print(comp_dict)
9531039
conc_dict = {}
9541040
# e.g. kwargs contains {"conc-Ag": 1e-6, "conc-Fe": 1e-4},
9551041
# essentially {slider_name: slider_value}
@@ -958,8 +1044,6 @@ def make_figure(pourbaix_entries, dependency, kwargs) -> go.Figure:
9581044
el = key.split("-")[1]
9591045
conc_dict[el] = val
9601046
conc_dict = conc_dict or None
961-
print("yeee2")
962-
print(conc_dict)
9631047
pourbaix_diagram = get_pourbaix_diagram(
9641048
pourbaix_entries,
9651049
comp_dict=comp_dict,
@@ -974,11 +1058,10 @@ def make_figure(pourbaix_entries, dependency, kwargs) -> go.Figure:
9741058
conc_dict,
9751059
comp_dict,
9761060
)
977-
9781061
return self.get_figure(
9791062
pourbaix_diagram,
9801063
heatmap_entry=heatmap_entry,
981-
)
1064+
), False
9821065

9831066
# TODO
9841067
# def graph_layout(self):

0 commit comments

Comments
 (0)