Vishwas1 commited on
Commit
92747d2
·
verified ·
1 Parent(s): 82e894f

Upload app (2).py

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