|
|
import streamlit as st |
|
|
import plotly.graph_objects as go |
|
|
import numpy as np |
|
|
|
|
|
|
|
|
elements = { |
|
|
1: ("Hydrogen", "H"), |
|
|
2: ("Helium", "He"), |
|
|
3: ("Lithium", "Li"), |
|
|
4: ("Beryllium", "Be"), |
|
|
5: ("Boron", "B"), |
|
|
6: ("Carbon", "C"), |
|
|
7: ("Nitrogen", "N"), |
|
|
8: ("Oxygen", "O"), |
|
|
9: ("Fluorine", "F"), |
|
|
10: ("Neon", "Ne"), |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
def get_electron_shells(electrons): |
|
|
shells = [] |
|
|
n = 1 |
|
|
while electrons > 0: |
|
|
max_electrons = 2 * n**2 |
|
|
if electrons >= max_electrons: |
|
|
shells.append(max_electrons) |
|
|
electrons -= max_electrons |
|
|
else: |
|
|
shells.append(electrons) |
|
|
electrons = 0 |
|
|
n += 1 |
|
|
return shells |
|
|
|
|
|
|
|
|
st.title("Atomic Structure Visualizer") |
|
|
|
|
|
|
|
|
col1, col2, col3 = st.columns(3) |
|
|
with col1: |
|
|
Z = st.number_input("Number of protons (Z)", min_value=1, max_value=118, value=6) |
|
|
with col2: |
|
|
N = st.number_input("Number of neutrons (N)", min_value=0, value=6) |
|
|
with col3: |
|
|
E = st.number_input("Number of electrons (E)", min_value=0, value=Z) |
|
|
|
|
|
|
|
|
A = Z + N |
|
|
charge = Z - E |
|
|
name, symbol = elements.get(Z, ("Unknown", "?")) |
|
|
|
|
|
|
|
|
st.subheader("Atomic Information") |
|
|
st.write(f"**Element**: {name} ({symbol})") |
|
|
st.write(f"**Atomic Number**: {Z}") |
|
|
st.write(f"**Mass Number**: {A}") |
|
|
st.write(f"**Number of Electrons**: {E}") |
|
|
st.write(f"**Charge**: {'+' if charge > 0 else ''}{charge}" if charge != 0 else "Neutral atom") |
|
|
|
|
|
|
|
|
shells = get_electron_shells(E) |
|
|
|
|
|
|
|
|
nucleus_radius = 0.1 |
|
|
|
|
|
|
|
|
proton_theta = np.random.uniform(0, 2 * np.pi, Z) |
|
|
proton_r = np.random.uniform(0, nucleus_radius, Z) |
|
|
proton_x = proton_r * np.cos(proton_theta) |
|
|
proton_y = proton_r * np.sin(proton_theta) |
|
|
|
|
|
|
|
|
neutron_theta = np.random.uniform(0, 2 * np.pi, N) |
|
|
neutron_r = np.random.uniform(0, nucleus_radius, N) |
|
|
neutron_x = neutron_r * np.cos(neutron_theta) |
|
|
neutron_y = neutron_r * np.sin(neutron_theta) |
|
|
|
|
|
|
|
|
electron_x = [] |
|
|
electron_y = [] |
|
|
r_shell = 1.0 |
|
|
for n, k in enumerate(shells, start=1): |
|
|
r = n * r_shell |
|
|
angles = np.linspace(0, 2 * np.pi, k, endpoint=False) |
|
|
for angle in angles: |
|
|
electron_x.append(r * np.cos(angle)) |
|
|
electron_y.append(r * np.sin(angle)) |
|
|
|
|
|
|
|
|
fig = go.Figure() |
|
|
|
|
|
|
|
|
fig.add_trace(go.Scatter( |
|
|
x=proton_x, y=proton_y, |
|
|
mode='markers', |
|
|
name='Protons', |
|
|
marker=dict(color='red', size=8) |
|
|
)) |
|
|
|
|
|
|
|
|
fig.add_trace(go.Scatter( |
|
|
x=neutron_x, y=neutron_y, |
|
|
mode='markers', |
|
|
name='Neutrons', |
|
|
marker=dict(color='blue', size=8) |
|
|
)) |
|
|
|
|
|
|
|
|
fig.add_trace(go.Scatter( |
|
|
x=electron_x, y=electron_y, |
|
|
mode='markers', |
|
|
name='Electrons', |
|
|
marker=dict(color='green', size=10) |
|
|
)) |
|
|
|
|
|
|
|
|
for n in range(1, len(shells) + 1): |
|
|
fig.add_shape( |
|
|
type="circle", |
|
|
x0=-n * r_shell, y0=-n * r_shell, |
|
|
x1=n * r_shell, y1=n * r_shell, |
|
|
line=dict(color="gray", width=1, dash="dash") |
|
|
) |
|
|
|
|
|
|
|
|
max_r = max(0.5, len(shells) * r_shell) |
|
|
fig.update_xaxes(range=[-max_r, max_r]) |
|
|
fig.update_yaxes(range=[-max_r, max_r], scaleanchor="x", scaleratio=1) |
|
|
|
|
|
|
|
|
fig.update_layout( |
|
|
title="Atomic Structure", |
|
|
width=600, |
|
|
height=600, |
|
|
showlegend=True |
|
|
) |
|
|
|
|
|
|
|
|
st.plotly_chart(fig) |
|
|
|
|
|
|
|
|
st.markdown(""" |
|
|
### How to Use |
|
|
- **Protons (Z)**: Defines the element (e.g., Z=6 for Carbon). |
|
|
- **Neutrons (N)**: Affects the mass number (A = Z + N). |
|
|
- **Electrons (E)**: Determines if it's an ion (Charge = Z - E). |
|
|
- Adjust the inputs to visualize different atoms or ions! |
|
|
""") |