bohrpy 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bohrpy-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Isabel Nieto y Kevin Osorio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
bohrpy-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.1
2
+ Name: bohrpy
3
+ Version: 0.1.0
4
+ Summary: Librería para cálculos y visualización del modelo atómico de Bohr
5
+ Author: Isabel Nieto, Kevin Osorio
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/isabelnieto900/bohrpy
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+ Provides-Extra: test
11
+ License-File: LICENSE
12
+
13
+ # bohrpy
14
+
15
+ Librería en Python del modelo atómico de Bohr para el hidrógeno y átomos hidrogenoides. Calcula niveles de energía, radios de órbita y transiciones electrónicas, y dibuja esos resultados.
16
+
17
+ ## Instalación
18
+
19
+ Desde la raíz del repositorio:
20
+
21
+ ```bash
22
+ python -m venv venv
23
+ source venv/bin/activate
24
+ pip install -e ".[test]"
25
+ ```
26
+
27
+ El paquete pide estas versiones, que son las usadas en el proyecto:
28
+
29
+ - `numpy==1.26.4`
30
+ - `matplotlib==3.6.3`
31
+ - `pytest==9.2.0` para las pruebas
32
+
33
+ ## Uso básico
34
+
35
+ ```python
36
+ from bohrpy import BohrAtom
37
+
38
+ hidrogeno = BohrAtom(Z=1)
39
+
40
+ print(hidrogeno.energy_level(1)) # energía del nivel n, en eV
41
+ print(hidrogeno.orbit_radius(1)) # radio de la órbita, en metros
42
+
43
+ transicion = hidrogeno.transition(3, 2)
44
+ print(transicion["delta_E_eV"])
45
+ print(transicion["frequency_Hz"])
46
+ print(transicion["wavelength_m"])
47
+ print(transicion["type"]) # "emisión" o "absorción"
48
+
49
+ hidrogeno.plot_energy_levels(max_n=5)
50
+ hidrogeno.plot_orbits(max_n=4)
51
+ ```
52
+
53
+ `Z` es el número atómico. Con `Z=1` se obtiene el hidrógeno y con `Z=2` el He+.
54
+
55
+ Para guardar una figura sin abrir una ventana:
56
+
57
+ ```python
58
+ hidrogeno.plot_energy_levels(max_n=5, show=False, save_path="niveles.png")
59
+ hidrogeno.plot_orbits(max_n=4, show=False, save_path="orbitas.png")
60
+ ```
61
+
62
+ ## Ejemplo
63
+
64
+ ```bash
65
+ python examples/ejemplo_uso.py
66
+ ```
67
+
68
+ Imprime los niveles del hidrógeno, la línea de Balmer n = 3 → n = 2 y una absorción n = 1 → n = 2. También guarda las figuras en `examples/figuras/`.
69
+
70
+ ## Pruebas
71
+
72
+ ```bash
73
+ pytest
74
+ ```
75
+
76
+ ## Autores
77
+
78
+ - Isabel Nieto
79
+ - Kevin Osorio
bohrpy-0.1.0/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # bohrpy
2
+
3
+ Librería en Python del modelo atómico de Bohr para el hidrógeno y átomos hidrogenoides. Calcula niveles de energía, radios de órbita y transiciones electrónicas, y dibuja esos resultados.
4
+
5
+ ## Instalación
6
+
7
+ Desde la raíz del repositorio:
8
+
9
+ ```bash
10
+ python -m venv venv
11
+ source venv/bin/activate
12
+ pip install -e ".[test]"
13
+ ```
14
+
15
+ El paquete pide estas versiones, que son las usadas en el proyecto:
16
+
17
+ - `numpy==1.26.4`
18
+ - `matplotlib==3.6.3`
19
+ - `pytest==9.2.0` para las pruebas
20
+
21
+ ## Uso básico
22
+
23
+ ```python
24
+ from bohrpy import BohrAtom
25
+
26
+ hidrogeno = BohrAtom(Z=1)
27
+
28
+ print(hidrogeno.energy_level(1)) # energía del nivel n, en eV
29
+ print(hidrogeno.orbit_radius(1)) # radio de la órbita, en metros
30
+
31
+ transicion = hidrogeno.transition(3, 2)
32
+ print(transicion["delta_E_eV"])
33
+ print(transicion["frequency_Hz"])
34
+ print(transicion["wavelength_m"])
35
+ print(transicion["type"]) # "emisión" o "absorción"
36
+
37
+ hidrogeno.plot_energy_levels(max_n=5)
38
+ hidrogeno.plot_orbits(max_n=4)
39
+ ```
40
+
41
+ `Z` es el número atómico. Con `Z=1` se obtiene el hidrógeno y con `Z=2` el He+.
42
+
43
+ Para guardar una figura sin abrir una ventana:
44
+
45
+ ```python
46
+ hidrogeno.plot_energy_levels(max_n=5, show=False, save_path="niveles.png")
47
+ hidrogeno.plot_orbits(max_n=4, show=False, save_path="orbitas.png")
48
+ ```
49
+
50
+ ## Ejemplo
51
+
52
+ ```bash
53
+ python examples/ejemplo_uso.py
54
+ ```
55
+
56
+ Imprime los niveles del hidrógeno, la línea de Balmer n = 3 → n = 2 y una absorción n = 1 → n = 2. También guarda las figuras en `examples/figuras/`.
57
+
58
+ ## Pruebas
59
+
60
+ ```bash
61
+ pytest
62
+ ```
63
+
64
+ ## Autores
65
+
66
+ - Isabel Nieto
67
+ - Kevin Osorio
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools==68.1.2"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "bohrpy"
7
+ version = "0.1.0"
8
+ description = "Librería para cálculos y visualización del modelo atómico de Bohr"
9
+ readme = "README.md"
10
+ authors = [
11
+ { name = "Isabel Nieto" },
12
+ { name = "Kevin Osorio" },
13
+ ]
14
+ license = { text = "MIT" }
15
+ requires-python = ">=3.9"
16
+ dependencies = [
17
+ "numpy==1.26.4",
18
+ "matplotlib==3.6.3",
19
+ ]
20
+
21
+ [project.optional-dependencies]
22
+ test = ["pytest==9.2.0"]
23
+
24
+ [project.urls]
25
+ Homepage = "https://github.com/isabelnieto900/bohrpy"
26
+
27
+ [tool.setuptools.packages.find]
28
+ where = ["src"]
29
+
30
+ [tool.pytest.ini_options]
31
+ testpaths = ["tests"]
32
+ pythonpath = ["src"]
bohrpy-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,29 @@
1
+ """
2
+ bohrpy: Una librería en Python para el modelo del átomo de Bohr.
3
+ Desarrollada para calcular niveles de energía, radios, transiciones electrónicas
4
+ y representaciones gráficas de átomos hidrogenoides.
5
+ """
6
+
7
+ # Importamos la clase principal para facilitar su acceso
8
+ from .bohr import BohrAtom
9
+
10
+ # Importamos las constantes físicas
11
+ from .constants import H_PLANCK, C_LIGHT, E_CHARGE, M_ELECTRON, A_0
12
+
13
+ # Importamos las gráficas para usarlas sin entrar al módulo plots
14
+ from .plots import plot_energy_levels, plot_orbits
15
+
16
+ # Definición de la versión de la librería (ideal para publicar en PyPI)
17
+ __version__ = "0.1.0"
18
+
19
+ # Limitamos qué se importa cuando alguien usa 'from bohrpy import *'
20
+ __all__ = [
21
+ "BohrAtom",
22
+ "H_PLANCK",
23
+ "C_LIGHT",
24
+ "E_CHARGE",
25
+ "M_ELECTRON",
26
+ "A_0",
27
+ "plot_energy_levels",
28
+ "plot_orbits",
29
+ ]
@@ -0,0 +1,84 @@
1
+ from .constants import H_PLANCK, C_LIGHT, E_CHARGE, A_0
2
+
3
+ class BohrAtom:
4
+ """
5
+ Clase que representa un átomo hidrogenoide según el modelo de Bohr.
6
+ Encapsula los parámetros físicos y métodos para cálculos y gráficos.
7
+ """
8
+
9
+ def __init__(self, Z=1):
10
+ """
11
+ Inicializa el átomo con un número atómico Z.
12
+ Por defecto, Z=1 (Hidrógeno).
13
+ """
14
+ if not isinstance(Z, int) or Z < 1:
15
+ raise ValueError("El número atómico Z debe ser un entero positivo.")
16
+ self.Z = Z
17
+ # Energía del estado fundamental del Hidrógeno en eV (aprox -13.6 eV)
18
+ self.E1_H = -13.605693
19
+
20
+ def energy_level(self, n):
21
+ """
22
+ Calcula la energía del electrón en el nivel n en electrón-voltios (eV).
23
+ """
24
+ if n < 1:
25
+ raise ValueError("El número cuántico principal n debe ser >= 1.")
26
+ return self.E1_H * (self.Z**2) / (n**2)
27
+
28
+ def orbit_radius(self, n):
29
+ """
30
+ Calcula el radio de la órbita para el nivel n en metros (m).
31
+ """
32
+ if n < 1:
33
+ raise ValueError("El número cuántico principal n debe ser >= 1.")
34
+ return A_0 * (n**2) / self.Z
35
+
36
+ def transition(self, n_initial, n_final):
37
+ """
38
+ Calcula la transición electrónica entre dos niveles.
39
+ Retorna la diferencia de energía (eV), la frecuencia (Hz) y la longitud de onda (m) del fotón emitido o absorbido.
40
+ """
41
+ if n_initial == n_final:
42
+ raise ValueError("Los niveles inicial y final deben ser diferentes.")
43
+
44
+ e_i = self.energy_level(n_initial)
45
+ e_f = self.energy_level(n_final)
46
+
47
+ # Diferencia de energía en eV y conversión a Julios
48
+ delta_E_eV = abs(e_f - e_i)
49
+ delta_E_J = delta_E_eV * E_CHARGE
50
+
51
+ # Frecuencia: f = E / h
52
+ frequency = delta_E_J / H_PLANCK
53
+
54
+ # Longitud de onda: lambda = c / f
55
+ wavelength = C_LIGHT / frequency
56
+
57
+ return {
58
+ "delta_E_eV": delta_E_eV,
59
+ "frequency_Hz": frequency,
60
+ "wavelength_m": wavelength,
61
+ "type": "emisión" if n_initial > n_final else "absorción"
62
+ }
63
+
64
+ def plot_energy_levels(self, max_n=5, show=True, save_path=None):
65
+ """
66
+ Representa los niveles de energía y las transiciones hasta max_n.
67
+
68
+ El dibujo vive en plots.py. Este método solo expone esa gráfica
69
+ desde el átomo.
70
+ """
71
+ from .plots import plot_energy_levels
72
+
73
+ return plot_energy_levels(self, max_n=max_n, show=show, save_path=save_path)
74
+
75
+ def plot_orbits(self, max_n=4, show=True, save_path=None):
76
+ """
77
+ Dibuja las órbitas electrónicas en el plano hasta el nivel max_n.
78
+
79
+ El dibujo vive en plots.py. Este método solo expone esa gráfica
80
+ desde el átomo.
81
+ """
82
+ from .plots import plot_orbits
83
+
84
+ return plot_orbits(self, max_n=max_n, show=show, save_path=save_path)
@@ -0,0 +1,14 @@
1
+ """
2
+ Constantes físicas fundamentales para el modelo del átomo de Bohr.
3
+ Valores basados en el Sistema Internacional (SI) y factores de conversión.
4
+ """
5
+
6
+ H_PLANCK = 6.62607015e-34 # Constante de Planck (J·s)
7
+ C_LIGHT = 299792458 # Velocidad de la luz en el vacío (m/s)
8
+ E_CHARGE = 1.602176634e-19 # Carga elemental o del electrón (C)
9
+ M_ELECTRON = 9.1093837015e-31 # Masa del electrón (kg)
10
+ EPSILON_0 = 8.8541878128e-12 # Permitividad del vacío (F/m)
11
+
12
+ # Constantes derivadas útiles
13
+ K_COULOMB = 1 / (4 * 3.141592653589793 * EPSILON_0) # Constante de Coulomb (N·m²/C²)
14
+ A_0 = 5.29177210903e-11 # Radio de Bohr (m)
@@ -0,0 +1,193 @@
1
+ """Gráficas del modelo de Bohr: niveles de energía y órbitas."""
2
+
3
+ import matplotlib.pyplot as plt
4
+ import numpy as np
5
+
6
+ # Nombre y color de cada serie, según el nivel inferior de la transición.
7
+ SERIES = {
8
+ 1: ("Lyman", "#1f77b4"),
9
+ 2: ("Balmer", "#d62728"),
10
+ 3: ("Paschen", "#2ca02c"),
11
+ 4: ("Brackett", "#9467bd"),
12
+ 5: ("Pfund", "#8c564b"),
13
+ }
14
+
15
+
16
+ def _validar_max_n(max_n):
17
+ """Comprueba que el nivel máximo sea un entero mayor o igual que 1."""
18
+ if not isinstance(max_n, int) or isinstance(max_n, bool) or max_n < 1:
19
+ raise ValueError("max_n debe ser un entero mayor o igual que 1.")
20
+ return max_n
21
+
22
+
23
+ def _estilo_serie(n_final):
24
+ """Devuelve el nombre y el color de la serie que termina en n_final."""
25
+ if n_final in SERIES:
26
+ return SERIES[n_final]
27
+ return (f"hacia n={n_final}", "#7f7f7f")
28
+
29
+
30
+ def _guardar_o_mostrar(fig, show, save_path):
31
+ """Guarda la figura si hay ruta y la muestra si show es verdadero."""
32
+ if save_path is not None:
33
+ fig.savefig(save_path, dpi=150, bbox_inches="tight")
34
+ if show:
35
+ plt.show()
36
+
37
+
38
+ def plot_energy_levels(atom, max_n=5, show=True, save_path=None):
39
+ """Dibuja los niveles de energía y las transiciones de emisión.
40
+
41
+ Cada línea horizontal es un nivel n. Las flechas bajan del nivel
42
+ inicial al final y se agrupan por serie (Lyman, Balmer, Paschen...).
43
+ Junto a cada flecha va la longitud de onda del fotón, en nanómetros.
44
+
45
+ Parameters
46
+ ----------
47
+ atom : BohrAtom
48
+ Átomo hidrogenoide cuyos niveles se van a dibujar.
49
+ max_n : int
50
+ Nivel más alto que entra en la figura.
51
+ show : bool
52
+ Si es verdadero, abre la ventana de la gráfica.
53
+ save_path : str o Path, opcional
54
+ Ruta donde se guarda la imagen. Si es None, no se guarda.
55
+
56
+ Returns
57
+ -------
58
+ tuple
59
+ La figura y los ejes de Matplotlib.
60
+ """
61
+ _validar_max_n(max_n)
62
+
63
+ fig, ax = plt.subplots(figsize=(8, 8))
64
+ energia_minima = atom.energy_level(1)
65
+
66
+ for n in range(1, max_n + 1):
67
+ energia = atom.energy_level(n)
68
+ ax.hlines(energia, 0, 1, colors="#1f4e79", linewidth=2)
69
+ ax.text(
70
+ 1.04,
71
+ energia,
72
+ f"n = {n} {energia:.2f} eV",
73
+ va="center",
74
+ fontsize=9,
75
+ )
76
+
77
+ # Cada transición ocupa su propia x. Las más largas van a la izquierda,
78
+ # donde hay más espacio vertical para la etiqueta.
79
+ transiciones = [
80
+ (n_inicial, n_final)
81
+ for n_final in range(1, max_n)
82
+ for n_inicial in range(n_final + 1, max_n + 1)
83
+ ]
84
+ transiciones.sort(
85
+ key=lambda niveles: atom.energy_level(niveles[0]) - atom.energy_level(niveles[1])
86
+ )
87
+ caja = {"boxstyle": "round,pad=0.12", "fc": "white", "ec": "none", "alpha": 0.9}
88
+
89
+ for indice, (n_inicial, n_final) in enumerate(transiciones):
90
+ _, color = _estilo_serie(n_final)
91
+ x = 0.06 + 0.88 * (indice + 0.5) / len(transiciones)
92
+ energia_inicial = atom.energy_level(n_inicial)
93
+ energia_final = atom.energy_level(n_final)
94
+ ax.annotate(
95
+ "",
96
+ xy=(x, energia_final),
97
+ xytext=(x, energia_inicial),
98
+ arrowprops={"arrowstyle": "-|>", "color": color, "lw": 1.4},
99
+ )
100
+ transicion = atom.transition(n_inicial, n_final)
101
+ longitud_nm = transicion["wavelength_m"] * 1e9
102
+ salto = abs(energia_inicial - energia_final)
103
+ if salto < 0.08 * abs(energia_minima):
104
+ y_texto = energia_inicial + 0.045 * abs(energia_minima)
105
+ va = "bottom"
106
+ else:
107
+ y_texto = (energia_inicial + energia_final) / 2
108
+ va = "center"
109
+ ax.text(
110
+ x,
111
+ y_texto,
112
+ f"{longitud_nm:.0f} nm",
113
+ color=color,
114
+ fontsize=7,
115
+ ha="center",
116
+ va=va,
117
+ bbox=caja,
118
+ )
119
+
120
+ for n_final in range(1, max_n):
121
+ nombre, color = _estilo_serie(n_final)
122
+ ax.plot([], [], color=color, label=nombre)
123
+
124
+ ax.set_xlim(-0.05, 1.62)
125
+ ax.set_ylim(energia_minima * 1.12, abs(energia_minima) * 0.08)
126
+ ax.set_xticks([])
127
+ ax.set_ylabel("Energía (eV)")
128
+ ax.set_title(f"Niveles de energía y transiciones (Z = {atom.Z})")
129
+ ax.grid(axis="y", linestyle="--", alpha=0.35)
130
+ ax.legend(title="Serie", loc="lower right", frameon=True)
131
+ fig.tight_layout()
132
+ _guardar_o_mostrar(fig, show, save_path)
133
+ return fig, ax
134
+
135
+
136
+ def plot_orbits(atom, max_n=4, show=True, save_path=None):
137
+ """Dibuja las órbitas circulares del modelo de Bohr en el plano.
138
+
139
+ El núcleo queda en el origen. El radio se muestra en ångströms
140
+ para que la escala sea legible, y un punto marca al electrón
141
+ sobre el eje x de cada órbita.
142
+
143
+ Parameters
144
+ ----------
145
+ atom : BohrAtom
146
+ Átomo hidrogenoide cuyas órbitas se van a dibujar.
147
+ max_n : int
148
+ Nivel más alto que entra en la figura.
149
+ show : bool
150
+ Si es verdadero, abre la ventana de la gráfica.
151
+ save_path : str o Path, opcional
152
+ Ruta donde se guarda la imagen. Si es None, no se guarda.
153
+
154
+ Returns
155
+ -------
156
+ tuple
157
+ La figura y los ejes de Matplotlib.
158
+ """
159
+ _validar_max_n(max_n)
160
+
161
+ fig, ax = plt.subplots(figsize=(7.2, 7))
162
+ angulo = np.linspace(0, 2 * np.pi, 400)
163
+ colores = plt.cm.viridis(np.linspace(0.15, 0.9, max_n))
164
+
165
+ ax.scatter(
166
+ [0],
167
+ [0],
168
+ s=130,
169
+ c="#c0392b",
170
+ zorder=5,
171
+ label=f"Núcleo (Z = {atom.Z})",
172
+ )
173
+
174
+ for n, color in zip(range(1, max_n + 1), colores):
175
+ radio_angstrom = atom.orbit_radius(n) * 1e10
176
+ ax.plot(
177
+ radio_angstrom * np.cos(angulo),
178
+ radio_angstrom * np.sin(angulo),
179
+ color=color,
180
+ linewidth=1.8,
181
+ label=f"n = {n} r = {radio_angstrom:.3f} Å",
182
+ )
183
+ ax.scatter([radio_angstrom], [0], s=30, color=color, zorder=4)
184
+
185
+ ax.set_aspect("equal")
186
+ ax.set_xlabel("x (Å)")
187
+ ax.set_ylabel("y (Å)")
188
+ ax.set_title(f"Órbitas del modelo de Bohr (Z = {atom.Z})")
189
+ ax.grid(True, linestyle="--", alpha=0.4)
190
+ ax.legend(loc="upper left", bbox_to_anchor=(1.02, 1), frameon=False)
191
+ fig.tight_layout()
192
+ _guardar_o_mostrar(fig, show, save_path)
193
+ return fig, ax
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.1
2
+ Name: bohrpy
3
+ Version: 0.1.0
4
+ Summary: Librería para cálculos y visualización del modelo atómico de Bohr
5
+ Author: Isabel Nieto, Kevin Osorio
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/isabelnieto900/bohrpy
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+ Provides-Extra: test
11
+ License-File: LICENSE
12
+
13
+ # bohrpy
14
+
15
+ Librería en Python del modelo atómico de Bohr para el hidrógeno y átomos hidrogenoides. Calcula niveles de energía, radios de órbita y transiciones electrónicas, y dibuja esos resultados.
16
+
17
+ ## Instalación
18
+
19
+ Desde la raíz del repositorio:
20
+
21
+ ```bash
22
+ python -m venv venv
23
+ source venv/bin/activate
24
+ pip install -e ".[test]"
25
+ ```
26
+
27
+ El paquete pide estas versiones, que son las usadas en el proyecto:
28
+
29
+ - `numpy==1.26.4`
30
+ - `matplotlib==3.6.3`
31
+ - `pytest==9.2.0` para las pruebas
32
+
33
+ ## Uso básico
34
+
35
+ ```python
36
+ from bohrpy import BohrAtom
37
+
38
+ hidrogeno = BohrAtom(Z=1)
39
+
40
+ print(hidrogeno.energy_level(1)) # energía del nivel n, en eV
41
+ print(hidrogeno.orbit_radius(1)) # radio de la órbita, en metros
42
+
43
+ transicion = hidrogeno.transition(3, 2)
44
+ print(transicion["delta_E_eV"])
45
+ print(transicion["frequency_Hz"])
46
+ print(transicion["wavelength_m"])
47
+ print(transicion["type"]) # "emisión" o "absorción"
48
+
49
+ hidrogeno.plot_energy_levels(max_n=5)
50
+ hidrogeno.plot_orbits(max_n=4)
51
+ ```
52
+
53
+ `Z` es el número atómico. Con `Z=1` se obtiene el hidrógeno y con `Z=2` el He+.
54
+
55
+ Para guardar una figura sin abrir una ventana:
56
+
57
+ ```python
58
+ hidrogeno.plot_energy_levels(max_n=5, show=False, save_path="niveles.png")
59
+ hidrogeno.plot_orbits(max_n=4, show=False, save_path="orbitas.png")
60
+ ```
61
+
62
+ ## Ejemplo
63
+
64
+ ```bash
65
+ python examples/ejemplo_uso.py
66
+ ```
67
+
68
+ Imprime los niveles del hidrógeno, la línea de Balmer n = 3 → n = 2 y una absorción n = 1 → n = 2. También guarda las figuras en `examples/figuras/`.
69
+
70
+ ## Pruebas
71
+
72
+ ```bash
73
+ pytest
74
+ ```
75
+
76
+ ## Autores
77
+
78
+ - Isabel Nieto
79
+ - Kevin Osorio
@@ -0,0 +1,13 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/bohrpy/__init__.py
5
+ src/bohrpy/bohr.py
6
+ src/bohrpy/constants.py
7
+ src/bohrpy/plots.py
8
+ src/bohrpy.egg-info/PKG-INFO
9
+ src/bohrpy.egg-info/SOURCES.txt
10
+ src/bohrpy.egg-info/dependency_links.txt
11
+ src/bohrpy.egg-info/requires.txt
12
+ src/bohrpy.egg-info/top_level.txt
13
+ tests/test_bohr.py
@@ -0,0 +1,5 @@
1
+ numpy==1.26.4
2
+ matplotlib==3.6.3
3
+
4
+ [test]
5
+ pytest==9.2.0
@@ -0,0 +1 @@
1
+ bohrpy
@@ -0,0 +1,104 @@
1
+ """Pruebas del modelo de Bohr: energías, radios, transiciones y gráficas."""
2
+
3
+ import matplotlib
4
+
5
+ matplotlib.use("Agg")
6
+
7
+ import matplotlib.pyplot as plt
8
+ import pytest
9
+
10
+ from bohrpy import A_0, BohrAtom
11
+ from bohrpy.constants import C_LIGHT, E_CHARGE, H_PLANCK
12
+
13
+
14
+ def test_energia_del_estado_fundamental_del_hidrogeno():
15
+ atomo = BohrAtom(Z=1)
16
+ assert atomo.energy_level(1) == pytest.approx(-13.605693)
17
+
18
+
19
+ def test_la_energia_crece_con_el_cuadrado_de_z():
20
+ hidrogeno = BohrAtom(Z=1)
21
+ helio = BohrAtom(Z=2)
22
+ assert helio.energy_level(1) == pytest.approx(4 * hidrogeno.energy_level(1))
23
+
24
+
25
+ def test_la_energia_cae_con_el_cuadrado_de_n():
26
+ atomo = BohrAtom()
27
+ assert atomo.energy_level(2) == pytest.approx(atomo.energy_level(1) / 4)
28
+ assert atomo.energy_level(3) == pytest.approx(atomo.energy_level(1) / 9)
29
+
30
+
31
+ def test_rechaza_nivel_y_numero_atomico_invalidos():
32
+ with pytest.raises(ValueError):
33
+ BohrAtom().energy_level(0)
34
+ with pytest.raises(ValueError):
35
+ BohrAtom(Z=0)
36
+ with pytest.raises(ValueError):
37
+ BohrAtom(Z=1.5)
38
+
39
+
40
+ def test_radio_de_bohr_y_su_dependencia_con_n_y_z():
41
+ hidrogeno = BohrAtom(Z=1)
42
+ assert hidrogeno.orbit_radius(1) == pytest.approx(A_0)
43
+ assert hidrogeno.orbit_radius(2) == pytest.approx(4 * A_0)
44
+
45
+ helio = BohrAtom(Z=2)
46
+ assert helio.orbit_radius(1) == pytest.approx(A_0 / 2)
47
+
48
+
49
+ def test_transicion_lyman_alfa_es_emision():
50
+ atomo = BohrAtom()
51
+ resultado = atomo.transition(2, 1)
52
+
53
+ delta_e = abs(atomo.energy_level(2) - atomo.energy_level(1))
54
+ frecuencia = delta_e * E_CHARGE / H_PLANCK
55
+
56
+ assert resultado["type"] == "emisión"
57
+ assert resultado["delta_E_eV"] == pytest.approx(delta_e)
58
+ assert resultado["frequency_Hz"] == pytest.approx(frecuencia)
59
+ assert resultado["wavelength_m"] == pytest.approx(C_LIGHT / frecuencia)
60
+ # La línea Lyman-alfa del hidrógeno está cerca de 121.6 nm.
61
+ assert resultado["wavelength_m"] * 1e9 == pytest.approx(121.6, abs=1.0)
62
+
63
+
64
+ def test_transicion_de_absorcion_y_niveles_iguales():
65
+ resultado = BohrAtom().transition(1, 3)
66
+ assert resultado["type"] == "absorción"
67
+ assert resultado["delta_E_eV"] > 0
68
+
69
+ with pytest.raises(ValueError):
70
+ BohrAtom().transition(2, 2)
71
+
72
+
73
+ def test_grafica_de_niveles_marca_energias_y_longitudes_de_onda():
74
+ fig, ax = BohrAtom().plot_energy_levels(max_n=4, show=False)
75
+ textos = [texto.get_text() for texto in ax.texts]
76
+
77
+ assert any(texto.startswith("n = 1") for texto in textos)
78
+ assert any("nm" in texto for texto in textos)
79
+ assert ax.get_ylabel() == "Energía (eV)"
80
+ plt.close(fig)
81
+
82
+
83
+ def test_grafica_de_orbitas_conserva_la_escala():
84
+ fig, ax = BohrAtom(Z=1).plot_orbits(max_n=3, show=False)
85
+
86
+ assert ax.get_aspect() in ("equal", 1.0)
87
+ assert len(ax.lines) == 3
88
+ plt.close(fig)
89
+
90
+
91
+ def test_las_graficas_rechazan_un_nivel_maximo_invalido():
92
+ with pytest.raises(ValueError):
93
+ BohrAtom().plot_energy_levels(max_n=0, show=False)
94
+ with pytest.raises(ValueError):
95
+ BohrAtom().plot_orbits(max_n=False, show=False)
96
+
97
+
98
+ def test_se_puede_guardar_la_figura(tmp_path):
99
+ destino = tmp_path / "niveles.png"
100
+ fig, _ = BohrAtom().plot_energy_levels(max_n=3, show=False, save_path=destino)
101
+
102
+ assert destino.is_file()
103
+ assert destino.stat().st_size > 0
104
+ plt.close(fig)