Esempio n. 1
0
def get_electrochemical_stability(mpid, pH, potential):
    '''
    A wrapper for pymatgen to construct Pourbaix amd calculate electrochemical
    stability under reaction condition (i.e. at a given pH and applied potential).

    Arg:
        mpid         Materials project ID of a bulk composition. e.g. Pt: 'mp-126'.
        pH:          pH at reaction condition. Commonly ones are: acidic: pH=0,
                     neutral: pH=7, and basic pH=14.
        potential:   Applied potential at reaction condition.

    Returns:
        stability    Electrochemical stability of a composition under reaction condition,
                     unit is eV/atom.
    '''
    mpr = MPRester(read_rc('matproj_api_key'))
    try:
        entry = mpr.get_entries(mpid)[0]
        composition = entry.composition
        comp_dict = {str(key): value for key, value in composition.items()
                     if key not in ELEMENTS_HO}
        entries = mpr.get_pourbaix_entries(list(comp_dict.keys()))
        entry = [entry for entry in entries if entry.entry_id == mpid][0]
        pbx = PourbaixDiagram(entries, comp_dict=comp_dict, filter_solids=False)
        stability = pbx.get_decomposition_energy(entry, pH=pH, V=potential)
        stability = round(stability, 3)
    # Some mpid's stability are not available
    except IndexError:
        stability = np.nan
    return stability
    def test_mpr_pipeline(self):
        from pymatgen import MPRester
        mpr = MPRester()
        data = mpr.get_pourbaix_entries(["Zn"])
        pbx = PourbaixDiagram(data, filter_solids=True, conc_dict={"Zn": 1e-8})
        pbx.find_stable_entry(10, 0)

        data = mpr.get_pourbaix_entries(["Ag", "Te"])
        pbx = PourbaixDiagram(data,
                              filter_solids=True,
                              conc_dict={
                                  "Ag": 1e-8,
                                  "Te": 1e-8
                              })
        self.assertEqual(len(pbx.stable_entries), 30)
        test_entry = pbx.find_stable_entry(8, 2)
        self.assertAlmostEqual(test_entry.energy, 2.3894017960000009, 1)

        # Test custom ions
        entries = mpr.get_pourbaix_entries(["Sn", "C", "Na"])
        ion = IonEntry(Ion.from_formula("NaO28H80Sn12C24+"), -161.676)
        custom_ion_entry = PourbaixEntry(ion, entry_id='my_ion')
        pbx = PourbaixDiagram(entries + [custom_ion_entry],
                              filter_solids=True,
                              comp_dict={
                                  "Na": 1,
                                  "Sn": 12,
                                  "C": 24
                              })
        self.assertAlmostEqual(
            pbx.get_decomposition_energy(custom_ion_entry, 5, 2), 2.1209002582,
            1)
Esempio n. 3
0
    def test_multicomponent(self):
        # Assure no ions get filtered at high concentration
        ag_n = [
            e for e in self.test_data['Ag-Te-N'] if not "Te" in e.composition
        ]
        highconc = PourbaixDiagram(ag_n,
                                   filter_solids=True,
                                   conc_dict={
                                       "Ag": 1e-5,
                                       "N": 1
                                   })
        entry_sets = [set(e.entry_id) for e in highconc.stable_entries]
        self.assertIn({"mp-124", "ion-17"}, entry_sets)

        # Binary system
        pd_binary = PourbaixDiagram(self.test_data['Ag-Te'],
                                    filter_solids=True,
                                    comp_dict={
                                        "Ag": 0.5,
                                        "Te": 0.5
                                    },
                                    conc_dict={
                                        "Ag": 1e-8,
                                        "Te": 1e-8
                                    })
        self.assertEqual(len(pd_binary.stable_entries), 30)
        test_entry = pd_binary.find_stable_entry(8, 2)
        self.assertTrue("mp-499" in test_entry.entry_id)

        # Find a specific multientry to test
        self.assertEqual(pd_binary.get_decomposition_energy(test_entry, 8, 2),
                         0)
        self.assertEqual(
            pd_binary.get_decomposition_energy(test_entry.entry_list[0], 8, 2),
            0)

        pd_ternary = PourbaixDiagram(self.test_data['Ag-Te-N'],
                                     filter_solids=True)
        self.assertEqual(len(pd_ternary.stable_entries), 49)

        ag = self.test_data['Ag-Te-N'][30]
        self.assertAlmostEqual(pd_ternary.get_decomposition_energy(ag, 2, -1),
                               0)
        self.assertAlmostEqual(pd_ternary.get_decomposition_energy(ag, 10, -2),
                               0)
Esempio n. 4
0
    def test_multicomponent(self):
        # Binary system
        pd_binary = PourbaixDiagram(self.test_data['Ag-Te'], filter_solids=True,
                                    comp_dict={"Ag": 0.5, "Te": 0.5},
                                    conc_dict={"Ag": 1e-8, "Te": 1e-8})
        self.assertEqual(len(pd_binary.stable_entries), 30)
        test_entry = pd_binary.find_stable_entry(8, 2)
        self.assertTrue("mp-499" in test_entry.entry_id)

        # Find a specific multientry to test
        self.assertEqual(pd_binary.get_decomposition_energy(test_entry, 8, 2), 0)
        self.assertEqual(pd_binary.get_decomposition_energy(
            test_entry.entry_list[0], 8, 2), 0)

        pd_ternary = PourbaixDiagram(self.test_data['Ag-Te-N'], filter_solids=True)
        self.assertEqual(len(pd_ternary.stable_entries), 49)

        ag = self.test_data['Ag-Te-N'][30]
        self.assertAlmostEqual(pd_ternary.get_decomposition_energy(ag, 2, -1), 0)
        self.assertAlmostEqual(pd_ternary.get_decomposition_energy(ag, 10, -2), 0)
Esempio n. 5
0
    def test_multicomponent(self):
        # Assure no ions get filtered at high concentration
        ag_n = [e for e in self.test_data['Ag-Te-N']
                if not "Te" in e.composition]
        highconc = PourbaixDiagram(ag_n, filter_solids=True,
                                   conc_dict={"Ag": 1e-5, "N": 1})
        entry_sets = [set(e.entry_id) for e in highconc.stable_entries]
        self.assertIn({"mp-124", "ion-17"}, entry_sets)

        # Binary system
        pd_binary = PourbaixDiagram(self.test_data['Ag-Te'], filter_solids=True,
                                    comp_dict={"Ag": 0.5, "Te": 0.5},
                                    conc_dict={"Ag": 1e-8, "Te": 1e-8})
        self.assertEqual(len(pd_binary.stable_entries), 30)
        test_entry = pd_binary.find_stable_entry(8, 2)
        self.assertTrue("mp-499" in test_entry.entry_id)

        # Find a specific multientry to test
        self.assertEqual(pd_binary.get_decomposition_energy(test_entry, 8, 2), 0)
        self.assertEqual(pd_binary.get_decomposition_energy(
            test_entry.entry_list[0], 8, 2), 0)

        pd_ternary = PourbaixDiagram(self.test_data['Ag-Te-N'], filter_solids=True)
        self.assertEqual(len(pd_ternary.stable_entries), 49)

        ag = self.test_data['Ag-Te-N'][30]
        self.assertAlmostEqual(pd_ternary.get_decomposition_energy(ag, 2, -1), 0)
        self.assertAlmostEqual(pd_ternary.get_decomposition_energy(ag, 10, -2), 0)

        # Test invocation of pourbaix diagram from ternary data
        new_ternary = PourbaixDiagram(pd_ternary.all_entries)
        self.assertEqual(len(new_ternary.stable_entries), 49)
        self.assertAlmostEqual(new_ternary.get_decomposition_energy(ag, 2, -1), 0)
        self.assertAlmostEqual(new_ternary.get_decomposition_energy(ag, 10, -2), 0)
    def test_multicomponent(self):
        # Assure no ions get filtered at high concentration
        ag_n = [e for e in self.test_data["Ag-Te-N"] if "Te" not in e.composition]
        highconc = PourbaixDiagram(
            ag_n, filter_solids=True, conc_dict={"Ag": 1e-5, "N": 1}
        )
        entry_sets = [set(e.entry_id) for e in highconc.stable_entries]
        self.assertIn({"mp-124", "ion-17"}, entry_sets)

        # Binary system
        pd_binary = PourbaixDiagram(
            self.test_data["Ag-Te"],
            filter_solids=True,
            comp_dict={"Ag": 0.5, "Te": 0.5},
            conc_dict={"Ag": 1e-8, "Te": 1e-8},
        )
        self.assertEqual(len(pd_binary.stable_entries), 30)
        test_entry = pd_binary.find_stable_entry(8, 2)
        self.assertTrue("mp-499" in test_entry.entry_id)

        # Find a specific multientry to test
        self.assertEqual(pd_binary.get_decomposition_energy(test_entry, 8, 2), 0)

        pd_ternary = PourbaixDiagram(self.test_data["Ag-Te-N"], filter_solids=True)
        self.assertEqual(len(pd_ternary.stable_entries), 49)

        # Fetch a solid entry and a ground state entry mixture
        ag_te_n = self.test_data["Ag-Te-N"][-1]
        ground_state_ag_with_ions = MultiEntry(
            [self.test_data["Ag-Te-N"][i] for i in [4, 18, 30]],
            weights=[1 / 3, 1 / 3, 1 / 3],
        )
        self.assertAlmostEqual(
            pd_ternary.get_decomposition_energy(ag_te_n, 2, -1), 2.767822855765
        )
        self.assertAlmostEqual(
            pd_ternary.get_decomposition_energy(ag_te_n, 10, -2), 3.756840056890625
        )
        self.assertAlmostEqual(
            pd_ternary.get_decomposition_energy(ground_state_ag_with_ions, 2, -1), 0
        )

        # Test invocation of pourbaix diagram from ternary data
        new_ternary = PourbaixDiagram(pd_ternary.all_entries)
        self.assertEqual(len(new_ternary.stable_entries), 49)
        self.assertAlmostEqual(
            new_ternary.get_decomposition_energy(ag_te_n, 2, -1), 2.767822855765
        )
        self.assertAlmostEqual(
            new_ternary.get_decomposition_energy(ag_te_n, 10, -2), 3.756840056890625
        )
        self.assertAlmostEqual(
            new_ternary.get_decomposition_energy(ground_state_ag_with_ions, 2, -1), 0
        )
Esempio n. 7
0
    def test_mpr_pipeline(self):
        from pymatgen.ext.matproj import MPRester

        mpr = MPRester()
        data = mpr.get_pourbaix_entries(["Zn"])
        pbx = PourbaixDiagram(data, filter_solids=True, conc_dict={"Zn": 1e-8})
        pbx.find_stable_entry(10, 0)

        data = mpr.get_pourbaix_entries(["Ag", "Te"])
        pbx = PourbaixDiagram(data,
                              filter_solids=True,
                              conc_dict={
                                  "Ag": 1e-8,
                                  "Te": 1e-8
                              })
        self.assertEqual(len(pbx.stable_entries), 29)
        test_entry = pbx.find_stable_entry(8, 2)
        self.assertAlmostEqual(test_entry.energy, 2.3894017960000009, 1)

        # Test custom ions
        entries = mpr.get_pourbaix_entries(["Sn", "C", "Na"])
        ion = IonEntry(Ion.from_formula("NaO28H80Sn12C24+"), -161.676)
        custom_ion_entry = PourbaixEntry(ion, entry_id="my_ion")
        pbx = PourbaixDiagram(
            entries + [custom_ion_entry],
            filter_solids=True,
            comp_dict={
                "Na": 1,
                "Sn": 12,
                "C": 24
            },
        )
        self.assertAlmostEqual(
            pbx.get_decomposition_energy(custom_ion_entry, 5, 2), 2.1209002582,
            1)

        # Test against ion sets with multiple equivalent ions (Bi-V regression)
        entries = mpr.get_pourbaix_entries(["Bi", "V"])
        pbx = PourbaixDiagram(entries,
                              filter_solids=True,
                              conc_dict={
                                  "Bi": 1e-8,
                                  "V": 1e-8
                              })
        self.assertTrue(
            all([
                "Bi" in entry.composition and "V" in entry.composition
                for entry in pbx.all_entries
            ]))
Esempio n. 8
0
    def test_get_decomposition(self):
        # Test a stable entry to ensure that it's zero in the stable region
        entry = self.test_data["Zn"][12]  # Should correspond to mp-2133
        self.assertAlmostEqual(
            self.pbx.get_decomposition_energy(entry, 10, 1),
            0.0,
            5,
            "Decomposition energy of ZnO is not 0.",
        )

        # Test an unstable entry to ensure that it's never zero
        entry = self.test_data["Zn"][11]
        ph, v = np.meshgrid(np.linspace(0, 14), np.linspace(-2, 4))
        result = self.pbx_nofilter.get_decomposition_energy(entry, ph, v)
        self.assertTrue((result >= 0).all(),
                        "Unstable energy has hull energy of 0 or less")

        # Test an unstable hydride to ensure HER correction works
        self.assertAlmostEqual(
            self.pbx.get_decomposition_energy(entry, -3, -2), 3.6979147983333)
        # Test a list of pHs
        self.pbx.get_decomposition_energy(entry, np.linspace(0, 2, 5), 2)

        # Test a list of Vs
        self.pbx.get_decomposition_energy(entry, 4, np.linspace(-3, 3, 10))

        # Test a set of matching arrays
        ph, v = np.meshgrid(np.linspace(0, 14), np.linspace(-3, 3))
        self.pbx.get_decomposition_energy(entry, ph, v)

        # Test custom ions
        entries = self.test_data["C-Na-Sn"]
        ion = IonEntry(Ion.from_formula("NaO28H80Sn12C24+"), -161.676)
        custom_ion_entry = PourbaixEntry(ion, entry_id="my_ion")
        pbx = PourbaixDiagram(
            entries + [custom_ion_entry],
            filter_solids=True,
            comp_dict={
                "Na": 1,
                "Sn": 12,
                "C": 24
            },
        )
        self.assertAlmostEqual(
            pbx.get_decomposition_energy(custom_ion_entry, 5, 2), 2.1209002582,
            1)
Esempio n. 9
0
    def test_mpr_pipeline(self):
        from pymatgen import MPRester
        mpr = MPRester()
        data = mpr.get_pourbaix_entries(["Zn"])
        pbx = PourbaixDiagram(data, filter_solids=True, conc_dict={"Zn": 1e-8})
        pbx.find_stable_entry(10, 0)

        data = mpr.get_pourbaix_entries(["Ag", "Te"])
        pbx = PourbaixDiagram(data, filter_solids=True,
                              conc_dict={"Ag": 1e-8, "Te": 1e-8})
        self.assertEqual(len(pbx.stable_entries), 30)
        test_entry = pbx.find_stable_entry(8, 2)
        self.assertAlmostEqual(test_entry.energy, 2.393900378500001)

        # Test custom ions
        entries = mpr.get_pourbaix_entries(["Sn", "C", "Na"])
        ion = IonEntry(Ion.from_formula("NaO28H80Sn12C24+"), -161.676)
        custom_ion_entry = PourbaixEntry(ion, entry_id='my_ion')
        pbx = PourbaixDiagram(entries + [custom_ion_entry], filter_solids=True,
                              comp_dict={"Na": 1, "Sn": 12, "C": 24})
        self.assertAlmostEqual(pbx.get_decomposition_energy(custom_ion_entry, 5, 2),
                               8.31082110278154)
 def test_solid_filter(self):
     entries = self.test_data['Ag-Te-N']
     pbx = PourbaixDiagram(entries, filter_solids=True)
     pbx.get_decomposition_energy(entries[0], 0, 0)
 def test_nofilter(self):
     entries = self.test_data['Ag-Te']
     pbx = PourbaixDiagram(entries)
     pbx.get_decomposition_energy(entries[0], 0, 0)
Esempio n. 12
0
 def test_solid_filter(self):
     entries = self.test_data['Ag-Te-N']
     pbx = PourbaixDiagram(entries, filter_solids=True)
     pbx.get_decomposition_energy(entries[0], 0, 0)
Esempio n. 13
0
 def test_nofilter(self):
     entries = self.test_data['Ag-Te']
     pbx = PourbaixDiagram(entries)
     pbx.get_decomposition_energy(entries[0], 0, 0)
Esempio n. 14
0
# Import necessary tools from pymatgen
from pymatgen import MPRester
from pymatgen.analysis.pourbaix_diagram import PourbaixDiagram, PourbaixPlotter
# %matplotlib inline
# Initialize the MP Rester
mpr = MPRester("hvSDrzMafUtXE0JQ")  ##将API 秘钥输入适配器中,并且初始化适配器,需要自己申请。
# Get all pourbaix entries corresponding to the Cu-O-H chemical system.
entries = mpr.get_pourbaix_entries(["Cu"])
# Construct the PourbaixDiagram object
pbx = PourbaixDiagram(entries)
# Get an entry stability as a function of pH and V
entry = [e for e in entries if e.entry_id == 'mp-1692'][0]
print(
    "CuO's potential energy per atom relative to the most",
    "stable decomposition product is {:0.2f} eV/atom".format(
        pbx.get_decomposition_energy(entry, pH=7, V=-0.2)))
plotter = PourbaixPlotter(pbx)
plotter.get_pourbaix_plot().show()
plt = plotter.plot_entry_stability(entry)
plt.show()
# Get all pourbaix entries corresponding to the Fe-O-H chemical system.
entries = mpr.get_pourbaix_entries(["Bi", "V"])
# Construct the PourbaixDiagram object
pbx = PourbaixDiagram(entries,
                      comp_dict={
                          "Bi": 0.5,
                          "V": 0.5
                      },
                      conc_dict={
                          "Bi": 1e-8,
                          "V": 1e-8
Esempio n. 15
0
    def get_figure(pourbaix_diagram: PourbaixDiagram,
                   heatmap_entry=None,
                   show_water_lines=True) -> go.Figure:
        """
        Static method for getting plotly figure from a Pourbaix diagram.

        Args:
            pourbaix_diagram (PourbaixDiagram): Pourbaix diagram to plot
            heatmap_entry (PourbaixEntry): id for the heatmap generation
            show_water_lines (bool): if True, show region of water stability

        Returns:
            (dict) figure layout

        """
        data = []

        shapes = []
        xydata = []
        labels = []
        domain_heights = []

        include_legend = set()

        for entry, vertices in pourbaix_diagram._stable_domain_vertices.items(
        ):
            formula = entry.name
            clean_formula = PourbaixDiagramComponent.clean_formula(formula)

            # Generate information for textual labels
            domain = Polygon(vertices)
            centroid = domain.centroid
            height = domain.bounds[3] - domain.bounds[1]

            # Ensure label is within plot area
            # TODO: remove hard-coded value here and make sure it's set dynamically
            xydata.append([centroid.x, centroid.y])
            labels.append(clean_formula)
            domain_heights.append(height)

            # Assumes that entry.phase_type is either "Solid" or "Ion" or
            # a list of "Solid" and "Ion"
            if isinstance(entry.phase_type, str):
                legend_entry = entry.phase_type
            elif isinstance(entry.phase_type, list):
                if len(set(entry.phase_type)) == 1:
                    legend_entry = ("Mixed Ion" if entry.phase_type[0] == "Ion"
                                    else "Mixed Solid")
                else:
                    legend_entry = "Mixed Ion/Solid"
            else:
                # Should never get here
                print(f"Debug required in Pourbaix for {entry.phase_type}")
                legend_entry = "Unknown"

            if not heatmap_entry:
                if legend_entry == "Ion" or legend_entry == "Unknown":
                    fillcolor = "rgb(255,255,250,1)"  # same color as old website
                elif legend_entry == "Mixed Ion":
                    fillcolor = "rgb(255,255,240,1)"
                elif legend_entry == "Solid":
                    fillcolor = "rgba(188,236,237,1)"  # same color as old website
                elif legend_entry == "Mixed Solid":
                    fillcolor = "rgba(155,229,232,1)"
                elif legend_entry == "Mixed Ion/Solid":
                    fillcolor = "rgb(95,238,222,1)"
            else:
                fillcolor = "rgba(0,0,0,0)"

            data.append(
                go.Scatter(
                    x=[v[0] for v in vertices],
                    y=[v[1] for v in vertices],
                    fill="toself",
                    fillcolor=fillcolor,
                    legendgroup=legend_entry,
                    # legendgrouptitle={"text": legend_entry},
                    name=legend_entry,
                    text=f"{clean_formula} ({entry.entry_id})",
                    marker={"color": "Black"},
                    line={
                        "color": "Black",
                        "width": 0
                    },
                    mode="lines",
                    showlegend=True
                    if legend_entry not in include_legend else False,
                ))

            include_legend.add(legend_entry)

            # Add lines separately so they show up on heatmap

            # Info on SVG paths: https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
            # Move to first point
            path = "M {},{}".format(*vertices[0])
            # Draw lines to each other point
            path += "".join(
                ["L {},{}".format(*vertex) for vertex in vertices[1:]])
            # Close path
            path += "Z"

            # stable entries are black with default color scheme,
            # so use white lines instead
            if heatmap_entry:
                line = {"color": "White", "width": 4}
            else:
                line = {"color": "Black", "width": 1}

            shape = go.layout.Shape(
                type="path",
                path=path,
                fillcolor="rgba(0,0,0,0)",
                opacity=1,
                line=line,
            )
            shapes.append(shape)

        layout = PourbaixDiagramComponent.default_plot_style
        layout.update({"shapes": shapes})

        if heatmap_entry is None:
            x, y = zip(*xydata)
            data.append(
                go.Scatter(x=x,
                           y=y,
                           text=labels,
                           hoverinfo="text",
                           mode="text",
                           name="Labels"))
            layout.update({"annotations": []})
        else:
            # Add annotations to layout to make text more readable when displaying heatmaps

            # TODO: this doesn't work yet; resolve or scrap
            # cmap = get_cmap(PourbaixDiagramComponent.colorscale)
            # def get_text_color(x, y):
            #     """
            #     Set text color based on whether background at that point is dark or light.
            #     """
            #     energy = pourbaix_diagram.get_decomposition_energy(entry, pH=x, V=y)
            #     c = [int(c * 255) for c in cmap(energy)[0:3]]
            #     # borrowed from crystal_toolkit.components.structure
            #     # TODO: move to utility function and ensure correct attribution for magic numbers
            #     if 1 - (c[0] * 0.299 + c[1] * 0.587 + c[2] * 0.114) / 255 < 0.5:
            #         font_color = "#000000"
            #     else:
            #         font_color = "#ffffff"
            #     #print(energy, c, font_color)
            #     return font_color

            def get_text_size(available_vertical_space):
                """
                Set text size based on available vertical space
                """
                return min(max(6 * available_vertical_space, 12), 20)

            annotations = [
                {
                    "align": "center",
                    "bgcolor": "white",
                    "font": {
                        "color": "black",
                        "size": get_text_size(height)
                    },
                    "opacity": 1,
                    "showarrow": False,
                    "text": label,
                    "x": x,
                    "xanchor": "center",
                    "yanchor": "auto",
                    # "xshift": -10,
                    # "yshift": -10,
                    "xref": "x",
                    "y": y,
                    "yref": "y",
                } for (x,
                       y), label, height in zip(xydata, labels, domain_heights)
            ]
            layout.update({"annotations": annotations})

        # Get data for heatmap
        if heatmap_entry is not None:
            ph_range = np.arange(-2, 16.001, 0.1)
            v_range = np.arange(-2, 4.001, 0.1)
            ph_mesh, v_mesh = np.meshgrid(ph_range, v_range)
            decomposition_e = pourbaix_diagram.get_decomposition_energy(
                heatmap_entry, ph_mesh, v_mesh)

            # Generate hoverinfo
            hovertexts = []
            for ph_val, v_val, de_val in zip(ph_mesh.ravel(), v_mesh.ravel(),
                                             decomposition_e.ravel()):
                hovertext = [
                    "∆G<sub>pbx</sub>={:.2f}".format(de_val),
                    "ph={:.2f}".format(ph_val),
                    "V={:.2f}".format(v_val),
                ]
                hovertext = "<br>".join(hovertext)
                hovertexts.append(hovertext)
            hovertexts = np.reshape(hovertexts, list(decomposition_e.shape))

            # Enforce decomposition limit energy
            decomposition_e = np.min(
                [decomposition_e,
                 np.ones(decomposition_e.shape)], axis=0)

            heatmap_formula = unicodeify(
                Composition(heatmap_entry.composition).reduced_formula)

            hmap = go.Contour(
                z=decomposition_e,
                x=list(ph_range),
                y=list(v_range),
                colorbar={
                    "title": "∆G<sub>pbx</sub> (eV/atom)",
                    "titleside": "right",
                },
                colorscale=PourbaixDiagramComponent.colorscale,  # or magma
                zmin=0,
                zmax=1,
                connectgaps=True,
                line_smoothing=0,
                line_width=0,
                # contours_coloring="heatmap",
                text=hovertexts,
                name=f"{heatmap_formula} ({heatmap_entry.entry_id}) Heatmap",
                showlegend=True,
            )
            data.append(hmap)

        if show_water_lines:
            ph_range = [-2, 16]
            # hydrogen line
            data.append(
                go.Scatter(
                    x=[ph_range[0], ph_range[1]],
                    y=[-ph_range[0] * PREFAC, -ph_range[1] * PREFAC],
                    mode="lines",
                    line={
                        "color": "orange",
                        "dash": "dash"
                    },
                    name="Hydrogen Stability Line",
                ))
            # oxygen line
            data.append(
                go.Scatter(
                    x=[ph_range[0], ph_range[1]],
                    y=[
                        -ph_range[0] * PREFAC + 1.23,
                        -ph_range[1] * PREFAC + 1.23
                    ],
                    mode="lines",
                    line={
                        "color": "orange",
                        "dash": "dash"
                    },
                    name="Oxygen Stability Line",
                ))

            #     h_line = np.transpose([[xlim[0], -xlim[0] * PREFAC], [xlim[1], -xlim[1] * PREFAC]])
            #     o_line = np.transpose([[xlim[0], -xlim[0] * PREFAC + 1.23], [xlim[1], -xlim[1] * PREFAC + 1.23]])
            # #   SHE line
            # #   data.append(go.Scatter(
            # #
            # #   ))

        figure = go.Figure(data=data, layout=layout)

        return figure