From 78a9356777f0eadceda5e334c8643bf452d4a776 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Fri, 12 Jul 2024 18:25:23 +0100 Subject: [PATCH 01/44] Add magpie data to data_loader --- smact/data_loader.py | 82 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/smact/data_loader.py b/smact/data_loader.py index 79e0dd8a..5e50c7f4 100644 --- a/smact/data_loader.py +++ b/smact/data_loader.py @@ -13,6 +13,8 @@ import csv import os +import pandas as pd + from smact import data_directory # Module-level switch: print "verbose" warning messages @@ -822,3 +824,83 @@ def lookup_element_sse_pauling_data(symbol): ) return None + + +_element_magpie_data = None + + +def lookup_element_magpie_data(symbol: str, copy: bool = True): + """ + Retrieve element data contained in the Magpie representation. + + Taken from Ward, L., Agrawal, A., Choudhary, A. et al. + A general-purpose machine learning framework for + predicting properties of inorganic materials. + npj Comput Mater 2, 16028 (2016). + https://doi.org/10.1038/npjcompumats.2016.28 + + Args: + symbol : the atomic symbol of the element to look up. + copy: if True (default), return a copy of the data dictionary, + rather than a reference to a cached object -- only use + copy=False in performance-sensitive code and where you are + certain the dictionary will not be modified! + + Returns: + list: + Magpie features. + Returns None if the element was not found among the external + data. + + Magpie features are dictionaries with the keys: + + + + + """ + + global _element_magpie_data + + if _element_magpie_data is None: + _element_magpie_data = {} + + df = pd.read_csv(os.path.join(data_directory, "magpie.csv")) + for index, row in df.iterrows(): + key = row.iloc[0] + + dataset = { + "Number": int(row.iloc[1]), + "MendeleevNumber": int(row.iloc[2]), + "AtomicWeight": float(row.iloc[3]), + "MeltingT": float(row.iloc[4]), + "Column": int(row.iloc[5]), + "Row": int(row.iloc[6]), + "CovalentRadius": float(row.iloc[7]), + "Electronegativity": float(row.iloc[8]), + "NsValence": int(row.iloc[9]), + "NpValence": int(row.iloc[10]), + "NdValence": int(row.iloc[11]), + "NfValence": int(row.iloc[12]), + "NValence": int(row.iloc[13]), + "NsUnfilled": int(row.iloc[14]), + "NpUnfilled": int(row.iloc[15]), + "NdUnfilled": int(row.iloc[16]), + "NfUnfilled": int(row.iloc[17]), + "NUnfilled": int(row.iloc[18]), + "GSvolume_pa": float(row.iloc[19]), + "GSbandgap": float(row.iloc[20]), + "GSmagmom": float(row.iloc[21]), + "SpaceGroupNumber": int(row.iloc[22]), + } + _element_magpie_data[key] = dataset + + if symbol in _element_magpie_data: + return _element_magpie_data[symbol] + else: + if _print_warnings: + print( + "WARNING: Magpie data for element {} not " + "found.".format(symbol) + ) + + return None From edbb91a894a852539bf3fc6e778f62477602cb4c Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Fri, 12 Jul 2024 18:25:32 +0100 Subject: [PATCH 02/44] Add magpie properties --- smact/__init__.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/smact/__init__.py b/smact/__init__.py index e19dc51b..9d358a95 100644 --- a/smact/__init__.py +++ b/smact/__init__.py @@ -73,6 +73,16 @@ class Element: Element.HHI_r (float) : Hirfindahl-Hirschman Index for elemental reserves + Element.mendeleev (int): Mendeleev number + + Element.AtomicWeight (float): Atomic weight + + Element.MeltingT (float): Melting temperature in K + + Element.num_valence (int): Number of valence electrons + + + Raises: NameError: Element not found in element.txt Warning: Element not found in Eigenvalues.csv @@ -140,6 +150,18 @@ def __init__( else: sse_Pauling = None + magpie_data = data_loader.lookup_element_magpie_data(symbol) + if magpie_data: + mendeleev = magpie_data["MendeleevNumber"] + AtomicWeight = magpie_data["AtomicWeight"] + MeltingT = magpie_data["MeltingT"] + num_valence = magpie_data["NValence"] + else: + mendeleev = None + AtomicWeight = None + MeltingT = None + num_valence = None + for attribute, value in ( ("coord_envs", coord_envs), ("covalent_radius", dataset["r_cov"]), @@ -174,6 +196,10 @@ def __init__( ("SSE", sse), ("SSEPauling", sse_Pauling), ("symbol", symbol), + ("mendeleev", mendeleev), + ("AtomicWeight", AtomicWeight), + ("MeltingT", MeltingT), + ("num_valence", num_valence), # ('vdw_radius', dataset['RVdW']), ): setattr(self, attribute, value) From 0f3355f166ff20a16582923e65efcc954b859c35 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Fri, 12 Jul 2024 18:25:43 +0100 Subject: [PATCH 03/44] Add magpie data --- smact/data/magpie.csv | 98 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 smact/data/magpie.csv diff --git a/smact/data/magpie.csv b/smact/data/magpie.csv new file mode 100644 index 00000000..9c75e52e --- /dev/null +++ b/smact/data/magpie.csv @@ -0,0 +1,98 @@ +element,Number,MendeleevNumber,AtomicWeight,MeltingT,Column,Row,CovalentRadius,Electronegativity,NsValence,NpValence,NdValence,NfValence,NValence,NsUnfilled,NpUnfilled,NdUnfilled,NfUnfilled,NUnfilled,GSvolume_pa,GSbandgap,GSmagmom,SpaceGroupNumber +H,1.0,92.0,1.00794,14.01,1.0,1.0,31.0,2.2,1.0,0.0,0.0,0.0,1.0,1.0,0.0,0.0,0.0,1.0,6.615,7.853,0.0,194.0 +He,2.0,98.0,4.002602,1211.4,18.0,1.0,28.0,1.63,2.0,0.0,0.0,0.0,2.0,0.0,0.0,0.0,0.0,0.0,12.305,18.098,0.0,225.0 +Li,3.0,1.0,6.941,453.69,1.0,2.0,128.0,0.98,1.0,0.0,0.0,0.0,1.0,1.0,0.0,0.0,0.0,1.0,16.5933333333,0.0,0.0,229.0 +Be,4.0,67.0,9.012182,1560.0,2.0,2.0,96.0,1.57,2.0,0.0,0.0,0.0,2.0,0.0,0.0,0.0,0.0,0.0,7.89,0.0,0.0,194.0 +B,5.0,72.0,10.811,2348.0,13.0,2.0,84.0,2.04,2.0,1.0,0.0,0.0,3.0,0.0,5.0,0.0,0.0,5.0,7.1725,1.524,0.0,166.0 +C,6.0,77.0,12.0107,3823.0,14.0,2.0,76.0,2.55,2.0,2.0,0.0,0.0,4.0,0.0,4.0,0.0,0.0,4.0,5.64,4.496,0.0,194.0 +N,7.0,82.0,14.0067,63.05,15.0,2.0,71.0,3.04,2.0,3.0,0.0,0.0,5.0,0.0,3.0,0.0,0.0,3.0,14.76875,6.437,0.0,194.0 +O,8.0,87.0,15.9994,54.8,16.0,2.0,66.0,3.44,2.0,4.0,0.0,0.0,6.0,0.0,2.0,0.0,0.0,2.0,9.105,0.0,0.0,12.0 +F,9.0,93.0,18.9984032,53.5,17.0,2.0,57.0,3.98,2.0,5.0,0.0,0.0,7.0,0.0,1.0,0.0,0.0,1.0,9.7075,1.97,0.0,15.0 +Ne,10.0,99.0,20.1791,24.56,18.0,2.0,58.0,1.63,2.0,6.0,0.0,0.0,8.0,0.0,0.0,0.0,0.0,0.0,12.64,13.088,0.0,225.0 +Na,11.0,2.0,22.98976928,370.87,1.0,3.0,166.0,0.93,1.0,0.0,0.0,0.0,1.0,1.0,0.0,0.0,0.0,1.0,29.2433333333,0.0,0.0,229.0 +Mg,12.0,68.0,24.305,923.0,2.0,3.0,141.0,1.31,2.0,0.0,0.0,0.0,2.0,0.0,0.0,0.0,0.0,0.0,22.89,0.0,0.0,194.0 +Al,13.0,73.0,26.9815386,933.47,13.0,3.0,121.0,1.61,2.0,1.0,0.0,0.0,3.0,0.0,5.0,0.0,0.0,5.0,16.48,0.0,0.0,225.0 +Si,14.0,78.0,28.0855,1687.0,14.0,3.0,111.0,1.9,2.0,2.0,0.0,0.0,4.0,0.0,4.0,0.0,0.0,4.0,20.44,0.773,0.0,227.0 +P,15.0,83.0,30.973762,317.3,15.0,3.0,107.0,2.19,2.0,3.0,0.0,0.0,5.0,0.0,3.0,0.0,0.0,3.0,22.5702380952,1.625,0.0,2.0 +S,16.0,88.0,32.065,388.36,16.0,3.0,105.0,2.58,2.0,4.0,0.0,0.0,6.0,0.0,2.0,0.0,0.0,2.0,25.786875,2.202,0.0,70.0 +Cl,17.0,94.0,35.453,171.6,17.0,3.0,102.0,3.16,2.0,5.0,0.0,0.0,7.0,0.0,1.0,0.0,0.0,1.0,24.4975,2.493,0.0,64.0 +Ar,18.0,100.0,39.948,83.8,18.0,3.0,106.0,1.63,2.0,6.0,0.0,0.0,8.0,0.0,0.0,0.0,0.0,0.0,28.54,9.26,0.0,225.0 +K,19.0,3.0,39.0983,336.53,1.0,4.0,203.0,0.82,1.0,0.0,0.0,0.0,1.0,1.0,0.0,0.0,0.0,1.0,73.1066666667,0.0,0.0,229.0 +Ca,20.0,7.0,40.078,1115.0,2.0,4.0,176.0,1.0,2.0,0.0,0.0,0.0,2.0,0.0,0.0,0.0,0.0,0.0,37.77,0.0,0.0,225.0 +Sc,21.0,11.0,44.955912,1814.0,3.0,4.0,170.0,1.36,2.0,0.0,1.0,0.0,3.0,0.0,0.0,9.0,0.0,9.0,22.235,0.0,6.35e-06,194.0 +Ti,22.0,43.0,47.867,1941.0,4.0,4.0,160.0,1.54,2.0,0.0,2.0,0.0,4.0,0.0,0.0,8.0,0.0,8.0,16.69,0.0,2.25333333333e-05,194.0 +V,23.0,46.0,50.9415,2183.0,5.0,4.0,153.0,1.63,2.0,0.0,3.0,0.0,5.0,0.0,0.0,7.0,0.0,7.0,13.01,0.0,0.0,229.0 +Cr,24.0,49.0,51.9961,2180.0,6.0,4.0,139.0,1.66,1.0,0.0,5.0,0.0,6.0,1.0,0.0,5.0,0.0,6.0,11.19,0.0,0.0,229.0 +Mn,25.0,52.0,54.938045,1519.0,7.0,4.0,139.0,1.55,2.0,0.0,5.0,0.0,7.0,0.0,0.0,5.0,0.0,5.0,10.4875862069,0.0,0.000310120689655,217.0 +Fe,26.0,55.0,55.845,1811.0,8.0,4.0,132.0,1.83,2.0,0.0,6.0,0.0,8.0,0.0,0.0,4.0,0.0,4.0,10.73,0.0,2.1106628,229.0 +Co,27.0,58.0,58.933195,1768.0,9.0,4.0,126.0,1.88,2.0,0.0,7.0,0.0,9.0,0.0,0.0,3.0,0.0,3.0,10.245,0.0,1.5484712,194.0 +Ni,28.0,61.0,58.6934,1728.0,10.0,4.0,124.0,1.91,2.0,0.0,8.0,0.0,10.0,0.0,0.0,2.0,0.0,2.0,10.32,0.0,0.5953947,225.0 +Cu,29.0,64.0,63.546,1357.77,11.0,4.0,132.0,1.9,1.0,0.0,10.0,0.0,11.0,1.0,0.0,0.0,0.0,1.0,11.07,0.0,0.0,225.0 +Zn,30.0,69.0,65.38,692.68,12.0,4.0,122.0,1.65,2.0,0.0,10.0,0.0,12.0,0.0,0.0,0.0,0.0,0.0,13.96,0.0,0.0,194.0 +Ga,31.0,74.0,69.723,302.91,13.0,4.0,122.0,1.81,2.0,1.0,10.0,0.0,13.0,0.0,5.0,0.0,0.0,5.0,18.8575,0.0,0.0,64.0 +Ge,32.0,79.0,72.64,1211.4,14.0,4.0,120.0,2.01,2.0,2.0,10.0,0.0,14.0,0.0,4.0,0.0,0.0,4.0,23.005,0.383,0.0,225.0 +As,33.0,84.0,74.9216,1090.0,15.0,4.0,119.0,2.18,2.0,3.0,10.0,0.0,15.0,0.0,3.0,0.0,0.0,3.0,22.175,0.0,0.0,166.0 +Se,34.0,89.0,78.96,494.0,16.0,4.0,120.0,2.55,2.0,4.0,10.0,0.0,16.0,0.0,2.0,0.0,0.0,2.0,25.92,0.799,0.0,14.0 +Br,35.0,95.0,79.904,265.8,17.0,4.0,120.0,2.96,2.0,5.0,10.0,0.0,17.0,0.0,1.0,0.0,0.0,1.0,29.48,1.457,0.0,64.0 +Kr,36.0,101.0,83.798,115.79,18.0,4.0,116.0,3.0,2.0,6.0,10.0,0.0,18.0,0.0,0.0,0.0,0.0,0.0,36.06,7.535,0.0,225.0 +Rb,37.0,4.0,85.4678,312.46,1.0,5.0,220.0,0.82,1.0,0.0,0.0,0.0,1.0,1.0,0.0,0.0,0.0,1.0,90.7225,0.0,0.0,229.0 +Sr,38.0,8.0,87.62,1050.0,2.0,5.0,195.0,0.95,2.0,0.0,0.0,0.0,2.0,0.0,0.0,0.0,0.0,0.0,54.23,0.0,0.0,225.0 +Y,39.0,12.0,88.90585,1799.0,3.0,5.0,190.0,1.22,2.0,0.0,1.0,0.0,3.0,0.0,0.0,9.0,0.0,9.0,32.365,0.0,0.0,194.0 +Zr,40.0,44.0,91.224,2128.0,4.0,5.0,175.0,1.33,2.0,0.0,2.0,0.0,4.0,0.0,0.0,8.0,0.0,8.0,23.195,0.0,0.0,194.0 +Nb,41.0,47.0,92.90638,2750.0,5.0,5.0,164.0,1.6,1.0,0.0,4.0,0.0,5.0,1.0,0.0,6.0,0.0,7.0,18.18,0.0,0.0,229.0 +Mo,42.0,50.0,95.96,2896.0,6.0,5.0,154.0,2.16,1.0,0.0,5.0,0.0,6.0,1.0,0.0,5.0,0.0,6.0,15.69,0.0,0.0,229.0 +Tc,43.0,53.0,98.0,2430.0,7.0,5.0,147.0,1.9,2.0,0.0,5.0,0.0,7.0,0.0,0.0,5.0,0.0,5.0,14.285,0.0,0.0,194.0 +Ru,44.0,56.0,101.07,2607.0,8.0,5.0,146.0,2.2,1.0,0.0,7.0,0.0,8.0,1.0,0.0,3.0,0.0,4.0,13.51,0.0,0.0,194.0 +Rh,45.0,59.0,102.9055,2237.0,9.0,5.0,142.0,2.28,1.0,0.0,8.0,0.0,9.0,1.0,0.0,2.0,0.0,3.0,13.64,0.0,0.0,225.0 +Pd,46.0,62.0,106.42,1828.05,10.0,5.0,139.0,2.2,0.0,0.0,10.0,0.0,10.0,0.0,0.0,0.0,0.0,0.0,14.41,0.0,0.0,225.0 +Ag,47.0,65.0,107.8682,1234.93,11.0,5.0,145.0,1.93,1.0,0.0,10.0,0.0,11.0,1.0,0.0,0.0,0.0,1.0,16.33,0.0,0.0,225.0 +Cd,48.0,70.0,112.411,594.22,12.0,5.0,144.0,1.69,2.0,0.0,10.0,0.0,12.0,0.0,0.0,0.0,0.0,0.0,19.495,0.0,0.0,194.0 +In,49.0,75.0,114.818,429.75,13.0,5.0,142.0,1.78,2.0,1.0,10.0,0.0,13.0,0.0,5.0,0.0,0.0,5.0,24.26,0.0,0.0,139.0 +Sn,50.0,80.0,118.71,505.08,14.0,5.0,139.0,1.96,2.0,2.0,10.0,0.0,14.0,0.0,4.0,0.0,0.0,4.0,33.285,0.0,0.0,141.0 +Sb,51.0,85.0,121.76,903.78,15.0,5.0,139.0,2.05,2.0,3.0,10.0,0.0,15.0,0.0,3.0,0.0,0.0,3.0,31.56,0.0,0.0,166.0 +Te,52.0,90.0,127.6,722.66,16.0,5.0,138.0,2.1,2.0,4.0,10.0,0.0,16.0,0.0,2.0,0.0,0.0,2.0,34.7633333333,0.464,0.0,152.0 +I,53.0,96.0,126.90447,386.85,17.0,5.0,139.0,2.66,2.0,5.0,10.0,0.0,17.0,0.0,1.0,0.0,0.0,1.0,43.015,1.062,0.0,64.0 +Xe,54.0,102.0,131.293,161.3,18.0,5.0,140.0,2.6,2.0,6.0,10.0,0.0,18.0,0.0,0.0,0.0,0.0,0.0,53.65,6.456,0.0,225.0 +Cs,55.0,5.0,132.9054519,301.59,1.0,6.0,244.0,0.79,1.0,0.0,0.0,0.0,1.0,1.0,0.0,0.0,0.0,1.0,115.765,0.0,0.0,229.0 +Ba,56.0,9.0,137.327,1000.0,2.0,6.0,215.0,0.89,2.0,0.0,0.0,0.0,2.0,0.0,0.0,0.0,0.0,0.0,63.59,0.0,0.0,229.0 +La,57.0,13.0,138.90547,1193.0,3.0,6.0,207.0,1.1,2.0,0.0,1.0,0.0,3.0,0.0,0.0,9.0,0.0,9.0,36.8975,0.0,0.0,194.0 +Ce,58.0,15.0,140.116,1071.0,3.0,6.0,204.0,1.12,2.0,0.0,1.0,1.0,4.0,0.0,0.0,9.0,13.0,22.0,37.24,0.0,0.0,194.0 +Pr,59.0,17.0,140.90765,1204.0,3.0,6.0,203.0,1.13,2.0,0.0,0.0,3.0,5.0,0.0,0.0,0.0,11.0,11.0,35.675,0.0,0.0,194.0 +Nd,60.0,19.0,144.242,1294.0,3.0,6.0,201.0,1.14,2.0,0.0,0.0,4.0,6.0,0.0,0.0,0.0,10.0,10.0,34.81,0.0,0.0,194.0 +Pm,61.0,21.0,145.0,1373.0,3.0,6.0,199.0,1.155,2.0,0.0,0.0,5.0,7.0,0.0,0.0,0.0,9.0,9.0,33.8425,0.0,0.0,194.0 +Sm,62.0,23.0,150.36,1345.0,3.0,6.0,198.0,1.17,2.0,0.0,0.0,6.0,8.0,0.0,0.0,0.0,8.0,8.0,33.23,0.0,0.0,166.0 +Eu,63.0,25.0,151.964,1095.0,3.0,6.0,198.0,1.185,2.0,0.0,0.0,7.0,9.0,0.0,0.0,0.0,7.0,7.0,36.46,0.0,0.0,229.0 +Gd,64.0,27.0,157.25,1586.0,3.0,6.0,196.0,1.2,2.0,0.0,1.0,7.0,10.0,0.0,0.0,9.0,7.0,16.0,32.05,0.0,0.0,194.0 +Tb,65.0,29.0,158.92535,1629.0,3.0,6.0,194.0,1.21,2.0,0.0,0.0,9.0,11.0,0.0,0.0,0.0,5.0,5.0,31.7366666667,0.0,0.0,194.0 +Dy,66.0,31.0,162.5,1685.0,3.0,6.0,192.0,1.22,2.0,0.0,0.0,10.0,12.0,0.0,0.0,0.0,4.0,4.0,31.24,0.0,0.0,194.0 +Ho,67.0,33.0,164.93032,1747.0,3.0,6.0,192.0,1.23,2.0,0.0,0.0,11.0,13.0,0.0,0.0,0.0,3.0,3.0,30.7333333333,0.0,0.0,194.0 +Er,68.0,35.0,167.259,1770.0,3.0,6.0,189.0,1.24,2.0,0.0,0.0,12.0,14.0,0.0,0.0,0.0,2.0,2.0,30.585,0.0,0.0,194.0 +Tm,69.0,37.0,168.93421,1818.0,3.0,6.0,190.0,1.25,2.0,0.0,0.0,13.0,15.0,0.0,0.0,0.0,1.0,1.0,29.78,0.0,0.0,194.0 +Yb,70.0,39.0,173.054,1092.0,3.0,6.0,187.0,1.26,2.0,0.0,0.0,14.0,16.0,0.0,0.0,0.0,0.0,0.0,34.12,0.0,0.0,225.0 +Lu,71.0,41.0,174.9668,1936.0,3.0,6.0,187.0,1.27,2.0,0.0,1.0,14.0,17.0,0.0,0.0,9.0,0.0,9.0,28.865,0.0,0.0022471,194.0 +Hf,72.0,45.0,178.49,2506.0,4.0,6.0,175.0,1.3,2.0,0.0,2.0,14.0,18.0,0.0,0.0,8.0,0.0,8.0,22.2,0.0,0.0,194.0 +Ta,73.0,48.0,180.94788,3290.0,5.0,6.0,170.0,1.5,2.0,0.0,3.0,14.0,19.0,0.0,0.0,7.0,0.0,7.0,18.12,0.0,0.0,229.0 +W,74.0,51.0,183.84,3695.0,6.0,6.0,162.0,2.36,2.0,0.0,4.0,14.0,20.0,0.0,0.0,6.0,0.0,6.0,16.05,0.0,0.0,229.0 +Re,75.0,54.0,186.207,3459.0,7.0,6.0,151.0,1.9,2.0,0.0,5.0,14.0,21.0,0.0,0.0,5.0,0.0,5.0,14.655,0.0,0.0,194.0 +Os,76.0,57.0,190.23,3306.0,8.0,6.0,144.0,2.2,2.0,0.0,6.0,14.0,22.0,0.0,0.0,4.0,0.0,4.0,14.09,0.0,0.0,194.0 +Ir,77.0,60.0,192.217,2739.0,9.0,6.0,141.0,2.2,2.0,0.0,7.0,14.0,23.0,0.0,0.0,3.0,0.0,3.0,14.21,0.0,0.0,225.0 +Pt,78.0,63.0,195.084,2041.4,10.0,6.0,136.0,2.28,1.0,0.0,9.0,14.0,24.0,1.0,0.0,1.0,0.0,2.0,15.02,0.0,0.0,225.0 +Au,79.0,66.0,196.966569,1337.33,11.0,6.0,136.0,2.54,1.0,0.0,10.0,14.0,25.0,1.0,0.0,0.0,0.0,1.0,16.7,0.0,0.0,225.0 +Hg,80.0,71.0,200.59,234.32,12.0,6.0,132.0,2.0,2.0,0.0,10.0,14.0,26.0,0.0,0.0,0.0,0.0,0.0,25.2375862069,0.0,0.0,166.0 +Tl,81.0,76.0,204.3833,577.0,13.0,6.0,145.0,1.62,2.0,1.0,10.0,14.0,27.0,0.0,5.0,0.0,0.0,5.0,26.91,0.0,0.0,194.0 +Pb,82.0,81.0,207.2,600.61,14.0,6.0,146.0,2.33,2.0,2.0,10.0,14.0,28.0,0.0,4.0,0.0,0.0,4.0,28.11,0.0,0.0,225.0 +Bi,83.0,86.0,208.9804,544.4,15.0,6.0,148.0,2.02,2.0,3.0,10.0,14.0,29.0,0.0,3.0,0.0,0.0,3.0,32.95,0.0,0.0,12.0 +Po,84.0,91.0,209.0,527.0,16.0,6.0,140.0,2.0,2.0,4.0,10.0,14.0,30.0,0.0,2.0,0.0,0.0,2.0,38.73125,0.0,0.0,221.0 +At,85.0,97.0,210.0,575.0,17.0,6.0,150.0,2.2,2.0,5.0,10.0,14.0,31.0,0.0,1.0,0.0,0.0,1.0,38.73125,0.0,0.0,194.0 +Rn,86.0,103.0,222.0,202.0,18.0,6.0,150.0,1.63,2.0,6.0,10.0,14.0,32.0,0.0,0.0,0.0,0.0,0.0,38.73125,0.0,0.0,194.0 +Fr,87.0,6.0,223.0,1211.4,1.0,7.0,260.0,0.7,1.0,0.0,0.0,0.0,1.0,1.0,0.0,0.0,0.0,1.0,38.73125,0.0,0.0,194.0 +Ra,88.0,10.0,226.0,973.0,2.0,7.0,221.0,0.9,2.0,0.0,0.0,0.0,2.0,0.0,0.0,0.0,0.0,0.0,38.73125,0.0,0.0,229.0 +Ac,89.0,14.0,227.0,1323.0,3.0,7.0,215.0,1.1,2.0,0.0,1.0,0.0,3.0,0.0,0.0,9.0,0.0,9.0,44.5125,0.0,0.0,225.0 +Th,90.0,16.0,232.03806,2023.0,3.0,7.0,206.0,1.3,2.0,0.0,2.0,0.0,4.0,0.0,0.0,8.0,0.0,8.0,32.37,0.0,0.0,225.0 +Pa,91.0,18.0,231.03586,1845.0,3.0,7.0,200.0,1.5,2.0,0.0,1.0,2.0,5.0,0.0,0.0,9.0,12.0,21.0,25.18,0.0,0.0,139.0 +U,92.0,20.0,238.02891,1408.0,3.0,7.0,196.0,1.38,2.0,0.0,1.0,3.0,6.0,0.0,0.0,9.0,11.0,20.0,20.025,0.0,0.0,63.0 +Np,93.0,22.0,237.0,917.0,3.0,7.0,190.0,1.36,2.0,0.0,1.0,4.0,7.0,0.0,0.0,9.0,10.0,19.0,18.45375,0.0,0.0,62.0 +Pu,94.0,24.0,244.0,913.0,3.0,7.0,187.0,1.28,2.0,0.0,0.0,6.0,8.0,0.0,0.0,0.0,8.0,8.0,18.08,0.0,0.3180036375,11.0 +Am,95.0,26.0,243.0,1449.0,3.0,7.0,180.0,1.3,2.0,0.0,0.0,7.0,9.0,0.0,0.0,0.0,7.0,7.0,18.08,0.0,0.3180036375,194.0 +Cm,96.0,28.0,247.0,1618.0,3.0,7.0,169.0,1.3,2.0,0.0,1.0,7.0,10.0,0.0,0.0,9.0,7.0,16.0,18.08,0.0,0.3180036375,194.0 +Bk,97.0,30.0,247.0,1323.0,3.0,7.0,146.0,1.3,2.0,0.0,0.0,9.0,11.0,0.0,0.0,0.0,5.0,5.0,18.08,0.0,0.3180036375,194.0 From b563be1c5c55a59b53b48d6a3cdd6b314b502b60 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli <30937913+AntObi@users.noreply.github.com> Date: Fri, 12 Jul 2024 18:33:17 +0100 Subject: [PATCH 04/44] Update smact/data_loader.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- smact/data_loader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smact/data_loader.py b/smact/data_loader.py index 5e50c7f4..2906cd63 100644 --- a/smact/data_loader.py +++ b/smact/data_loader.py @@ -865,7 +865,7 @@ def lookup_element_magpie_data(symbol: str, copy: bool = True): _element_magpie_data = {} df = pd.read_csv(os.path.join(data_directory, "magpie.csv")) - for index, row in df.iterrows(): + for _index, row in df.iterrows(): key = row.iloc[0] dataset = { From ddef919f1fe6e4396f1fe0fe3b932beeb842be6c Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Tue, 16 Jul 2024 09:35:25 +0100 Subject: [PATCH 05/44] Add the modified NValence file --- smact/data/element_valence_modified.csv | 98 +++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 smact/data/element_valence_modified.csv diff --git a/smact/data/element_valence_modified.csv b/smact/data/element_valence_modified.csv new file mode 100644 index 00000000..9a642db3 --- /dev/null +++ b/smact/data/element_valence_modified.csv @@ -0,0 +1,98 @@ +element,NValence +H,1 +He,2 +Li,1 +Be,2 +B,3 +C,4 +N,5 +O,6 +F,7 +Ne,8 +Na,1 +Mg,2 +Al,3 +Si,4 +P,5 +S,6 +Cl,7 +Ar,8 +K,1 +Ca,2 +Sc,3 +Ti,4 +V,5 +Cr,6 +Mn,7 +Fe,8 +Co,9 +Ni,10 +Cu,11 +Zn,12 +Ga,3 +Ge,4 +As,5 +Se,6 +Br,7 +Kr,8 +Rb,1 +Sr,2 +Y,3 +Zr,4 +Nb,5 +Mo,6 +Tc,7 +Ru,8 +Rh,9 +Pd,10 +Ag,11 +Cd,12 +In,3 +Sn,4 +Sb,5 +Te,6 +I,7 +Xe,8 +Cs,1 +Ba,2 +La,3 +Ce,4 +Pr,5 +Nd,6 +Pm,7 +Sm,8 +Eu,9 +Gd,10 +Tb,11 +Dy,12 +Ho,13 +Er,14 +Tm,15 +Yb,16 +Lu,3 +Hf,4 +Ta,5 +W,6 +Re,7 +Os,8 +Ir,9 +Pt,10 +Au,11 +Hg,12 +Tl,3 +Pb,4 +Bi,5 +Po,6 +At,7 +Rn,8 +Fr,1 +Ra,2 +Ac,3 +Th,4 +Pa,5 +U,6 +Np,7 +Pu,8 +Am,9 +Cm,10 +Bk,11 From 9a2250e12ad1acbb41b7ea21f507c1d0d50126e1 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Tue, 16 Jul 2024 09:35:44 +0100 Subject: [PATCH 06/44] Load in the modified NValence data --- smact/__init__.py | 7 ++++++ smact/data_loader.py | 51 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/smact/__init__.py b/smact/__init__.py index 9d358a95..f37dc9f2 100644 --- a/smact/__init__.py +++ b/smact/__init__.py @@ -81,6 +81,8 @@ class Element: Element.num_valence (int): Number of valence electrons + Element.num_valence_modified (int): Number of valence electrons based on a modified definition + Raises: @@ -162,6 +164,10 @@ def __init__( MeltingT = None num_valence = None + valence_data = data_loader.lookup_element_valence_data(symbol) + if valence_data: + num_valence_modified = valence_data["NValence"] + for attribute, value in ( ("coord_envs", coord_envs), ("covalent_radius", dataset["r_cov"]), @@ -200,6 +206,7 @@ def __init__( ("AtomicWeight", AtomicWeight), ("MeltingT", MeltingT), ("num_valence", num_valence), + ("num_valence_modified", num_valence_modified), # ('vdw_radius', dataset['RVdW']), ): setattr(self, attribute, value) diff --git a/smact/data_loader.py b/smact/data_loader.py index 2906cd63..188404a1 100644 --- a/smact/data_loader.py +++ b/smact/data_loader.py @@ -904,3 +904,54 @@ def lookup_element_magpie_data(symbol: str, copy: bool = True): ) return None + + +_element_valence_data = None + + +def lookup_element_valence_data(symbol: str, copy: bool = True): + """ + Retrieve valence electron data. + + For d-block elements, the s and d electrons contribute to NValence. + For p-block elements, the s and p electrons contribute to NValence. + For s- and f-block elements, NValence is calculated from the Noble Gas electron configuration + i.e. + + Args: + symbol : the atomic symbol of the element to look up. + copy: if True (default), return a copy of the data dictionary, + rather than a reference to a cached object -- only use + copy=False in performance-sensitive code and where you are + certain the dictionary will not be modified! + + Returns: + NValence (int): the number of valence electrons + Returns None if the element was not found among the external + data. + """ + + global _element_valence_data + + if _element_valence_data is None: + _element_valence_data = {} + + df = pd.read_csv( + os.path.join(data_directory, "element_valence_modified.csv") + ) + for _index, row in df.iterrows(): + key = row.iloc[0] + + dataset = {"NValence": int(row.iloc[1])} + _element_valence_data[key] = dataset + + if symbol in _element_valence_data: + return _element_valence_data[symbol] + else: + if _print_warnings: + print( + "WARNING: Valence data for element {} not " + "found.".format(symbol) + ) + + return None From 9d8f9f0fe7b01c22201fd075bb2fcfd9fc93f8a5 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Tue, 16 Jul 2024 09:57:45 +0100 Subject: [PATCH 07/44] Fix __init__.py --- smact/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/smact/__init__.py b/smact/__init__.py index f37dc9f2..5ef153d6 100644 --- a/smact/__init__.py +++ b/smact/__init__.py @@ -167,6 +167,8 @@ def __init__( valence_data = data_loader.lookup_element_valence_data(symbol) if valence_data: num_valence_modified = valence_data["NValence"] + else: + num_valence_modified = None for attribute, value in ( ("coord_envs", coord_envs), From 231bb2048e28a9c3f2cc12e551a0a77b95b54e7d Mon Sep 17 00:00:00 2001 From: ryannduma Date: Thu, 22 Aug 2024 11:26:26 +0100 Subject: [PATCH 08/44] VEC integration into smact.properties --- VECexample.py | 11 ++++ elements_valence_modified.csv | 98 +++++++++++++++++++++++++++++++++++ smact/properties.py | 63 ++++++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 VECexample.py create mode 100644 elements_valence_modified.csv diff --git a/VECexample.py b/VECexample.py new file mode 100644 index 00000000..98164396 --- /dev/null +++ b/VECexample.py @@ -0,0 +1,11 @@ +from smact.properties import valence_electron_count + +# Define the compound and the path to the valence data file +compound = "Ba5In4Bi5" +valence_file = "element_valence_modifiedry.csv" + +# Calculate the Valence Electron Count (VEC) +vec = valence_electron_count(compound, valence_file) + +# Print the result +print(f"The Valence Electron Count (VEC) for {compound} is: {vec:.2f}") \ No newline at end of file diff --git a/elements_valence_modified.csv b/elements_valence_modified.csv new file mode 100644 index 00000000..9a642db3 --- /dev/null +++ b/elements_valence_modified.csv @@ -0,0 +1,98 @@ +element,NValence +H,1 +He,2 +Li,1 +Be,2 +B,3 +C,4 +N,5 +O,6 +F,7 +Ne,8 +Na,1 +Mg,2 +Al,3 +Si,4 +P,5 +S,6 +Cl,7 +Ar,8 +K,1 +Ca,2 +Sc,3 +Ti,4 +V,5 +Cr,6 +Mn,7 +Fe,8 +Co,9 +Ni,10 +Cu,11 +Zn,12 +Ga,3 +Ge,4 +As,5 +Se,6 +Br,7 +Kr,8 +Rb,1 +Sr,2 +Y,3 +Zr,4 +Nb,5 +Mo,6 +Tc,7 +Ru,8 +Rh,9 +Pd,10 +Ag,11 +Cd,12 +In,3 +Sn,4 +Sb,5 +Te,6 +I,7 +Xe,8 +Cs,1 +Ba,2 +La,3 +Ce,4 +Pr,5 +Nd,6 +Pm,7 +Sm,8 +Eu,9 +Gd,10 +Tb,11 +Dy,12 +Ho,13 +Er,14 +Tm,15 +Yb,16 +Lu,3 +Hf,4 +Ta,5 +W,6 +Re,7 +Os,8 +Ir,9 +Pt,10 +Au,11 +Hg,12 +Tl,3 +Pb,4 +Bi,5 +Po,6 +At,7 +Rn,8 +Fr,1 +Ra,2 +Ac,3 +Th,4 +Pa,5 +U,6 +Np,7 +Pu,8 +Am,9 +Cm,10 +Bk,11 diff --git a/smact/properties.py b/smact/properties.py index 55e33700..3374042b 100644 --- a/smact/properties.py +++ b/smact/properties.py @@ -1,4 +1,7 @@ from typing import List, Optional, Union +import re +from collections import defaultdict +import csv import numpy as np @@ -157,3 +160,63 @@ def compound_electroneg( print("Geometric mean = Compound 'electronegativity'=", compelectroneg) return compelectroneg + + +def valence_electron_count(compound: str, valence_file: str) -> float: + """ + Calculate the Valence Electron Count (VEC) for a given compound. + + This function parses the input compound, extracts the elements and their + stoichiometries, and calculates the VEC by referencing a CSV file containing + valence electron data for each element. + + Args: + compound (str): Chemical formula of the compound (e.g., "Fe2O3"). + valence_file (str): Path to the CSV file containing valence electron data. + + Returns: + float: Valence Electron Count (VEC) for the compound. + + Raises: + FileNotFoundError: If the valence_file is not found. + ValueError: If an element in the compound is not found in the valence data. + """ + def parse_formula(formula): + pattern = re.compile(r'([A-Z][a-z]*)(\d*)') + elements = pattern.findall(formula) + element_stoich = defaultdict(int) + for (element, count) in elements: + count = int(count) if count else 1 + element_stoich[element] += count + return element_stoich + + def get_element_valence(element, valence_data): + for row in valence_data: + if row['element'] == element: + return int(row['NValence']) + raise ValueError(f"Valence data not found for element: {element}") + + try: + with open(valence_file, 'r') as file: + reader = csv.DictReader(file) + valence_data = list(reader) + except FileNotFoundError: + raise FileNotFoundError(f"Valence data file not found: {valence_file}") + + element_stoich = parse_formula(compound) + + total_valence = 0 + total_stoich = 0 + + for element, stoich in element_stoich.items(): + valence = get_element_valence(element, valence_data) + total_valence += stoich * valence + total_stoich += stoich + + if total_stoich == 0: + return 0 + + vec = total_valence / total_stoich + return vec + + From c84e475ccd346115ca4857c63174d6d71691c9df Mon Sep 17 00:00:00 2001 From: ryannduma Date: Thu, 22 Aug 2024 11:29:03 +0100 Subject: [PATCH 09/44] small tweak to csv file naming --- VECexample.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VECexample.py b/VECexample.py index 98164396..d6a2ac86 100644 --- a/VECexample.py +++ b/VECexample.py @@ -2,7 +2,7 @@ # Define the compound and the path to the valence data file compound = "Ba5In4Bi5" -valence_file = "element_valence_modifiedry.csv" +valence_file = "elements_valence_modified.csv" # Calculate the Valence Electron Count (VEC) vec = valence_electron_count(compound, valence_file) From 289ac52f93aaec87b1d7cc145fbd96c4a02a37a4 Mon Sep 17 00:00:00 2001 From: "Ryan (Ry) Napo Nduma" <54267734+ryannduma@users.noreply.github.com> Date: Thu, 22 Aug 2024 12:56:17 +0100 Subject: [PATCH 10/44] changes to valence_electron_count & test_core.py --- VECexample.py | 7 +++--- smact/properties.py | 46 ++++++++++++++++------------------------ smact/tests/test_core.py | 16 +++++++++++++- 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/VECexample.py b/VECexample.py index d6a2ac86..17e1f0ff 100644 --- a/VECexample.py +++ b/VECexample.py @@ -1,11 +1,10 @@ from smact.properties import valence_electron_count -# Define the compound and the path to the valence data file +# Define the compound compound = "Ba5In4Bi5" -valence_file = "elements_valence_modified.csv" # Calculate the Valence Electron Count (VEC) -vec = valence_electron_count(compound, valence_file) +vec = valence_electron_count(compound) # Print the result -print(f"The Valence Electron Count (VEC) for {compound} is: {vec:.2f}") \ No newline at end of file +print(f"The Valence Electron Count (VEC) for {compound} is: {vec:.2f}") diff --git a/smact/properties.py b/smact/properties.py index 3374042b..10f97a76 100644 --- a/smact/properties.py +++ b/smact/properties.py @@ -1,7 +1,7 @@ -from typing import List, Optional, Union +import csv import re from collections import defaultdict -import csv +from typing import List, Optional, Union import numpy as np @@ -161,47 +161,39 @@ def compound_electroneg( return compelectroneg - -def valence_electron_count(compound: str, valence_file: str) -> float: +def valence_electron_count(compound: str) -> float: """ Calculate the Valence Electron Count (VEC) for a given compound. This function parses the input compound, extracts the elements and their - stoichiometries, and calculates the VEC by referencing a CSV file containing - valence electron data for each element. + stoichiometries, and calculates the VEC using the valence electron data + from SMACT's Element class. Args: compound (str): Chemical formula of the compound (e.g., "Fe2O3"). - valence_file (str): Path to the CSV file containing valence electron data. Returns: float: Valence Electron Count (VEC) for the compound. Raises: - FileNotFoundError: If the valence_file is not found. ValueError: If an element in the compound is not found in the valence data. """ - def parse_formula(formula): - pattern = re.compile(r'([A-Z][a-z]*)(\d*)') + from typing import Dict + + def parse_formula(formula: str) -> Dict[str, int]: + pattern = re.compile(r"([A-Z][a-z]*)(\d*)") elements = pattern.findall(formula) - element_stoich = defaultdict(int) - for (element, count) in elements: + element_stoich: Dict[str, int] = defaultdict(int) + for element, count in elements: count = int(count) if count else 1 element_stoich[element] += count return element_stoich - def get_element_valence(element, valence_data): - for row in valence_data: - if row['element'] == element: - return int(row['NValence']) - raise ValueError(f"Valence data not found for element: {element}") - - try: - with open(valence_file, 'r') as file: - reader = csv.DictReader(file) - valence_data = list(reader) - except FileNotFoundError: - raise FileNotFoundError(f"Valence data file not found: {valence_file}") + def get_element_valence(element: str) -> int: + try: + return smact.Element(element).num_valence_modified + except AttributeError: + raise ValueError(f"Valence data not found for element: {element}") element_stoich = parse_formula(compound) @@ -209,14 +201,12 @@ def get_element_valence(element, valence_data): total_stoich = 0 for element, stoich in element_stoich.items(): - valence = get_element_valence(element, valence_data) + valence = get_element_valence(element) total_valence += stoich * valence total_stoich += stoich if total_stoich == 0: - return 0 + return 0.0 vec = total_valence / total_stoich return vec - - diff --git a/smact/tests/test_core.py b/smact/tests/test_core.py index a30b4f28..d05fdfb2 100755 --- a/smact/tests/test_core.py +++ b/smact/tests/test_core.py @@ -14,7 +14,7 @@ import smact.screening from smact import Species from smact.builder import wurtzite -from smact.properties import band_gap_Harrison, compound_electroneg +from smact.properties import band_gap_Harrison, compound_electroneg, valence_electron_count files_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "files") TEST_OX_STATES = os.path.join(files_dir, "test_oxidation_states.txt") @@ -87,6 +87,20 @@ def test_harrison_gap_MgCl(self): 3.545075110572662, ) + def test_valence_electron_count(self): + self.assertAlmostEqual( + valence_electron_count("Fe2O3"), + 8.0, + places=2 + ) + self.assertAlmostEqual( + valence_electron_count("CuZn"), + 11.5, + places=2 + ) + with self.assertRaises(ValueError): + valence_electron_count("Xx2O3") # Xx is not a real element + # ---------------- BUILDER ---------------- def test_builder_ZnS(self): From c085b49f5631633638b37f7dec39beaced1c895c Mon Sep 17 00:00:00 2001 From: "Ryan (Ry) Napo Nduma" <54267734+ryannduma@users.noreply.github.com> Date: Thu, 22 Aug 2024 12:58:31 +0100 Subject: [PATCH 11/44] ran pre-commit --- smact/properties.py | 1 + smact/tests/test_core.py | 18 +++++++----------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/smact/properties.py b/smact/properties.py index 10f97a76..7e29ce58 100644 --- a/smact/properties.py +++ b/smact/properties.py @@ -161,6 +161,7 @@ def compound_electroneg( return compelectroneg + def valence_electron_count(compound: str) -> float: """ Calculate the Valence Electron Count (VEC) for a given compound. diff --git a/smact/tests/test_core.py b/smact/tests/test_core.py index d05fdfb2..200ca7c6 100755 --- a/smact/tests/test_core.py +++ b/smact/tests/test_core.py @@ -14,7 +14,11 @@ import smact.screening from smact import Species from smact.builder import wurtzite -from smact.properties import band_gap_Harrison, compound_electroneg, valence_electron_count +from smact.properties import ( + band_gap_Harrison, + compound_electroneg, + valence_electron_count, +) files_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "files") TEST_OX_STATES = os.path.join(files_dir, "test_oxidation_states.txt") @@ -88,16 +92,8 @@ def test_harrison_gap_MgCl(self): ) def test_valence_electron_count(self): - self.assertAlmostEqual( - valence_electron_count("Fe2O3"), - 8.0, - places=2 - ) - self.assertAlmostEqual( - valence_electron_count("CuZn"), - 11.5, - places=2 - ) + self.assertAlmostEqual(valence_electron_count("Fe2O3"), 8.0, places=2) + self.assertAlmostEqual(valence_electron_count("CuZn"), 11.5, places=2) with self.assertRaises(ValueError): valence_electron_count("Xx2O3") # Xx is not a real element From b9a303a7f3a1a18dc600e971173ab1b593b74002 Mon Sep 17 00:00:00 2001 From: "Ryan (Ry) Napo Nduma" <54267734+ryannduma@users.noreply.github.com> Date: Thu, 22 Aug 2024 13:09:56 +0100 Subject: [PATCH 12/44] moved VECexample.py to examples/vec_example.py --- VECexample.py => examples/vec_example.py | 0 smact/properties.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename VECexample.py => examples/vec_example.py (100%) diff --git a/VECexample.py b/examples/vec_example.py similarity index 100% rename from VECexample.py rename to examples/vec_example.py diff --git a/smact/properties.py b/smact/properties.py index 7e29ce58..0845c273 100644 --- a/smact/properties.py +++ b/smact/properties.py @@ -194,7 +194,7 @@ def get_element_valence(element: str) -> int: try: return smact.Element(element).num_valence_modified except AttributeError: - raise ValueError(f"Valence data not found for element: {element}") + raise ValueError(f"Valence data not found for element: {element}") from None element_stoich = parse_formula(compound) From e90a23b29c9aa7a5bb8da77fabd48849eaf7a9be Mon Sep 17 00:00:00 2001 From: "Ryan (Ry) Napo Nduma" <54267734+ryannduma@users.noreply.github.com> Date: Thu, 22 Aug 2024 13:12:41 +0100 Subject: [PATCH 13/44] ran pre-commit to fix qa failures --- smact/properties.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/smact/properties.py b/smact/properties.py index 0845c273..87f3cfbf 100644 --- a/smact/properties.py +++ b/smact/properties.py @@ -194,7 +194,9 @@ def get_element_valence(element: str) -> int: try: return smact.Element(element).num_valence_modified except AttributeError: - raise ValueError(f"Valence data not found for element: {element}") from None + raise ValueError( + f"Valence data not found for element: {element}" + ) from None element_stoich = parse_formula(compound) From 80ccabfc8468909fa38a4d42bba37b3b1ff010d9 Mon Sep 17 00:00:00 2001 From: "Ryan (Ry) Napo Nduma" <54267734+ryannduma@users.noreply.github.com> Date: Thu, 22 Aug 2024 13:29:27 +0100 Subject: [PATCH 14/44] fixing test.core issues --- examples/vec_example.py | 2 +- smact/tests/test_core.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/vec_example.py b/examples/vec_example.py index 17e1f0ff..318bc298 100644 --- a/examples/vec_example.py +++ b/examples/vec_example.py @@ -1,7 +1,7 @@ from smact.properties import valence_electron_count # Define the compound -compound = "Ba5In4Bi5" +compound = "Fe2O3" # Calculate the Valence Electron Count (VEC) vec = valence_electron_count(compound) diff --git a/smact/tests/test_core.py b/smact/tests/test_core.py index 200ca7c6..3deb274a 100755 --- a/smact/tests/test_core.py +++ b/smact/tests/test_core.py @@ -92,7 +92,7 @@ def test_harrison_gap_MgCl(self): ) def test_valence_electron_count(self): - self.assertAlmostEqual(valence_electron_count("Fe2O3"), 8.0, places=2) + self.assertAlmostEqual(valence_electron_count("Fe2O3"), 6.8, places=2) self.assertAlmostEqual(valence_electron_count("CuZn"), 11.5, places=2) with self.assertRaises(ValueError): valence_electron_count("Xx2O3") # Xx is not a real element From 8377670b8161feadcd4c35a79616b035cacdb924 Mon Sep 17 00:00:00 2001 From: "Ryan (Ry) Napo Nduma" <54267734+ryannduma@users.noreply.github.com> Date: Thu, 22 Aug 2024 16:52:02 +0100 Subject: [PATCH 15/44] updates on tests, pre-commit-config.yaml, and properties.py --- .pre-commit-config.yaml | 7 +- elements_valence_modified.csv | 98 ------------------------ examples/Crystal_Space/1_reduction.ipynb | 2 +- smact/properties.py | 14 ++-- smact/tests/test_core.py | 14 +++- 5 files changed, 26 insertions(+), 109 deletions(-) delete mode 100644 elements_valence_modified.csv diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3ff89c33..43b843b9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ repos: hooks: - id: isort additional_dependencies: [toml] - args: ["--profile", "black", "--filter-files","--line-length=80"] + args: ["--profile", "black", "--filter-files", "--line-length=80"] - repo: https://github.com/psf/black rev: "23.1.0" hooks: @@ -16,9 +16,8 @@ repos: - id: pyupgrade args: [--py38-plus] - repo: https://github.com/nbQA-dev/nbQA - rev: "1.6.1" + rev: "1.8.7" hooks: - id: nbqa-pyupgrade additional_dependencies: [pyupgrade==3.3.1] - args: [--py38-plus] - + args: [--py39-plus] diff --git a/elements_valence_modified.csv b/elements_valence_modified.csv deleted file mode 100644 index 9a642db3..00000000 --- a/elements_valence_modified.csv +++ /dev/null @@ -1,98 +0,0 @@ -element,NValence -H,1 -He,2 -Li,1 -Be,2 -B,3 -C,4 -N,5 -O,6 -F,7 -Ne,8 -Na,1 -Mg,2 -Al,3 -Si,4 -P,5 -S,6 -Cl,7 -Ar,8 -K,1 -Ca,2 -Sc,3 -Ti,4 -V,5 -Cr,6 -Mn,7 -Fe,8 -Co,9 -Ni,10 -Cu,11 -Zn,12 -Ga,3 -Ge,4 -As,5 -Se,6 -Br,7 -Kr,8 -Rb,1 -Sr,2 -Y,3 -Zr,4 -Nb,5 -Mo,6 -Tc,7 -Ru,8 -Rh,9 -Pd,10 -Ag,11 -Cd,12 -In,3 -Sn,4 -Sb,5 -Te,6 -I,7 -Xe,8 -Cs,1 -Ba,2 -La,3 -Ce,4 -Pr,5 -Nd,6 -Pm,7 -Sm,8 -Eu,9 -Gd,10 -Tb,11 -Dy,12 -Ho,13 -Er,14 -Tm,15 -Yb,16 -Lu,3 -Hf,4 -Ta,5 -W,6 -Re,7 -Os,8 -Ir,9 -Pt,10 -Au,11 -Hg,12 -Tl,3 -Pb,4 -Bi,5 -Po,6 -At,7 -Rn,8 -Fr,1 -Ra,2 -Ac,3 -Th,4 -Pa,5 -U,6 -Np,7 -Pu,8 -Am,9 -Cm,10 -Bk,11 diff --git a/examples/Crystal_Space/1_reduction.ipynb b/examples/Crystal_Space/1_reduction.ipynb index e4a3c14e..cb178649 100644 --- a/examples/Crystal_Space/1_reduction.ipynb +++ b/examples/Crystal_Space/1_reduction.ipynb @@ -58,7 +58,7 @@ "metadata": {}, "outputs": [], "source": [ - "from typing import Iterable\n", + "from collections.abc import Iterable\n", "from pathlib import Path\n", "\n", "from tqdm import tqdm\n", diff --git a/smact/properties.py b/smact/properties.py index 87f3cfbf..d5930273 100644 --- a/smact/properties.py +++ b/smact/properties.py @@ -193,7 +193,7 @@ def parse_formula(formula: str) -> Dict[str, int]: def get_element_valence(element: str) -> int: try: return smact.Element(element).num_valence_modified - except AttributeError: + except NameError: raise ValueError( f"Valence data not found for element: {element}" ) from None @@ -202,11 +202,15 @@ def get_element_valence(element: str) -> int: total_valence = 0 total_stoich = 0 - for element, stoich in element_stoich.items(): - valence = get_element_valence(element) - total_valence += stoich * valence - total_stoich += stoich + try: + valence = get_element_valence(element) + total_valence += stoich * valence + total_stoich += stoich + except TypeError: + print( + f"Calculation failed: No valence information for element {element}" + ) if total_stoich == 0: return 0.0 diff --git a/smact/tests/test_core.py b/smact/tests/test_core.py index 3deb274a..ab44e4c9 100755 --- a/smact/tests/test_core.py +++ b/smact/tests/test_core.py @@ -92,11 +92,23 @@ def test_harrison_gap_MgCl(self): ) def test_valence_electron_count(self): + # Test valid compounds self.assertAlmostEqual(valence_electron_count("Fe2O3"), 6.8, places=2) self.assertAlmostEqual(valence_electron_count("CuZn"), 11.5, places=2) - with self.assertRaises(ValueError): + + # Test single element + self.assertEqual(valence_electron_count("Fe"), 8) + + # Test empty string + self.assertEqual(valence_electron_count(""), 0.0) + + # Test invalid elements and formats + with self.assertRaises(NameError): valence_electron_count("Xx2O3") # Xx is not a real element + with self.assertRaises(TypeError): + valence_electron_count("LrO") + # ---------------- BUILDER ---------------- def test_builder_ZnS(self): From f8b7a8aec513830870e987f19df1e93998c8e153 Mon Sep 17 00:00:00 2001 From: "Ryan (Ry) Napo Nduma" <54267734+ryannduma@users.noreply.github.com> Date: Thu, 22 Aug 2024 16:58:09 +0100 Subject: [PATCH 16/44] testing value error for invalid elements in VEC --- smact/tests/test_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smact/tests/test_core.py b/smact/tests/test_core.py index ab44e4c9..ee3f6c3d 100755 --- a/smact/tests/test_core.py +++ b/smact/tests/test_core.py @@ -103,7 +103,7 @@ def test_valence_electron_count(self): self.assertEqual(valence_electron_count(""), 0.0) # Test invalid elements and formats - with self.assertRaises(NameError): + with self.assertRaises(ValueError): valence_electron_count("Xx2O3") # Xx is not a real element with self.assertRaises(TypeError): From 55ba0db43bdfd38e5c8388a6921e874791ca9b3d Mon Sep 17 00:00:00 2001 From: "Ryan (Ry) Napo Nduma" <54267734+ryannduma@users.noreply.github.com> Date: Thu, 22 Aug 2024 17:03:55 +0100 Subject: [PATCH 17/44] ValueError Modifications in VEC for invalid elements ie Lr0 --- smact/properties.py | 4 +--- smact/tests/test_core.py | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/smact/properties.py b/smact/properties.py index d5930273..8b2abf53 100644 --- a/smact/properties.py +++ b/smact/properties.py @@ -208,9 +208,7 @@ def get_element_valence(element: str) -> int: total_valence += stoich * valence total_stoich += stoich except TypeError: - print( - f"Calculation failed: No valence information for element {element}" - ) + raise ValueError(f"No valence information for element {element}") if total_stoich == 0: return 0.0 diff --git a/smact/tests/test_core.py b/smact/tests/test_core.py index ee3f6c3d..c3c53f73 100755 --- a/smact/tests/test_core.py +++ b/smact/tests/test_core.py @@ -106,7 +106,7 @@ def test_valence_electron_count(self): with self.assertRaises(ValueError): valence_electron_count("Xx2O3") # Xx is not a real element - with self.assertRaises(TypeError): + with self.assertRaises(ValueError): valence_electron_count("LrO") # ---------------- BUILDER ---------------- From 538d6aeee349f577746adfc8b136c670a21a9c4a Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Wed, 28 Aug 2024 20:07:12 +0100 Subject: [PATCH 18/44] Add composition utility module --- smact/utils/composition.py | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 smact/utils/composition.py diff --git a/smact/utils/composition.py b/smact/utils/composition.py new file mode 100644 index 00000000..a5091e43 --- /dev/null +++ b/smact/utils/composition.py @@ -0,0 +1,47 @@ +"""Utility functioms for handling elements, species, formulas and composition""" +import re +from collections import defaultdict + + +# Adapted from ElementEmbeddings and Pymatgen +def parse_formula(formula: str) -> dict[str, int]: + """Parse a formula into a dict of el:amt + + Args: + formula (str): Chemical formula + + Returns: + dict: Dictionary of element symbol: amount + """ + regex = r"\(([^\(\)]+)\)\s*([\.e\d]*)" + r = re.compile(regex) + m = re.search(r, formula) + if m: + factor = 1.0 + if m.group(2) != "": + factor = float(m.group(2)) + unit_sym_dict = _get_sym_dict(m.group(1), factor) + expanded_sym = "".join( + [f"{el}{amt}" for el, amt in unit_sym_dict.items()] + ) + expanded_formula = formula.replace(m.group(), expanded_sym) + return parse_formula(expanded_formula) + return _get_sym_dict(formula, 1) + + +def _get_sym_dict(formula: str, factor: float) -> dict[str, float]: + sym_dict: dict[str, float] = defaultdict(float) + regex = r"([A-Z][a-z]*)\s*([-*\.e\d]*)" + r = re.compile(regex) + for m in re.finditer(r, formula): + el = m.group(1) + amt = 1.0 + if m.group(2).strip() != "": + amt = float(m.group(2)) + sym_dict[el] += amt * factor + formula = formula.replace(m.group(), "", 1) + if formula.strip(): + msg = f"{formula} is an invalid formula" + raise ValueError(msg) + + return sym_dict From e73ca254b4b0e38fe89f0159738fe22df854327f Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Wed, 28 Aug 2024 20:07:55 +0100 Subject: [PATCH 19/44] Add test for parsing formulas --- smact/tests/test_utils.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 smact/tests/test_utils.py diff --git a/smact/tests/test_utils.py b/smact/tests/test_utils.py new file mode 100644 index 00000000..7df1ebb7 --- /dev/null +++ b/smact/tests/test_utils.py @@ -0,0 +1,32 @@ +import unittest + +from smact.utils.composition import parse_formula + + +class TestComposition(unittest.TestCase): + """Test composition utilities""" + + def test_parse_formula(self): + formulas = ["Li10GeP2S12", "Mg0.5O0.5", "CaMg(CO3)2"] + + LGPS = parse_formula(formulas[0]) + self.assertIsInstance(LGPS, dict) + for el_sym, ammt in LGPS.items(): + self.assertIsInstance(el_sym, str) + self.assertIsInstance(ammt, float) + self.assertEqual(LGPS["Li"], 10) + self.assertEqual(LGPS["Ge"], 1) + self.assertEqual(LGPS["P"], 2) + self.assertEqual(LGPS["S"], 12) + + MgO = parse_formula(formulas[1]) + self.assertIsInstance(MgO, dict) + self.assertEqual(MgO["Mg"], 0.5) + self.assertEqual(MgO["O"], 0.5) + + dolomite = parse_formula(formulas[2]) + self.assertIsInstance(dolomite, dict) + self.assertEqual(dolomite["Ca"], 1) + self.assertEqual(dolomite["Mg"], 1) + self.assertEqual(dolomite["C"], 2) + self.assertEqual(dolomite["O"], 6) From 44927f5821fb43fbf4783c06ab24d967877531bd Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Wed, 28 Aug 2024 20:08:30 +0100 Subject: [PATCH 20/44] Remove parser function defined within a function --- smact/properties.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/smact/properties.py b/smact/properties.py index 8b2abf53..3d392477 100644 --- a/smact/properties.py +++ b/smact/properties.py @@ -6,6 +6,7 @@ import numpy as np import smact +from smact.utils.composition import parse_formula def eneg_mulliken(element: Union[smact.Element, str]) -> float: @@ -179,16 +180,6 @@ def valence_electron_count(compound: str) -> float: Raises: ValueError: If an element in the compound is not found in the valence data. """ - from typing import Dict - - def parse_formula(formula: str) -> Dict[str, int]: - pattern = re.compile(r"([A-Z][a-z]*)(\d*)") - elements = pattern.findall(formula) - element_stoich: Dict[str, int] = defaultdict(int) - for element, count in elements: - count = int(count) if count else 1 - element_stoich[element] += count - return element_stoich def get_element_valence(element: str) -> int: try: From bbf35d49fdb1ed50cf2a98681a21366eb91192ba Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Wed, 28 Aug 2024 20:11:33 +0100 Subject: [PATCH 21/44] Add contributing guide --- CONTRIBUTING.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..58165abb --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,51 @@ +# Contributing + +This is a quick guide on how to follow best practice and contribute smoothly to `SMACT`. + +## Workflow + +We follow the [GitHub flow] +(), using +branches for new work and pull requests for verifying the work. + +The steps for a new piece of work can be summarised as follows: + +1. Push up or create [an issue](https://github.com/WMD-group/SMACT/issues). +2. Create a branch from main, with a sensible name that relates to the issue. +3. Do the work and commit changes to the branch. Push the branch + regularly to GitHub to make sure no work is accidentally lost. +4. Write or update unit tests for the code you work on. +5. When you are finished with the work, ensure that all of the unit + tests pass on your own machine. +6. Open a pull request [on the pull request page](https://github.com/WMD-group/SMACT/pulls). +7. If nobody acknowledges your pull request promptly, feel free to poke one of the main developers into action. + +## Pull requests + +For a general overview of using pull requests on GitHub look [in the GitHub docs](https://help.github.com/en/articles/about-pull-requests). + +When creating a pull request you should: + +- Ensure that the title succinctly describes the changes so it is easy to read on the overview page +- Reference the issue which the pull request is closing + +Recommended reading: [How to Write the Perfect Pull Request](https://github.blog/2015-01-21-how-to-write-the-perfect-pull-request/) + +## Dev requirements + +When developing locally, it is recommended to install the python packages in `requirements-dev.txt`. + +```bash +pip install -r requirements-dev.txt +``` + +This will allow you to run the tests locally with pytest as described in the main README, +as well as run pre-commit hooks to automatically format python files with isort and black. +To install the pre-commit hooks (only needs to be done once): + +```bash +pre-commit install +pre-commit run --all-files # optionally run hooks on all files +``` + +Pre-commit hooks will check all files when you commit changes, automatically fixing any files which are not formatted correctly. Those files will need to be staged again before re-attempting the commit. From 6dbda8b1cfdaa35860d5c02a9326dc5e0e5a39c1 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Thu, 29 Aug 2024 11:48:55 +0100 Subject: [PATCH 22/44] Add form_maker and comp_maker to utils --- smact/utils/composition.py | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/smact/utils/composition.py b/smact/utils/composition.py index a5091e43..284fd59a 100644 --- a/smact/utils/composition.py +++ b/smact/utils/composition.py @@ -2,6 +2,10 @@ import re from collections import defaultdict +from pymatgen.core import Composition + +from smact.structure_prediction.utilities import unparse_spec + # Adapted from ElementEmbeddings and Pymatgen def parse_formula(formula: str) -> dict[str, int]: @@ -45,3 +49,47 @@ def _get_sym_dict(formula: str, factor: float) -> dict[str, float]: raise ValueError(msg) return sym_dict + + +def comp_maker( + smact_filter_output: tuple[str, int, int] | tuple[str, int] +) -> Composition: + """Convert an output of smact.screening.smact_filer into a Pymatgen Compositions + + Args: + smact_filter_output (tuple[str, int, int]|tuple[str, int]): An item in the list returned from smact_filter + + Returns: + composition (pymatgen.core.Composition): An instance of the Composition class + """ + if len(smact_filter_output) == 2: + form = [] + for el, ammt in zip(smact_filter_output[0], smact_filter_output[-1]): + form.append(el) + form.append(ammt) + form = "".join(str(e) for e in form) + else: + form = {} + for el, ox, ammt in zip( + smact_filter_output[0], + smact_filter_output[1], + smact_filter_output[2], + ): + sp = unparse_spec((el, ox)) + form[sp] = ammt + return Composition(form) + + +def formula_maker( + smact_filter_output: tuple[str, int, int] | tuple[str, int] +) -> str: + """Convert an output of smact.screening.smact_filter into a formula. + + Args: + smact_filter_output (tuple[str, int, int]|tuple[str, int]): An item in the list returned from smact_filter + + Returns: + formula (str): A formula + + """ + return comp_maker(smact_filter_output).reduced_formula From df08b83b2061e12efbb5075f47f6d4ba312e6dd1 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Thu, 29 Aug 2024 11:49:10 +0100 Subject: [PATCH 23/44] Add tests for comp_maker and form_maker --- smact/tests/test_utils.py | 46 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/smact/tests/test_utils.py b/smact/tests/test_utils.py index 7df1ebb7..aad99f6c 100644 --- a/smact/tests/test_utils.py +++ b/smact/tests/test_utils.py @@ -1,12 +1,29 @@ import unittest -from smact.utils.composition import parse_formula +from pymatgen.core import Composition + +from smact import Element +from smact.screening import smact_filter +from smact.utils.composition import comp_maker, formula_maker, parse_formula class TestComposition(unittest.TestCase): """Test composition utilities""" + def setUp(self) -> None: + self.mock_filter_output = [ + (("Fe", "O"), (2, -2), (1, 1)), + (("Fe", "O"), (1, 1)), + (("Fe", "Fe", "O"), (2, 3, -2), (1, 2, 4)), + ] + self.smact_filter_output = smact_filter( + els=[Element("Li"), Element("Ge"), Element("P"), Element("S")], + stoichs=[[10], [1], [2], [12]], + ) + def test_parse_formula(self): + """Test the parse_formula function""" + formulas = ["Li10GeP2S12", "Mg0.5O0.5", "CaMg(CO3)2"] LGPS = parse_formula(formulas[0]) @@ -30,3 +47,30 @@ def test_parse_formula(self): self.assertEqual(dolomite["Mg"], 1) self.assertEqual(dolomite["C"], 2) self.assertEqual(dolomite["O"], 6) + + def test_comp_maker(self): + """Test the comp_maker function""" + comp1 = comp_maker(self.mock_filter_output[0]) + comp2 = comp_maker(self.mock_filter_output[1]) + comp3 = comp_maker(self.mock_filter_output[2]) + comp4 = comp_maker(self.smact_filter_output[1]) + for comp in [comp1, comp2, comp3, comp4]: + self.assertIsInstance(comp, Composition) + self.assertEqual(Composition("FeO"), comp2) + self.assertEqual(Composition({"Fe2+": 1, "O2-": 1}), comp1) + self.assertEqual(Composition({"Fe2+": 1, "Fe3+": 2, "O2-": 4}), comp3) + self.assertEqual( + Composition({"Li+": 10, "Ge4+": 1, "P5+": 2, "S2-": 12}), comp4 + ) + + def test_formula_maker(self): + """Test the formula_maker function""" + form1 = formula_maker(self.mock_filter_output[0]) + form2 = formula_maker(self.mock_filter_output[1]) + form3 = formula_maker(self.mock_filter_output[2]) + form4 = formula_maker(self.smact_filter_output[1]) + self.assertEqual(form1, "FeO") + self.assertEqual(form2, "FeO") + self.assertEqual(form1, form2) + self.assertEqual(form3, "Fe3O4") + self.assertEqual(form4, "Li10Ge(PS6)2") From 508f78af9f51d52b73af89eba6c60975d990058f Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Thu, 29 Aug 2024 11:49:20 +0100 Subject: [PATCH 24/44] Fix link --- CONTRIBUTING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 58165abb..72982003 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,8 +4,7 @@ This is a quick guide on how to follow best practice and contribute smoothly to ## Workflow -We follow the [GitHub flow] -(), using +We follow the [GitHub flow](), using branches for new work and pull requests for verifying the work. The steps for a new piece of work can be summarised as follows: From 92fca9fe40a5974fd48e561d19381652e70bea0c Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Thu, 29 Aug 2024 12:21:51 +0100 Subject: [PATCH 25/44] Add __future__ import --- smact/utils/composition.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/smact/utils/composition.py b/smact/utils/composition.py index 284fd59a..1b04119c 100644 --- a/smact/utils/composition.py +++ b/smact/utils/composition.py @@ -1,4 +1,6 @@ """Utility functioms for handling elements, species, formulas and composition""" +from __future__ import annotations + import re from collections import defaultdict From 4bf54cef8fb36fa1a559d356a1b12cd3ce51a028 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Thu, 29 Aug 2024 12:22:17 +0100 Subject: [PATCH 26/44] Correct the type hint --- smact/utils/composition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smact/utils/composition.py b/smact/utils/composition.py index 1b04119c..b833bc09 100644 --- a/smact/utils/composition.py +++ b/smact/utils/composition.py @@ -10,7 +10,7 @@ # Adapted from ElementEmbeddings and Pymatgen -def parse_formula(formula: str) -> dict[str, int]: +def parse_formula(formula: str) -> dict[str, float]: """Parse a formula into a dict of el:amt Args: From 87e4f612144cf92b444ae0fe6097b04503e2aec0 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Thu, 29 Aug 2024 13:37:40 +0100 Subject: [PATCH 27/44] Add __init__.py in utils --- smact/utils/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 smact/utils/__init__.py diff --git a/smact/utils/__init__.py b/smact/utils/__init__.py new file mode 100644 index 00000000..bf0ca3f4 --- /dev/null +++ b/smact/utils/__init__.py @@ -0,0 +1 @@ +"""Utility functions for SMACT.""" \ No newline at end of file From 7a8a13b4c9e3d5f62b84901e1c6e7989bfe32d9d Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Thu, 29 Aug 2024 13:37:54 +0100 Subject: [PATCH 28/44] Run pre-commit --- smact/utils/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smact/utils/__init__.py b/smact/utils/__init__.py index bf0ca3f4..bdf9739d 100644 --- a/smact/utils/__init__.py +++ b/smact/utils/__init__.py @@ -1 +1 @@ -"""Utility functions for SMACT.""" \ No newline at end of file +"""Utility functions for SMACT.""" From b06704808cd76005303c0beff0f7231b6048e9be Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Thu, 29 Aug 2024 13:40:54 +0100 Subject: [PATCH 29/44] Add utils to packages in setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 421b7136..1b786a3c 100644 --- a/setup.py +++ b/setup.py @@ -32,6 +32,7 @@ license="MIT", packages=[ "smact", + "smact.utils", "smact.tests", "smact.structure_prediction", "smact.dopant_prediction", From e34abb8e19e437014ed951ac8a6ad0428fac6afb Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Thu, 29 Aug 2024 14:22:32 +0100 Subject: [PATCH 30/44] Add docs for utils --- docs/smact.utils.compositions.rst | 9 +++++++++ docs/smact.utils.rst | 11 +++++++++++ 2 files changed, 20 insertions(+) create mode 100644 docs/smact.utils.compositions.rst create mode 100644 docs/smact.utils.rst diff --git a/docs/smact.utils.compositions.rst b/docs/smact.utils.compositions.rst new file mode 100644 index 00000000..68f87a73 --- /dev/null +++ b/docs/smact.utils.compositions.rst @@ -0,0 +1,9 @@ +SMACT Utilities Composition Module +===================================== + +Miscellaneous utilities for composition handling + +.. automodule:: smact.utils.composition + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/smact.utils.rst b/docs/smact.utils.rst new file mode 100644 index 00000000..06d0a868 --- /dev/null +++ b/docs/smact.utils.rst @@ -0,0 +1,11 @@ +SMACT Utilities module +=========================== + +The utilities module provides some utilty functions to support the core functionalities of SMACT + +Submodules +---------- + +.. toctree:: + + smact.utils.composition From 487247a11482e5538addb7b11231897d89ab6749 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Thu, 29 Aug 2024 14:23:08 +0100 Subject: [PATCH 31/44] Fix name --- .../{smact.utils.compositions.rst => smact.utils.composition.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{smact.utils.compositions.rst => smact.utils.composition.rst} (100%) diff --git a/docs/smact.utils.compositions.rst b/docs/smact.utils.composition.rst similarity index 100% rename from docs/smact.utils.compositions.rst rename to docs/smact.utils.composition.rst From 83507d80ed6b163cca8dfe2843bc167d9d482fc1 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Thu, 29 Aug 2024 15:05:30 +0100 Subject: [PATCH 32/44] Add utils to docs --- docs/smact.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/smact.rst b/docs/smact.rst index 215effd8..82783775 100644 --- a/docs/smact.rst +++ b/docs/smact.rst @@ -25,6 +25,7 @@ Submodules smact.structure_prediction smact.dopant_prediction + smact.utils smact.properties smact.screening smact.oxidation_states From b937faec36584b171e6999ca5a9b6e3d6fe054fb Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Fri, 30 Aug 2024 11:00:20 +0100 Subject: [PATCH 33/44] Modify if else block for valence data for conciseness --- smact/__init__.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/smact/__init__.py b/smact/__init__.py index 5ef153d6..58b5afcb 100644 --- a/smact/__init__.py +++ b/smact/__init__.py @@ -165,10 +165,9 @@ def __init__( num_valence = None valence_data = data_loader.lookup_element_valence_data(symbol) - if valence_data: - num_valence_modified = valence_data["NValence"] - else: - num_valence_modified = None + num_valence_modified = ( + valence_data["NValence"] if valence_data else None + ) for attribute, value in ( ("coord_envs", coord_envs), From 54672d0f5e12a8a4ee56ae694fb67c296f6d61a5 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Fri, 30 Aug 2024 11:02:38 +0100 Subject: [PATCH 34/44] Improve docstring in parse_formula --- smact/utils/composition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smact/utils/composition.py b/smact/utils/composition.py index b833bc09..4d9ae4b2 100644 --- a/smact/utils/composition.py +++ b/smact/utils/composition.py @@ -11,7 +11,7 @@ # Adapted from ElementEmbeddings and Pymatgen def parse_formula(formula: str) -> dict[str, float]: - """Parse a formula into a dict of el:amt + """Parse a chemical formula into a dictionary of elements and their amounts. Args: formula (str): Chemical formula From 97034aad64d8c2531ed792c93a21f2773888f3fe Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Fri, 30 Aug 2024 11:04:49 +0100 Subject: [PATCH 35/44] Improve docstring and use a dictionary comprehension for simplification --- smact/utils/composition.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/smact/utils/composition.py b/smact/utils/composition.py index 4d9ae4b2..0365104c 100644 --- a/smact/utils/composition.py +++ b/smact/utils/composition.py @@ -56,7 +56,7 @@ def _get_sym_dict(formula: str, factor: float) -> dict[str, float]: def comp_maker( smact_filter_output: tuple[str, int, int] | tuple[str, int] ) -> Composition: - """Convert an output of smact.screening.smact_filer into a Pymatgen Compositions + """Convert an item in the output of smact.screening.smact_filer into a Pymatgen Composition. Args: smact_filter_output (tuple[str, int, int]|tuple[str, int]): An item in the list returned from smact_filter @@ -71,14 +71,14 @@ def comp_maker( form.append(ammt) form = "".join(str(e) for e in form) else: - form = {} - for el, ox, ammt in zip( - smact_filter_output[0], - smact_filter_output[1], - smact_filter_output[2], - ): - sp = unparse_spec((el, ox)) - form[sp] = ammt + form = { + unparse_spec((el, ox)): ammt + for el, ox, ammt in zip( + smact_filter_output[0], + smact_filter_output[1], + smact_filter_output[2], + ) + } return Composition(form) From 32d7f23f22d465e37aafaced39ae1314d1ad217e Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Fri, 30 Aug 2024 11:05:43 +0100 Subject: [PATCH 36/44] Improve docstring --- smact/utils/composition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smact/utils/composition.py b/smact/utils/composition.py index 0365104c..29bc660a 100644 --- a/smact/utils/composition.py +++ b/smact/utils/composition.py @@ -85,7 +85,7 @@ def comp_maker( def formula_maker( smact_filter_output: tuple[str, int, int] | tuple[str, int] ) -> str: - """Convert an output of smact.screening.smact_filter into a formula. + """Convert an item in the output of smact.screening.smact_filter into a chemical formula. Args: smact_filter_output (tuple[str, int, int]|tuple[str, int]): An item in the list returned from smact_filter From a5bcf698706ed9a8c3aebe8602ac4ef44de2e654 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Fri, 30 Aug 2024 11:06:39 +0100 Subject: [PATCH 37/44] Improve docstring --- smact/properties.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smact/properties.py b/smact/properties.py index 3d392477..896e78e6 100644 --- a/smact/properties.py +++ b/smact/properties.py @@ -165,7 +165,7 @@ def compound_electroneg( def valence_electron_count(compound: str) -> float: """ - Calculate the Valence Electron Count (VEC) for a given compound. + Calculate the Valence Electron Count (VEC) for a given chemical compound. This function parses the input compound, extracts the elements and their stoichiometries, and calculates the VEC using the valence electron data From 69dae14b6fcd35c74c9698cd078c9c2888a08cef Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Fri, 30 Aug 2024 11:07:58 +0100 Subject: [PATCH 38/44] Remove unused imports --- smact/properties.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/smact/properties.py b/smact/properties.py index 896e78e6..8ccd52f1 100644 --- a/smact/properties.py +++ b/smact/properties.py @@ -1,6 +1,3 @@ -import csv -import re -from collections import defaultdict from typing import List, Optional, Union import numpy as np From 50865b785ebfc4aa84734b031f6d44bd64afbf20 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Fri, 30 Aug 2024 11:11:26 +0100 Subject: [PATCH 39/44] Update metadata --- setup.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 1b786a3c..a93d5d6a 100644 --- a/setup.py +++ b/setup.py @@ -1,14 +1,15 @@ #!/usr/bin/env python -__author__ = "Daniel W. Davies" -__author_email__ = "d.w.davies@imperial.ac.uk" +__author__ = "The SMACT Developers" +__author_email__ = "a.walsh@imperial.ac.uk" __copyright__ = ( "Copyright Daniel W. Davies, Adam J. Jackson, Keith T. Butler (2019)" ) -__version__ = "2.6" +__version__ = "2.7" __maintainer__ = "Anthony O. Onwuli" -__maintaier_email__ = "anthony.onwuli16@imperial.ac.uk" -__date__ = "July 10 2024" +__maintainer_email__ = "anthony.onwuli16@imperial.ac.uk" +__date__ = "August 30 2024" + import os import unittest @@ -28,7 +29,7 @@ author=__author__, author_email=__author_email__, maintainer=__maintainer__, - maintainer_email=__maintaier_email__, + maintainer_email=__maintainer_email__, license="MIT", packages=[ "smact", From 4b0ea93f8d09dc9d49df191eab51286f0c1458d9 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Fri, 30 Aug 2024 11:12:12 +0100 Subject: [PATCH 40/44] Update conf.py --- docs/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index e65a0e31..b378c4e8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -60,9 +60,9 @@ # built documents. # # The short X.Y version. -version = "2.6" +version = "2.7" # The full version, including alpha/beta/rc tags. -release = "2.6.0" +release = "2.7.0" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. From 61990012046f77a3121d07540182684ce5da5490 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Fri, 30 Aug 2024 15:32:25 +0100 Subject: [PATCH 41/44] Add warnings about changing SMACT defaults --- smact/__init__.py | 10 ++++++++-- smact/screening.py | 28 ++++++++++++++++++---------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/smact/__init__.py b/smact/__init__.py index 58b5afcb..e967bccf 100644 --- a/smact/__init__.py +++ b/smact/__init__.py @@ -53,9 +53,11 @@ class Element: Element.oxidation_states (list) : Default list of allowed oxidation states for use in SMACT - Element.oxidation_states_sp (list) : List of oxdation states recognised by the Pymatgen Structure Predictor + Element.oxidation_states_smact14 (list): Original list of oxidation states that were manually compiled for SMACT in 2014 (default in SMACT < 3.0) - Element.oxidation_states_icsd (list) : List of oxidation states that appear in the ICSD + Element.oxidation_states_sp (list) : List of oxidation states recognised by the Pymatgen Structure Predictor + + Element.oxidation_states_icsd (list) : List of oxidation states that appear in the 2016 version of ICSD Element.oxidation_states_wiki (list): List of oxidation states that appear wikipedia (https://en.wikipedia.org/wiki/Template:List_of_oxidation_states_of_the_elements) Data retrieved: 2022-09-22 @@ -186,6 +188,10 @@ def __init__( "oxidation_states", data_loader.lookup_element_oxidation_states(symbol), ), + ( + "oxidation_states_smact14", + data_loader.lookup_element_oxidation_states(symbol), + ), ( "oxidation_states_icsd", data_loader.lookup_element_oxidation_states_icsd(symbol), diff --git a/smact/screening.py b/smact/screening.py index d3e45945..dd700d18 100644 --- a/smact/screening.py +++ b/smact/screening.py @@ -321,19 +321,23 @@ def smact_filter( threshold: Optional[int] = 8, stoichs: Optional[List[List[int]]] = None, species_unique: bool = True, - oxidation_states_set: str = "default", + oxidation_states_set: str = "smact14", comp_tuple: bool = False, ) -> Union[List[Tuple[str, int, int]], List[Tuple[str, int]]]: """Function that applies the charge neutrality and electronegativity tests in one go for simple application in external scripts that wish to apply the general 'smact test'. + .. warning:: + For backwards compatability in SMACT >=2.7, expllicitly set oxidation_states_set to 'smact14' if you wish to use the 2014 SMACT default oxidation states. + In SMACT 3.0, the smact_filter function will be set to use a new default oxidation states set. + Args: els (tuple/list): A list of smact.Element objects threshold (int): Threshold for stoichiometry limit, default = 8 stoichs (list[int]): A selection of valid stoichiometric ratios for each site. species_unique (bool): Whether or not to consider elements in different oxidation states as unique in the results. - oxidation_states_set (string): A string to choose which set of oxidation states should be chosen. Options are 'default', 'icsd', 'pymatgen' and 'wiki' for the default, icsd, pymatgen structure predictor and Wikipedia (https://en.wikipedia.org/wiki/Template:List_of_oxidation_states_of_the_elements) oxidation states respectively. A filepath to an oxidation states text file can also be supplied as well. + oxidation_states_set (string): A string to choose which set of oxidation states should be chosen. Options are 'smact14', 'icsd', 'pymatgen' and 'wiki' for the 2014 SMACT default, 2016 ICSD, pymatgen structure predictor and Wikipedia (https://en.wikipedia.org/wiki/Template:List_of_oxidation_states_of_the_elements) oxidation states respectively. A filepath to an oxidation states text file can also be supplied as well. comp_tuple (bool): Whether or not to return the results as a named tuple of elements and stoichiometries (True) or as a normal tuple of elements and stoichiometries (False). Returns: allowed_comps (list): Allowed compositions for that chemical system @@ -373,7 +377,7 @@ def smact_filter( # Select the specified oxidation states set: oxi_set = { - "default": [e.oxidation_states for e in els], + "smact14": [e.oxidation_states_smact14 for e in els], "icsd": [e.oxidation_states_icsd for e in els], "pymatgen": [e.oxidation_states_sp for e in els], "wiki": [e.oxidation_states_wiki for e in els], @@ -385,7 +389,7 @@ def smact_filter( else: raise ( Exception( - f'{oxidation_states_set} is not valid. Enter either "default", "icsd", "pymatgen","wiki" or a filepath to a textfile of oxidation states.' + f'{oxidation_states_set} is not valid. Enter either "smact14", "icsd", "pymatgen","wiki" or a filepath to a textfile of oxidation states.' ) ) if oxidation_states_set == "wiki": @@ -430,19 +434,23 @@ def smact_validity( composition: Union[pymatgen.core.Composition, str], use_pauling_test: bool = True, include_alloys: bool = True, - oxidation_states_set: Union[str, bytes, os.PathLike] = "default", + oxidation_states_set: Union[str, bytes, os.PathLike] = "smact14", ) -> bool: """Check if a composition is valid according to the SMACT rules. Composition is considered valid if it passes the charge neutrality test and the Pauling electronegativity test. + .. warning:: + For backwards compatability in SMACT >=2.7, expllicitly set oxidation_states_set to 'smact14' if you wish to use the 2014 SMACT default oxidation states. + In SMACT 3.0, the smact_filter function will be set to use a new default oxidation states set. + Args: composition (Union[pymatgen.core.Composition, str]): Composition/formula to check. This can be a pymatgen Composition object or a string. use_pauling_test (bool): Whether to use the Pauling electronegativity test include_alloys (bool): If True, compositions which only contain metal elements will be considered valid without further checks. oxidation_states_set (Union[str, bytes, os.PathLike]): A string to choose which set of - oxidation states should be chosen for charge-balancing. Options are 'default', 'icsd', - 'pymatgen' and 'wiki' for the default, icsd, pymatgen structure predictor and Wikipedia + oxidation states should be chosen for charge-balancing. Options are 'smact14', 'icsd', + 'pymatgen' and 'wiki' for the 2014 SMACT default, 2016 ICSD, pymatgen structure predictor and Wikipedia (https://en.wikipedia.org/wiki/Template:List_of_oxidation_states_of_the_elements) oxidation states respectively. A filepath to an oxidation states text file can also be supplied. @@ -471,8 +479,8 @@ def smact_validity( smact_elems = [e[1] for e in space.items()] electronegs = [e.pauling_eneg for e in smact_elems] - if oxidation_states_set == "default" or oxidation_states_set is None: - ox_combos = [e.oxidation_states for e in smact_elems] + if oxidation_states_set == "smact14" or oxidation_states_set is None: + ox_combos = [e.oxidation_states_smact14 for e in smact_elems] elif oxidation_states_set == "icsd": ox_combos = [e.oxidation_states_icsd for e in smact_elems] elif oxidation_states_set == "pymatgen": @@ -491,7 +499,7 @@ def smact_validity( else: raise ( Exception( - f'{oxidation_states_set} is not valid. Enter either "default", "icsd", "pymatgen","wiki" or a filepath to a textfile of oxidation states.' + f'{oxidation_states_set} is not valid. Enter either "smact14", "icsd", "pymatgen","wiki" or a filepath to a textfile of oxidation states.' ) ) From add458e3f0264cc92722aad325803024ddde8066 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Fri, 30 Aug 2024 16:36:44 +0100 Subject: [PATCH 42/44] Update contact info in the README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 793a36df..0b1ff41b 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ Development notes ----------------- ### Bugs, features and questions -Please use the [Issue Tracker](https://github.com/WMD-group/smact/issues) to report bugs or request features in the first instance. While we hope that most questions can be answered by searching [the docs](https://smact.readthedocs.io/en/latest/), we welcome new questions on the issue tracker, especially if they helps us improve the docs! For other queries about any aspect of the code, please contact either Dan Davies (author) or Anthony Onwuli (maintainer) by e-mail: d.w.davies@imperial.ac.uk or anthony.onwuli16@imperial.ac.uk respectively. +Please use the [Issue Tracker](https://github.com/WMD-group/smact/issues) to report bugs or request features in the first instance. While we hope that most questions can be answered by searching [the docs](https://smact.readthedocs.io/en/latest/), we welcome new questions on the issue tracker, especially if they helps us improve the docs! For other queries about any aspect of the code, please contact either Aron Walsh on behalf of The SMACT Developers (author) or Anthony Onwuli (maintainer) by e-mail: a.walsh@imperial.ac.uk or anthony.onwuli16@imperial.ac.uk respectively. ### Code contributions We are always looking for ways to make SMACT better and more useful to the wider community; contributions are welcome. Please use the ["Fork and Pull"](https://guides.github.com/activities/forking/) workflow to make contributions and stick as closely as possible to the following: @@ -130,6 +130,7 @@ We are always looking for ways to make SMACT better and more useful to the wider - Code style should comply with [PEP8](http://www.python.org/dev/peps/pep-0008) where possible. [Google's house style](https://google.github.io/styleguide/pyguide.html) is also helpful, including a good model for docstrings. - Please use comments liberally when adding nontrivial features, and take the chance to clean up other people's code while looking at it. - Add tests wherever possible, and use the test suite to check if you broke anything. +- Look at the [contributing guide](CONTRIBUTING.md) for more information. ### Tests Testing modules should be pass/fail and wrapped into **tests/test_core.py** or another **tests/test_something.py** file added, if appropriate. From 4dc711737bcaae5defbb23dda9d13e14675d42c2 Mon Sep 17 00:00:00 2001 From: Anthony Onwuli Date: Fri, 30 Aug 2024 16:40:48 +0100 Subject: [PATCH 43/44] Update requirement.txt --- requirements.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 0cdba503..5ae10dcd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile with Python 3.10 # by the following command: # -# pip-compile +# pip-compile --output-file=requirements.txt setup.py # ase==3.23.0 # via SMACT (setup.py) @@ -20,8 +20,6 @@ dill==0.3.8 # pathos fonttools==4.53.0 # via matplotlib -future==1.0.0 - # via uncertainties idna==3.7 # via requests joblib==1.4.2 @@ -34,11 +32,11 @@ matplotlib==3.8.2 # via # ase # pymatgen -monty==2023.11.3 +monty==2024.7.30 # via pymatgen mpmath==1.3.0 # via sympy -multiprocess==0.70.15 +multiprocess==0.70.16 # via pathos networkx==3.2.1 # via pymatgen @@ -74,7 +72,7 @@ ppft==1.7.6.8 # via pathos pybtex==0.24.0 # via pymatgen -pymatgen==2023.11.12 +pymatgen==2024.5.1 # via SMACT (setup.py) pyparsing==3.1.1 # via matplotlib @@ -114,6 +112,8 @@ tenacity==8.2.3 # via plotly tqdm==4.66.4 # via pymatgen +typing-extensions==4.12.2 + # via SMACT (setup.py) tzdata==2024.1 # via pandas uncertainties==3.2.2 From a7946c9bafc67a254818d61b26ee0ecd15b2c66a Mon Sep 17 00:00:00 2001 From: Anthony Onwuli <30937913+AntObi@users.noreply.github.com> Date: Fri, 30 Aug 2024 16:52:42 +0100 Subject: [PATCH 44/44] Update README.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b1ff41b..3b53c7f3 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ Development notes ----------------- ### Bugs, features and questions -Please use the [Issue Tracker](https://github.com/WMD-group/smact/issues) to report bugs or request features in the first instance. While we hope that most questions can be answered by searching [the docs](https://smact.readthedocs.io/en/latest/), we welcome new questions on the issue tracker, especially if they helps us improve the docs! For other queries about any aspect of the code, please contact either Aron Walsh on behalf of The SMACT Developers (author) or Anthony Onwuli (maintainer) by e-mail: a.walsh@imperial.ac.uk or anthony.onwuli16@imperial.ac.uk respectively. +Please use the [Issue Tracker](https://github.com/WMD-group/smact/issues) to report bugs or request features in the first instance. While we hope that most questions can be answered by searching [the docs](https://smact.readthedocs.io/en/latest/), we welcome new questions on the issue tracker, especially if they help us improve the docs! For other queries about any aspect of the code, please contact either Aron Walsh on behalf of The SMACT Developers (author) or Anthony Onwuli (maintainer) by e-mail: a.walsh@imperial.ac.uk or anthony.onwuli16@imperial.ac.uk respectively. ### Code contributions We are always looking for ways to make SMACT better and more useful to the wider community; contributions are welcome. Please use the ["Fork and Pull"](https://guides.github.com/activities/forking/) workflow to make contributions and stick as closely as possible to the following: