Source code for pyphyschemtools.PeriodicTable

############################################################
#                       Periodic Table
############################################################
from .visualID_Eng import fg, bg, hl
from .core import centerTitle, centertxt

import mendeleev

[docs] class TableauPeriodique: """ Classe permettant de manipuler et d'afficher les données du tableau périodique. Cette classe utilise la bibliothèque 'mendeleev' pour récupérer les données chimiques et 'bokeh' pour la visualisation interactive. Elle francise les noms et corrige certaines classifications de familles chimiques. Initialise l'objet TableauPeriodique en chargeant les données de la bibliothèque mendeleev. """ nomsFr=['Hydrogène','Hélium','Lithium','Béryllium','Bore','Carbone','Azote','Oxygène', 'Fluor','Néon','Sodium','Magnésium','Aluminium','Silicium','Phosphore','Soufre', 'Chlore','Argon','Potassium','Calcium','Scandium','Titane','Vanadium','Chrome', 'Manganèse','Fer','Cobalt','Nickel','Cuivre','Zinc','Gallium','Germanium', 'Arsenic','Sélénium','Brome','Krypton','Rubidium','Strontium','Yttrium', 'Zirconium','Niobium','Molybdène','Technétium','Ruthénium','Rhodium', 'Palladium','Argent','Cadmium','Indium', 'Étain','Antimoine','Tellure','Iode','Xénon','Césium','Baryum','Lanthane','Cérium', 'Praséodyme','Néodyme','Prométhium','Samarium','Europium','Gadolinium','Terbium', 'Dysprosium','Holmium','Erbium','Thulium','Ytterbium','Lutetium','Hafnium','Tantale', 'Tungstène','Rhénium','Osmium','Iridium','Platine','Or','Mercure','Thallium','Plomb', 'Bismuth','Polonium','Astate','Radon','Francium','Radium','Actinium','Thorium','Protactinium', 'Uranium','Neptunium','Plutonium','Americium','Curium','Berkelium','Californium','Einsteinium', 'Fermium','Mendelevium','Nobelium','Lawrencium','Rutherfordium','Dubnium','Seaborgium','Bohrium', 'Hassium','Meitnerium','Darmstadtium','Roentgenium','Copernicium','Nihonium','Flerovium', 'Moscovium','Livermorium','Tennesse','Oganesson', ] trad = {'Nonmetals':'Non métal', 'Noble gases':'Gaz noble', 'Alkali metals':'Métal alcalin', 'Alkaline earth metals':'Métal alcalino-terreux', 'Metalloids':'Métalloïde', 'Halogens':'Halogène', 'Poor metals':'Métal pauvre', 'Transition metals':'Métal de transition', 'Lanthanides':'Lanthanide', 'Actinides':'Actinide', 'Metals':'Métal', } def __init__(self): from mendeleev.vis import create_vis_dataframe self.elements = create_vis_dataframe() self.patch_elements()
[docs] def patch_elements(self): ''' Ce patch, appliqué à self.elements, créé par l'appel à create_vis_dataframe(), va servir à : - ajouter des informations en français : les noms des éléments et des séries (familles) auxquelles ils appartiennent - retirer les éléments du groupe 12 de la famille des métaux de transition, qui est le choix CONTESTABLE par défaut de la bibliothèque mendeleev input : elements est un dataframe pandas préalablement créé par la fonction create_vis_dataframe() de mendeleev.vis output : elements avec deux nouvelles colonnes name_seriesFr et nom, qui contient dorénavant les noms des éléments en français + correction des données name_series et series_id pour les éléments Zn, Cd, Hg, Cn + de nouvelles colonnes qui contiennent l'énergie de première ionisation et les isotopes naturels ''' def series_eng2fr(s): '''Correspondance entre nom des séries (familles) en anglais et en français''' s = TableauPeriodique.trad[s] return s def name_eng2fr(): """Remplace les noms anglais des éléments par leurs équivalents français.""" self.elements["nom"] = TableauPeriodique.nomsFr return def ajouter_donnees(): """Récupère et fusionne des données supplémentaires comme l'énergie de première ionisation.""" import numpy as np from mendeleev.fetch import fetch_table, fetch_ionization_energies import pandas as pd # dfElts = fetch_table("elements") # display(dfElts) dfEi1 = fetch_ionization_energies(degree = 1) # display(dfEi1) b = pd.DataFrame({'atomic_number':[x for x in range(1, 119)]}) dfEi1tot = pd.merge(left=dfEi1, right=b, on='atomic_number', how='outer').sort_values(by='atomic_number') self.elements["Ei1"] = dfEi1tot["IE1"] # les éléments du groupe 12 ne sont pas des métaux de transition self.elements.loc[29,"name_series"] = 'Metals' self.elements.loc[47,"name_series"] = 'Metals' self.elements.loc[79,"name_series"] = 'Metals' self.elements.loc[111,"name_series"] = 'Metals' self.elements.loc[29,"series_id"] = 11 self.elements.loc[47,"series_id"] = 11 self.elements.loc[79,"series_id"] = 11 self.elements.loc[111,"series_id"] = 11 self.elements.loc[29,"color"] = "#bbd3a5" self.elements.loc[47,"color"] = "#bbd3a5" self.elements.loc[79,"color"] = "#bbd3a5" self.elements.loc[111,"color"] = "#bbd3a5" # english > français. Ajout d'une nouvelle colonne self.elements["name_seriesFr"] = self.elements["name_series"].apply(series_eng2fr) # english > français. Noms des éléments en français changés dans la colonne name name_eng2fr() ajouter_donnees() return
[docs] def prop(self,elt_id): """ Affiche les propriétés détaillées d'un élément chimique. Args: elt_id (str ou int): Symbole de l'élément (ex: 'O') ou numéro atomique (ex: 8). """ from mendeleev import element elt = element(elt_id) print(f"Nom de l'élement = {TableauPeriodique.nomsFr[elt.atomic_number-1]} ({elt.symbol}, Z = {elt.atomic_number})") print(f"Nom en anglais = {elt.name}") print(f"Origine du nom = {elt.name_origin}") print() print(f"CEF = {elt.ec} = {elt.econf}") print(f"Nombre d'électrons célibataires = {elt.ec.unpaired_electrons()}") print(f"Groupe {elt.group_id}, Période {elt.period}, bloc {elt.block}") print(f"Famille = {self.elements.loc[elt.atomic_number-1,'name_seriesFr']}") print() print(f"Masse molaire = {elt.atomic_weight} g/mol") isotopes = "" X = elt.symbol for i in elt.isotopes: if i.abundance is not None: isotopes = isotopes + str(i.mass_number)+ "^" + X + f"({i.abundance}%) / " print("Isotopes naturels = ",isotopes[:-2]) print() if elt.electronegativity(scale='pauling') is None: print(f"Électronégativité de Pauling = Non définie") else: print(f"Électronégativité de Pauling = {elt.electronegativity(scale='pauling')}") print(f"Énergie de 1ère ionisation = {elt.ionenergies[1]:.2f} eV") if elt.electron_affinity is None: print(f"Affinité électronique = Non définie") else: print(f"Afinité électronique = {elt.electron_affinity:.2f} eV") print(f"Rayon atomique = {elt.atomic_radius:.1f} pm") print() print("▶ Description : ",elt.description) print("▶ Sources : ",elt.sources) print("▶ Utilisation : ",elt.uses) print("---------------------------------------------------------------------------------------") print()
[docs] def afficher(self): """ Génère et affiche le tableau périodique interactif dans un notebook Jupyter. Le tableau permet de visualiser les propriétés au survol de la souris. """ from bokeh.plotting import show, output_notebook from mendeleev.vis import periodic_table_bokeh # Toute cette partie du code est une copie du module bokeh de mendeleev.vis # La fonction periodic_table_bokeh étant faiblement configurable avec des args/kwargs, # elle est adaptée ici pour un affichage personnalisé from collections import OrderedDict import pandas as pd from pandas.api.types import is_float_dtype from bokeh.plotting import figure from bokeh.models import HoverTool, ColumnDataSource, FixedTicker from mendeleev.vis.utils import colormap_column def periodic_table_bokeh( elements: pd.DataFrame, attribute: str = "atomic_weight", cmap: str = "RdBu_r", colorby: str = "color", decimals: int = 3, height: int = 800, missing: str = "#ffffff", title: str = "Periodic Table", wide_layout: bool = False, width: int = 1200, ): """ Use Bokeh backend to plot the periodic table. Adaptation by Romuald Poteau (romuald.poteau@univ-tlse3.fr) of the orignal periodic_table_bokeh() function of the mendeleev library Args: elements : Pandas DataFrame with the elements data. Needs to have `x` and `y` columns with coordianates for each tile. attribute : Name of the attribute to be displayed cmap : Colormap to use, see matplotlib colormaps colorby : Name of the column containig the colors decimals : Number of decimals to be displayed in the bottom row of each cell height : Height of the figure in pixels missing : Hex code of the color to be used for the missing values title : Title to appear above the periodic table wide_layout: wide layout variant of the periodic table width : Width of the figure in pixels """ if any(col not in elements.columns for col in ["x", "y"]): raise ValueError( "Coordinate columns named 'x' and 'y' are required " "in 'elements' DataFrame. Consider using " "'mendeleev.vis.utils.create_vis_dataframe' and try again." ) # additional columns for positioning of the text elements.loc[:, "y_anumber"] = elements["y"] - 0.3 elements.loc[:, "y_name"] = elements["y"] + 0.2 if attribute: elements.loc[elements[attribute].notnull(), "y_prop"] = ( elements.loc[elements[attribute].notnull(), "y"] + 0.35 ) else: elements.loc[:, "y_prop"] = elements["y"] + 0.35 ac = "display_attribute" if is_float_dtype(elements[attribute]): elements[ac] = elements[attribute].round(decimals=decimals) else: elements[ac] = elements[attribute] if colorby == "attribute": colored = colormap_column(elements, attribute, cmap=cmap, missing=missing) elements.loc[:, "attribute_color"] = colored colorby = "attribute_color" # bokeh configuration source = ColumnDataSource(data=elements) TOOLS = "hover,save,reset" fig = figure( title=title, tools=TOOLS, x_axis_location="above", x_range=(elements.x.min() - 0.5, elements.x.max() + 0.5), y_range=(elements.y.max() + 0.5, elements.y.min() - 0.5), width=width, height=height, toolbar_location="above", toolbar_sticky=False, ) fig.rect("x", "y", 0.9, 0.9, source=source, color=colorby, fill_alpha=0.6) # adjust the ticks and axis bounds fig.yaxis.bounds = (1, 7) fig.axis[1].ticker.num_minor_ticks = 0 if wide_layout: # Turn off tick labels fig.axis[0].major_label_text_font_size = "0pt" # Turn off tick marks fig.axis[0].major_tick_line_color = None # turn off major ticks fig.axis[0].ticker.num_minor_ticks = 0 # turn off minor ticks else: fig.axis[0].ticker = FixedTicker(ticks=list(range(1, 19))) text_props = { "source": source, "angle": 0, "color": "black", "text_align": "center", "text_baseline": "middle", } fig.text( x="x", y="y", text="symbol", text_font_style="bold", text_font_size="15pt", **text_props, ) fig.text( x="x", y="y_anumber", text="atomic_number", text_font_size="9pt", **text_props ) fig.text(x="x", y="y_name", text="name", text_font_size="6pt", **text_props) fig.text(x="x", y="y_prop", text=ac, text_font_size="7pt", **text_props) fig.grid.grid_line_color = None hover = fig.select(dict(type=HoverTool)) hover.tooltips = OrderedDict( [ ("nom", "@nom"), ("name", "@name"), ("famille", "@name_seriesFr"), ("numéro atomique", "@atomic_number"), ("masse molaire", "@atomic_weight"), ("rayon atomique", "@atomic_radius"), ("énergie de première ionisation", "@Ei1"), ("affinité électronique", "@electron_affinity"), ("EN Pauling", "@en_pauling"), ("CEF", "@electronic_configuration"), ] ) return fig output_notebook() fig = periodic_table_bokeh(self.elements, colorby="color") show(fig)