Vishwas1 commited on
Commit
82e894f
·
verified ·
1 Parent(s): 6d76ed4

Delete app.py

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