From d1083c975fccb9e134f43bf196d661006ce34681 Mon Sep 17 00:00:00 2001 From: Sean Kavanagh Date: Fri, 27 Oct 2023 11:27:24 +0100 Subject: [PATCH 1/3] Add py-sc-fermi draft tutorial to docs --- docs/Tutorials.rst | 3 ++- docs/interface_py_sc_fermi.ipynb | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) create mode 120000 docs/interface_py_sc_fermi.ipynb diff --git a/docs/Tutorials.rst b/docs/Tutorials.rst index 5124e3f7..c6303d90 100644 --- a/docs/Tutorials.rst +++ b/docs/Tutorials.rst @@ -12,4 +12,5 @@ tutorials: YT_defects_tutorial dope_workflow_example dope_parsing_example - dope_chemical_potentials \ No newline at end of file + dope_chemical_potentials + interface_py_sc_fermi \ No newline at end of file diff --git a/docs/interface_py_sc_fermi.ipynb b/docs/interface_py_sc_fermi.ipynb new file mode 120000 index 00000000..c700596a --- /dev/null +++ b/docs/interface_py_sc_fermi.ipynb @@ -0,0 +1 @@ +../examples/interface_py_sc_fermi.ipynb \ No newline at end of file From 0ede233d52a7d88c6aaaaab2a102051e7e40de7b Mon Sep 17 00:00:00 2001 From: Sean Kavanagh Date: Fri, 27 Oct 2023 11:49:04 +0100 Subject: [PATCH 2/3] Small default value and docstring update --- doped/interface/py_sc_fermi.py | 117 ++++++++++++++++++++------------- 1 file changed, 70 insertions(+), 47 deletions(-) diff --git a/doped/interface/py_sc_fermi.py b/doped/interface/py_sc_fermi.py index cb2bc246..126fc379 100644 --- a/doped/interface/py_sc_fermi.py +++ b/doped/interface/py_sc_fermi.py @@ -1,16 +1,18 @@ -from dataclasses import dataclass, field -from doped.utils.legacy_pmg.thermodynamics import DefectPhaseDiagram from copy import deepcopy -from typing import List, Dict, Union, Tuple, Optional -from scipy.spatial import Delaunay, ConvexHull -from tqdm import tqdm -import pandas as pd +from dataclasses import dataclass, field +from typing import Dict, List, Optional + import numpy as np +import pandas as pd +from scipy.spatial import ConvexHull, Delaunay +from tqdm import tqdm + +from doped.utils.legacy_pmg.thermodynamics import DefectPhaseDiagram try: - from py_sc_fermi.dos import DOS - from py_sc_fermi.defect_system import DefectSystem from py_sc_fermi.defect_species import DefectSpecies + from py_sc_fermi.defect_system import DefectSystem + from py_sc_fermi.dos import DOS except ImportError: raise ImportError( "Please install py-sc-fermi via `pip install py-sc-fermi` to use this functionality." @@ -18,7 +20,8 @@ def _get_label_and_charge(name: str) -> tuple: - """Extracts the label and charge from a defect name string. + """ + Extracts the label and charge from a defect name string. Args: name (str): Name of the defect. @@ -38,7 +41,9 @@ class FermiSolver: bulk_vasprun: str def __post_init__(self) -> None: - """Initializes additional attributes after dataclass instantiation.""" + """ + Initializes additional attributes after dataclass instantiation. + """ self.bulk_dos = DOS.from_vasprun(self.bulk_vasprun) self.volume = self.defect_phase_diagram.entries[0].defect.structure.volume @@ -58,7 +63,9 @@ def _defect_picker(self, name: str): # Type hint should represent the actual ty return defect def _generate_defect_system(self, temperature: float, chemical_potentials: dict) -> DefectSystem: - """Generates a DefectSystem object from the DefectPhaseDiagram and a set of chemical potentials. + """ + Generates a DefectSystem object from the DefectPhaseDiagram and a set + of chemical potentials. Args: temperature (float): Temperature in K. @@ -97,8 +104,10 @@ def _generate_defect_system(self, temperature: float, chemical_potentials: dict) def defect_system_from_chemical_potentials( self, chemical_potentials: dict, temperature: float = 300.0 ) -> DefectSystem: - """Updates the energies of the DefectSystem with a new set of chemical potentials.""" - + """ + Updates the energies of the DefectSystem with a new set of chemical + potentials. + """ defect_system = self._generate_defect_system( temperature=temperature, chemical_potentials=chemical_potentials ) @@ -120,12 +129,13 @@ def scan_temperature_and_save( exceptions: List[str] = [], ) -> pd.DataFrame: """ - Scans a range of temperatures and saves the concentration_dict() to a DataFrame. + Scans a range of temperatures and returns the concentration_dict() as a + DataFrame. Args: temp_range (List[float]): List of temperatures to scan. fix_concentration_temp (float, optional): The temperature at which to fix concentrations. Defaults to None. - level (str, optional): The level at which to fix concentrations. Defaults to "DefectChargeState". + level (str, optional): The level at which to fix concentrations. Defaults to "DefectSpecies". exceptions (List[str], optional): List of species or charge states to exclude from fixing. Defaults to None. Returns: @@ -157,7 +167,8 @@ def scan_temperature_and_save( return pd.merge(concentration_df, fermi_level_df, on="Temperature") def _get_concentrations(self, defect_system: DefectSystem): - """_summary_ + """ + _summary_. Args: defect_system (DefectSystem): _description_ @@ -165,7 +176,6 @@ def _get_concentrations(self, defect_system: DefectSystem): Returns: _type_: _description_ """ - conc_dict = defect_system.concentration_dict() concentration_data = [] @@ -197,10 +207,12 @@ def interpolate_chemical_potentials_and_save( n_points: int, temp: float = 300.0, anneal_temp: Optional[float] = None, - level: str = "DefectChargeState", + level: str = "DefectSpecies", exceptions: List[str] = [], ) -> pd.DataFrame: - """Scans a range of chemical potentials and saves the concentration_dict() to a DataFrame. + """ + Scans a range of chemical potentials and returns the + concentration_dict() as a DataFrame. Args: chem_pot_start (dict): Dictionary of starting chemical potentials. @@ -227,7 +239,7 @@ def interpolate_chemical_potentials_and_save( ) else: updated_system = self.generate_annealed_defect_system( - mu = chem_pot_interpolated, + mu=chem_pot_interpolated, annealing_temperature=anneal_temp, target_temperature=temp, level=level, @@ -244,11 +256,7 @@ def interpolate_chemical_potentials_and_save( concentration_df = pd.DataFrame(all_concentration_data) fermi_level_df = pd.DataFrame(all_fermi_level_data) - return pd.merge( - concentration_df, - fermi_level_df, - on=["Interpolation_Parameter"] - ) + return pd.merge(concentration_df, fermi_level_df, on=["Interpolation_Parameter"]) def generate_annealed_defect_system( self, @@ -288,59 +296,70 @@ def generate_annealed_defect_system( elif level == "DefectChargeState": for k, v in defect_species.charge_states.items(): key = f"{defect_species.name}_{int(k)}" - if key in [k for k in fixed_concs.keys()]: + if key in list(fixed_concs.keys()): v.fix_concentration(fixed_concs[key] / 1e24 * self.volume) target_system = self.update_defect_system_temperature(initial_system, target_temperature) return target_system def chempot_grid(self, chemical_potentials, num_points=10, num_points_along_edge=5): - """generate a grid of chemical potentials. + """ + Generate a grid of chemical potentials. Args: chemical_potenials ([type]): [description] """ - return ChemicalPotentialGrid(chemical_potentials, num_points_along_edge=num_points_along_edge, num_points=num_points).get_grid() + return ChemicalPotentialGrid( + chemical_potentials, num_points_along_edge=num_points_along_edge, num_points=num_points + ).get_grid() def grid_solve( - self, chempot_grid, temperature=300.0, anneal_temperature=None, level="DefectSpecies", exceptions=[] + self, + chempot_grid, + temperature=300.0, + anneal_temperature=None, + level="DefectSpecies", + exceptions=[], ): """ Args: - chempot_grid (_type_): _description_ + chempot_grid (_type_): _description_. Returns: _type_: _description_ """ all_concentration_data = [] all_fermi_level_data = [] - for i, row in tqdm(chempot_grid.iterrows()): - row.drop(["is_vertex", "facet"], inplace=True) + for _i, row in tqdm(chempot_grid.iterrows()): + row = row.drop(["is_vertex", "facet"]) chemical_potentials = row.to_dict() if anneal_temperature: - live_system = self.generate_annealed_defect_system(chemical_potentials, anneal_temperature, temperature, level, exceptions) + live_system = self.generate_annealed_defect_system( + chemical_potentials, anneal_temperature, temperature, level, exceptions + ) else: - live_system = self.defect_system_from_chemical_potentials(chemical_potentials, temperature=temperature) + live_system = self.defect_system_from_chemical_potentials( + chemical_potentials, temperature=temperature + ) concentration_data, fermi_level_data = self._get_concentrations(live_system) [d.update(chemical_potentials) for d in concentration_data] [d.update(chemical_potentials) for d in fermi_level_data] all_concentration_data.extend(concentration_data) all_fermi_level_data.extend(fermi_level_data) - + concentration_df = pd.DataFrame(all_concentration_data) fermi_level_df = pd.DataFrame(all_fermi_level_data) return pd.merge( concentration_df, fermi_level_df, - on=["Temperature"] + list(chemical_potentials.keys()), + on=["Temperature", *list(chemical_potentials.keys())], ) - @dataclass class ChemicalPotentialGrid: chemical_potentials: dict @@ -356,7 +375,9 @@ def __post_init__(self): self._generate_internal_grid() def _initialize_vertices(self): - vertices = [np.array(list(facet.values())) for facet in self.chemical_potentials["facets"].values()] + vertices = [ + np.array(list(facet.values())) for facet in self.chemical_potentials["facets"].values() + ] return np.array(vertices) def _generate_grid_from_vertices(self): @@ -371,9 +392,9 @@ def _generate_grid_from_vertices(self): distance_covered = 0 total_perimeter = sum( - np.linalg.norm(self.vertices[simplex[i]] - self.vertices[simplex[j]]) - for simplex in hull.simplices - for i in range(len(simplex)) + np.linalg.norm(self.vertices[simplex[i]] - self.vertices[simplex[j]]) + for simplex in hull.simplices + for i in range(len(simplex)) for j in range(i + 1, len(simplex)) ) @@ -385,16 +406,16 @@ def _generate_grid_from_vertices(self): point1 = self.vertices[simplex[i]] point2 = self.vertices[simplex[j]] edge_length = np.linalg.norm(point2 - point1) - + num_points_on_edge = int( - np.floor((distance_covered + edge_length) / spacing) + np.floor((distance_covered + edge_length) / spacing) - np.floor(distance_covered / spacing) ) - + if num_points_on_edge > 0: edge_points = np.linspace(point1, point2, num_points_on_edge + 2)[1:-1] grid_points.extend(edge_points) - + distance_covered += edge_length self.grid_points = np.array(grid_points) @@ -433,6 +454,8 @@ def get_grid(self) -> pd.DataFrame: vertices_df["facet"] = self.chemical_potentials["facets"].keys() df = pd.concat([grid_df, internal_grid_df, vertices_df], ignore_index=True) - grid_df = df.drop_duplicates(subset=list(list(self.chemical_potentials["facets"].values())[0].keys()) + ["is_vertex"]) + grid_df = df.drop_duplicates( + subset=[*list(list(self.chemical_potentials["facets"].values())[0].keys()), "is_vertex"] + ) - return grid_df \ No newline at end of file + return grid_df From 53883a1f206376c50ac3fc7b5a2908762a34422b Mon Sep 17 00:00:00 2001 From: alexquires Date: Tue, 31 Oct 2023 11:15:14 +0000 Subject: [PATCH 3/3] catch up --- examples/interface_py_sc_fermi.ipynb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/interface_py_sc_fermi.ipynb b/examples/interface_py_sc_fermi.ipynb index 81a95498..f18fa92c 100644 --- a/examples/interface_py_sc_fermi.ipynb +++ b/examples/interface_py_sc_fermi.ipynb @@ -424,7 +424,7 @@ "# define a range of chemical potentials to scan over\n", "chem_pot_df = fs.interpolate_chemical_potentials_and_save(chem_pot_start=chemical_potentials[\"facets\"][\"Cu2SiSe3-Cu2Se-Cu\"],\n", " chem_pot_end=chemical_potentials[\"facets\"][\"Cu2SiSe3-SiSe2-Se\"],\n", - " n_points=100, temp=300, anneal_temp=1000)\n", + " n_points=100, temp=300)\n", "\n", "# Create a unique list of defects\n", "unique_defects = chem_pot_df['Defect'].unique()\n", @@ -1679,23 +1679,23 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 1, "metadata": {}, "outputs": [ { "ename": "NameError", - "evalue": "name 'sliced_df' is not defined", + "evalue": "name 'concs' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[1;32m/Users/alex/work/doped/examples/interface_py_sc_fermi.ipynb Cell 20\u001b[0m line \u001b[0;36m1\n\u001b[0;32m----> 1\u001b[0m \u001b[39mprint\u001b[39m(sliced_df)\n", - "\u001b[0;31mNameError\u001b[0m: name 'sliced_df' is not defined" + "\u001b[1;32m/Users/alex/work/doped/examples/interface_py_sc_fermi.ipynb Cell 20\u001b[0m line \u001b[0;36m1\n\u001b[0;32m----> 1\u001b[0m \u001b[39mprint\u001b[39m(concs)\n", + "\u001b[0;31mNameError\u001b[0m: name 'concs' is not defined" ] } ], "source": [ - "print(sliced_df)" + "print(concs)" ] }, {