Vishwas1 commited on
Commit
f395cf7
·
verified ·
1 Parent(s): 4302be8

Upload 2 files

Browse files
Files changed (2) hide show
  1. app (3).py +264 -0
  2. requirements (2).txt +5 -0
app (3).py ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import math
3
+ from typing import Dict, List, Optional
4
+
5
+ import gradio as gr
6
+ import pandas as pd
7
+ import numpy as np
8
+ import matplotlib.pyplot as plt
9
+ from periodictable import elements
10
+
11
+ NUMERIC_PROPS = [
12
+ ("mass", "Atomic mass (u)"),
13
+ ("density", "Density (g/cm^3)"),
14
+ ("electronegativity", "Pauling electronegativity"),
15
+ ("boiling_point", "Boiling point (K)"),
16
+ ("melting_point", "Melting point (K)"),
17
+ ("vdw_radius", "van der Waals radius (pm)"),
18
+ ("covalent_radius", "Covalent radius (pm)"),
19
+ ]
20
+
21
+ CURATED_FACTS: Dict[str, List[str]] = {
22
+ "H": ["Lightest element; ~74% of the visible universe by mass is hydrogen in stars."],
23
+ "He": ["Inert, used in cryogenics and balloons; second lightest element."],
24
+ "Li": ["Batteries MVP: lithium-ion cells power phones and EVs."],
25
+ "C": ["Backbone of life; diamond and graphite are pure carbon with wildly different properties."],
26
+ "N": ["~78% of Earth's atmosphere is nitrogen (mostly N₂)."],
27
+ "O": ["Essential for respiration; ~21% of Earth's atmosphere."],
28
+ "Na": ["Sodium metal reacts violently with water—handle only under oil or inert gas."],
29
+ "Mg": ["Burns with a bright white flame; used in flares and fireworks."],
30
+ "Al": ["Light and strong; forms a protective oxide layer that resists corrosion."],
31
+ "Si": ["Silicon is the basis of modern electronics—hello, semiconductors."],
32
+ "Cl": ["Powerful disinfectant; elemental chlorine is toxic, compounds are widely useful."],
33
+ "Ar": ["Argon is used to provide inert atmospheres for welding and 3D printing."],
34
+ "Fe": ["Core of steel; iron is essential in hemoglobin for oxygen transport."],
35
+ "Cu": ["Excellent electrical conductor; iconic blue-green patina (verdigris)."],
36
+ "Ag": ["Highest electrical conductivity of all metals; historically used as currency."],
37
+ "Au": ["Very unreactive ('noble'); prized for electronics and jewelry."],
38
+ "Hg": ["Only metal that's liquid at room temperature; toxic—use with care."],
39
+ "Pb": ["Dense and malleable; toxicity led to phase-out from gasoline and paints."],
40
+ "U": ["Radioactive; used as nuclear reactor fuel (U-235)."],
41
+ "Pu": ["Man-made in quantity; key in certain nuclear technologies."],
42
+ "F": ["Most electronegative element; extremely reactive."],
43
+ "Ne": ["Neon glows striking red-orange in discharge tubes—classic signs."],
44
+ "Xe": ["Xenon makes bright camera flashes and high-intensity lamps."],
45
+ }
46
+
47
+ GROUP_FACTS = {
48
+ "alkali": "Alkali metal: very reactive soft metal; forms +1 cations and reacts with water.",
49
+ "alkaline-earth": "Alkaline earth metal: reactive (less than Group 1); forms +2 cations.",
50
+ "transition": "Transition metal: often good catalysts, colorful compounds, multiple oxidation states.",
51
+ "post-transition": "Post-transition metal: softer metals with lower melting points than transition metals.",
52
+ "metalloid": "Metalloid: properties between metals and nonmetals; often semiconductors.",
53
+ "nonmetal": "Nonmetal: tends to form covalent compounds; wide range of roles in biology and materials.",
54
+ "halogen": "Halogen: very reactive nonmetals; form salts with metals and −1 oxidation state.",
55
+ "noble-gas": "Noble gas: chemically inert under most conditions; monatomic gases.",
56
+ "lanthanide": "Lanthanide: f-block rare earths; notable for magnets, lasers, and phosphors.",
57
+ "actinide": "Actinide: radioactive f-block; includes nuclear fuel materials.",
58
+ }
59
+
60
+ def classify_category(el) -> str:
61
+ try:
62
+ if el.block == "s" and el.group == 1 and el.number != 1:
63
+ return "alkali"
64
+ if el.block == "s" and el.group == 2:
65
+ return "alkaline-earth"
66
+ if el.block == "p" and el.group in (13, 14, 15, 16) and el.metallic:
67
+ return "post-transition"
68
+ if el.block == "d":
69
+ return "transition"
70
+ if el.block == "p" and el.group == 17:
71
+ return "halogen"
72
+ if el.block == "p" and el.group == 18:
73
+ return "noble-gas"
74
+ if el.block == "p" and not el.metallic:
75
+ return "nonmetal"
76
+ if el.block == "f" and 57 <= el.number <= 71:
77
+ return "lanthanide"
78
+ if el.block == "f" and 89 <= el.number <= 103:
79
+ return "actinide"
80
+ except Exception:
81
+ pass
82
+ return "nonmetal" if not getattr(el, "metallic", False) else "post-transition"
83
+
84
+ def build_elements_df() -> pd.DataFrame:
85
+ rows = []
86
+ for Z in range(1, 119):
87
+ el = elements[Z]
88
+ if el is None:
89
+ continue
90
+ data = {
91
+ "Z": el.number,
92
+ "symbol": el.symbol,
93
+ "name": el.name.title(),
94
+ "period": getattr(el, "period", None),
95
+ "group": getattr(el, "group", None),
96
+ "block": getattr(el, "block", None),
97
+ "mass": getattr(el, "mass", None),
98
+ "density": getattr(el, "density", None),
99
+ "electronegativity": getattr(el, "electronegativity", None),
100
+ "boiling_point": getattr(el, "boiling_point", None),
101
+ "melting_point": getattr(el, "melting_point", None),
102
+ "vdw_radius": getattr(el, "vdw_radius", None),
103
+ "covalent_radius": getattr(el, "covalent_radius", None),
104
+ "category": classify_category(el),
105
+ "is_radioactive": bool(getattr(el, "radioactive", False)),
106
+ }
107
+ rows.append(data)
108
+ df = pd.DataFrame(rows).sort_values("Z").reset_index(drop=True)
109
+ return df
110
+
111
+ DF = build_elements_df()
112
+ MAX_GROUP = 18
113
+ MAX_PERIOD = 7
114
+
115
+ GRID: List[List[Optional[int]]] = [[None for _ in range(MAX_GROUP)] for _ in range(MAX_PERIOD)]
116
+ for _, row in DF.iterrows():
117
+ period, group, Z = int(row["period"]), row["group"], int(row["Z"])
118
+ if group is None:
119
+ continue
120
+ GRID[period-1][group-1] = Z
121
+
122
+ LAN = [z for z in DF["Z"] if 57 <= z <= 71]
123
+ ACT = [z for z in DF["Z"] if 89 <= z <= 103]
124
+
125
+ def plot_trend(trend_df: pd.DataFrame, prop_key: str, Z: int, symbol: str):
126
+ fig, ax = plt.subplots()
127
+ ax.scatter(trend_df["Z"], trend_df[prop_key])
128
+ # highlight
129
+ sel = trend_df.loc[trend_df["Z"] == Z, prop_key]
130
+ if not sel.empty and not pd.isna(sel.values[0]):
131
+ ax.scatter([Z], [sel.values[0]], s=80)
132
+ ax.text(Z, sel.values[0], symbol, ha="center", va="bottom")
133
+ ax.set_xlabel("Atomic number (Z)")
134
+ ax.set_ylabel(dict(NUMERIC_PROPS)[prop_key])
135
+ ax.set_title(f"{dict(NUMERIC_PROPS)[prop_key]} across the periodic table")
136
+ fig.tight_layout()
137
+ return fig
138
+
139
+ def plot_heatmap(property_key: str):
140
+ prop_label = dict(NUMERIC_PROPS)[property_key]
141
+ grid_vals = np.full((MAX_PERIOD, MAX_GROUP), np.nan, dtype=float)
142
+ for r in range(MAX_PERIOD):
143
+ for c in range(MAX_GROUP):
144
+ z = GRID[r][c]
145
+ if z is None:
146
+ continue
147
+ val = DF.loc[DF['Z'] == z, property_key].values[0]
148
+ if not pd.isna(val):
149
+ grid_vals[r, c] = float(val)
150
+
151
+ fig, ax = plt.subplots()
152
+ im = ax.imshow(grid_vals, origin="upper", aspect="auto")
153
+ ax.set_xticks(range(MAX_GROUP))
154
+ ax.set_xticklabels([str(i) for i in range(1, MAX_GROUP+1)])
155
+ ax.set_yticks(range(MAX_PERIOD))
156
+ ax.set_yticklabels([str(i) for i in range(1, MAX_PERIOD+1)])
157
+ ax.set_xlabel("Group")
158
+ ax.set_ylabel("Period")
159
+ ax.set_title(f"Periodic heatmap: {prop_label}")
160
+ fig.colorbar(im, ax=ax, label=prop_label)
161
+ fig.tight_layout()
162
+ return fig
163
+
164
+ def element_info(z_or_symbol: str):
165
+ try:
166
+ if z_or_symbol.isdigit():
167
+ Z = int(z_or_symbol)
168
+ _ = elements[Z]
169
+ else:
170
+ el = elements.symbol(z_or_symbol)
171
+ Z = el.number
172
+ except Exception:
173
+ return f"Unknown element: {z_or_symbol}", None, None
174
+
175
+ row = DF.loc[DF['Z'] == Z].iloc[0].to_dict()
176
+ symbol = row['symbol']
177
+
178
+ facts = []
179
+ facts.extend(CURATED_FACTS.get(symbol, []))
180
+ facts.append(GROUP_FACTS.get(row['category'], None))
181
+ facts = [f for f in facts if f]
182
+
183
+ props_lines = [
184
+ f"{row['name']} ({symbol}), Z = {Z}",
185
+ f"Period {int(row['period'])}, Group {row['group']}, Block {row['block']} | Category: {row['category'].replace('-', ' ').title()}",
186
+ f"Atomic mass: {row['mass'] if row['mass'] else '—'} u",
187
+ f"Density: {row['density'] if row['density'] else '—'} g/cm³",
188
+ f"Electronegativity: {row['electronegativity'] if row['electronegativity'] else '—'} (Pauling)",
189
+ f"Melting point: {row['melting_point'] if row['melting_point'] else '—'} K | Boiling point: {row['boiling_point'] if row['boiling_point'] else '—'} K",
190
+ f"vdW radius: {row['vdw_radius'] if row['vdw_radius'] else '—'} pm | Covalent radius: {row['covalent_radius'] if row['covalent_radius'] else '—'} pm",
191
+ f"Radioactive: {'Yes' if row['is_radioactive'] else 'No'}",
192
+ ]
193
+ info_text = "\n".join(props_lines)
194
+ facts_text = "\n• ".join(["Interesting facts:"] + facts) if facts else "No fact on file—still cool though!"
195
+
196
+ prop_key = 'electronegativity' if not pd.isna(row['electronegativity']) else 'mass'
197
+ trend_df = DF[['Z', 'symbol', prop_key]].dropna()
198
+ fig = plot_trend(trend_df, prop_key, Z, symbol)
199
+
200
+ return info_text, facts_text, fig
201
+
202
+ def handle_button_click(z: int):
203
+ return element_info(str(z))
204
+
205
+ def search_element(query: str):
206
+ query = (query or '').strip()
207
+ if not query:
208
+ return gr.update(), gr.update(), gr.update()
209
+ return element_info(query)
210
+
211
+ with gr.Blocks(title="Interactive Periodic Table") as demo:
212
+ gr.Markdown("# 🧪 Interactive Periodic Table\nClick an element or search by symbol/name/atomic number.")
213
+
214
+ with gr.Row():
215
+ with gr.Column(scale=2):
216
+ gr.Markdown("### Main Table")
217
+ with gr.Row():
218
+ for g in range(1, 19):
219
+ gr.Markdown(f"**{g}**")
220
+ for r in range(MAX_PERIOD):
221
+ with gr.Row():
222
+ for c in range(MAX_GROUP):
223
+ z = GRID[r][c]
224
+ if z is None:
225
+ gr.Button("", interactive=False)
226
+ else:
227
+ sym = DF.loc[DF['Z'] == z, 'symbol'].values[0]
228
+ btn = gr.Button(sym)
229
+ btn.click(handle_button_click, inputs=[gr.Number(z, visible=False)], outputs=[
230
+ gr.Textbox(interactive=False), gr.Markdown(), gr.Matplotlib()])
231
+
232
+ gr.Markdown("### f-block (lanthanides & actinides)")
233
+ with gr.Row():
234
+ for z in LAN:
235
+ sym = DF.loc[DF['Z'] == z, 'symbol'].values[0]
236
+ btn = gr.Button(sym)
237
+ btn.click(handle_button_click, inputs=[gr.Number(z, visible=False)], outputs=[
238
+ gr.Textbox(interactive=False), gr.Markdown(), gr.Matplotlib()])
239
+ with gr.Row():
240
+ for z in ACT:
241
+ sym = DF.loc[DF['Z'] == z, 'symbol'].values[0]
242
+ btn = gr.Button(sym)
243
+ btn.click(handle_button_click, inputs=[gr.Number(z, visible=False)], outputs=[
244
+ gr.Textbox(interactive=False), gr.Markdown(), gr.Matplotlib()])
245
+
246
+ with gr.Column(scale=1):
247
+ search = gr.Textbox(label="Search (symbol/name/Z)", placeholder="e.g., C, Iron, 79")
248
+ info = gr.Textbox(label="Properties", lines=10, interactive=False)
249
+ facts = gr.Markdown("Select an element to see fun facts.")
250
+ trend = gr.Matplotlib()
251
+
252
+ search.submit(search_element, inputs=[search], outputs=[info, facts, trend])
253
+
254
+ gr.Markdown("### Trend heatmap")
255
+ prop = gr.Dropdown(choices=[k for k, _ in NUMERIC_PROPS], value="electronegativity", label="Property")
256
+ heat = gr.Matplotlib()
257
+
258
+ def heatmap_callback(property_key):
259
+ return plot_heatmap(property_key)
260
+ prop.change(heatmap_callback, inputs=[prop], outputs=[heat])
261
+ heat.update(plot_heatmap("electronegativity"))
262
+
263
+ if __name__ == "__main__":
264
+ demo.launch()
requirements (2).txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio>=4.29.0
2
+ pandas>=2.2.2
3
+ periodictable>=1.6.1
4
+ numpy>=1.26.0
5
+ matplotlib>=3.8.0