# Plan F10: la curva de Morse en vivo — romper un enlace con muelle, con Morse y con electrones > **Qué es este documento**: el plan de la primera química del proyecto. Hasta F9 la conectividad es fija: los enlaces son muelles que nunca ceden (P24). F10 tira de un enlace hasta que se rompe, y lo hace en tres escalones para que se vea exactamente qué añade cada uno: el **muelle** de F2 (no cede nunca), el **potencial de Morse** (cede, pero no sabe de electrones) y un **potencial con electrones**, aprendido (MACE) o calculado (GFN2-xTB), donde el enlace muere porque los electrones lo dejan. Todo con la curva de energía E(r) dibujándose mientras tiras. Al final se compara la fuerza de ruptura con la que mide un microscopio de fuerza atómica. > > Escrito el 02/09/2026. En la máquina: Python 3.14.0 (por defecto) y 3.12 (instalado), OpenMM 8.6 con `PythonForce`, sin PyTorch ni potenciales ML instalados aún; PyTorch 2.14 disponible en PyPI para 3.14 (solo CPU en Windows; con CUDA, vía Python 3.12). > > ## Estado (05/09/2026): F10 ENTREGADA, pero por otro camino > > Al ir a montar el escalón cuántico (F10b) resultó que **en esta máquina no hay ninguna vía cuántica instalable con pip**: ni `tblite`, ni `xtb`, ni `pyscf` publican ruedas para Windows, en ninguna versión de Python (comprobado el 05/09 con 3.14 y con 3.12). Lo único instalable es `torchani`, que arrastra PyTorch (~2,5 GB) y solo sabe de moléculas orgánicas neutras. > > Así que el escalón cuántico se hizo **calculándolo nosotros**, que además encaja mucho mejor con el proyecto: para la molécula más simple del universo —el **H₂⁺**, dos protones y un solo electrón— la ecuación de Schrödinger se resuelve en la aproximación LCAO con **tres integrales de forma cerrada**, y sale una curva de energía **sin un solo parámetro ajustado**. Son unas quince líneas de JavaScript. Todo está en `enlace.html`, sin servidor ni dependencias. > > **Comprobación**: el cálculo da r₀ = **1,319 Å** y Dₑ = **170 kJ/mol**; los valores LCAO de libro son 2,5 a₀ = 1,323 Å y 1,76 eV = 170 kJ/mol. El muelle y el Morse **no se sacan de tablas**: se ajustan a esa curva (mínimo, profundidad y curvatura), de modo que los tres coinciden en el fondo del pozo por construcción y se ve exactamente dónde se separan. > > **Fuerza de ruptura medida tirando con la pinza**, contra la teórica a·Dₑ/2: > > | Enlace | Medida | Teórica | > |---|---|---| > | H₂⁺ | 1,81 nN | 1,86 nN | > | H₂ | 6,86 | 7,37 | > | O–H | 8,71 | 9,35 | > | C–C | 6,06 | 6,15 | > > Sale siempre un poco por debajo, y es correcto: la agitación térmica ayuda a saltar la barrera antes de llegar al máximo. Es el efecto de Bell y Evans de la sección 1, ahora medido. > > **Y lo que no se hizo**: el escalón con potencial de aprendizaje automático (F10b) sigue pendiente; requiere PyTorch en un entorno con Python 3.12, o WSL/conda para xTB y PySCF. Para lo que viene después —simular un átomo con varios electrones— ese es el camino, y está anotado en la sección 8b. --- ## 0. Resumen en una pantalla | Escalón | Qué | Dónde corre | ¿Rompe? | ¿Es química? | |---|---|---|---|---| | **F10a** | `enlace.html`: una molécula (agua por defecto; H₂, metano, etano opcionales) con una **pinza** que tira de un átomo; tecla para conmutar muelle ↔ Morse; gráfica E(r) con el punto vivo | navegador, JS, copia del motor F2 | Morse sí, muelle no | **no**: al romperse, el H se va con su +0,417 e a cuestas (un «protón» absurdo). Esa es la lección | | **F10b** | `f10_enlace.py`: la misma molécula con **MACE-OFF24** (aprendizaje automático) o **GFN2-xTB** (cuántico ligero) enchufado a OpenMM 8.6 por `PythonForce`; pinza como fuerza externa; retransmitido con el protocolo de F9 (puerto 8323) al visor `directo.html` | Python, CPU (GPU opcional) | sí | **sí**: los fragmentos salen neutros, la geometría se relaja, y con xTB se ve **el orden de enlace bajar de 1 a 0** y las cargas reorganizarse en vivo | | **F10c** | `f10_referencia.py`: la curva E(r) de referencia calculada fuera de línea (xTB; DFT si hay), superpuesta en los dos visores; fuerza máxima; comparación con el AFM | fuera de línea | — | sí | **El número de cada escalón**: F10a, la fuerza máxima de Morse coincide con la analítica a·De/2 y el enlace se dilata al calentar (el muelle no). F10b, el enlace se rompe con una fuerza del orden de 6–9 nN y los fragmentos quedan neutros. F10c, la curva real se pega a la parábola cerca del fondo (por eso el muelle es exacto a 300 K) y se aplana a la energía de disociación. --- ## 1. La física de los tres escalones Sea x = r − r₀ el estiramiento del enlace. | Modelo | Energía | Fuerza | Se rompe | Lo que sabe | |---|---|---|---|---| | **Muelle** (F2, F5) | E = k_b·x² | F = −2k_b·x, crece sin límite | nunca | la curvatura del fondo del pozo | | **Morse** | E = D_e·(1 − e^(−a·x))² | F = 2aD_e(1 − e^(−ax))e^(−ax); máxima **F_max = a·D_e/2** en x = ln2/a | sí, al superar F_max | además, la profundidad D_e y la asimetría | | **Con electrones** (xTB, MACE) | E(r) sale de resolver (o de haber aprendido) la estructura electrónica | derivada numérica o analítica | sí | qué queda al romper: cargas, radicales, geometría de los fragmentos, y qué otros enlaces pueden formarse | Cerca del fondo, Morse y muelle coinciden: k_b = D_e·a² (en la forma E = k_b·x²). Por eso el muelle es exacto donde la agitación térmica lo visita (amplitud ~0,05 Å a 300 K, P13) y por eso F2 y F5 no necesitan más. **Parámetros de Morse de los enlaces candidatos** (espectroscópicos; D_e es la profundidad del pozo, algo mayor que la energía de disociación medida D₀ porque incluye la energía de punto cero): | Enlace | r₀ (Å) | D_e (kJ/mol) | a (Å⁻¹) | k_b = D_e·a² (kJ/mol/Ų) | F_max = a·D_e/2 | En nN | |---|---|---|---|---|---|---| | H–H | 0,741 | 458 | 1,94 | 1.720 | 444 kJ/mol/Å | 7,4 | | O–H (agua) | 0,958 | ~490 | ~2,3 | ~2.600 | ~560 | ~9,3 | | C–H (metano) | 1,09 | ~460 | ~1,9 | ~1.660 | ~440 | 7,3 | | C–C (etano) | 1,54 | ~390 | ~1,9 | ~1.400 | ~370 | 6,2 | Conversión: 1 kJ/mol/Å = 1,66 pN. El muelle O–H del sandbox tiene k_b = 2.300 kJ/mol/Ų: coherente con el Morse real (~2.600). **Lo que Morse no puede y los electrones sí.** Morse es un potencial de pares con la carga parcial fija: al romper el O–H del agua, el H se aleja con +0,417 e y el OH con −0,417 e, como si la ruptura fuera iónica. La ruptura real a la que se llega tirando es **homolítica**: cada fragmento se lleva un electrón, ambos quedan neutros (radicales H· y ·OH). Solo un modelo con electrones lo sabe. Y solo él sabe que dos fragmentos pueden formar **otro** enlace distinto al recombinarse: Morse solo conoce la pareja original. **La fuerza de ruptura y el AFM.** Grandbois y otros midieron en 1999 con un microscopio de fuerza atómica la ruptura de un enlace covalente Si–C: **2,0 ± 0,3 nN** a una velocidad de carga de 10 nN/s. Nuestra pinza tirará unos 10²⁰ veces más rápido (nanonewtons por picosegundo), y a esa velocidad la agitación térmica no tiene tiempo de ayudar: veremos fuerzas cercanas a F_max (6–9 nN). La relación entre ambas es la de Bell y Evans: la fuerza de ruptura crece con el logaritmo de la velocidad de carga, unos kT/x‡ por cada factor 10 (x‡ ≈ 0,3 Å es la distancia al máximo de Morse ⇒ ~0,15 nN por década). Veinte décadas ≈ 3 nN de diferencia: los dos números son compatibles. Es la misma idea que P15 (Arrhenius): esperar lo bastante, y el temblor rompe lo que la fuerza sola no rompería. --- ## 2. F10a — `enlace.html`: muelle contra Morse, en el navegador (sin instalar nada) - **Motor**: copia del de F2 (Å, fs, kJ/mol, velocity-Verlet, Langevin, mismas globales que `bases.html` por si se quiere enganchar `campo.js`). Una molécula de agua por defecto; `?mol=h2|agua|metano|etano` (metano y etano con geometría y muelles simples; lo que importa es el enlace que se tira). - **Tecla `M`**: alterna muelle ↔ Morse en el enlace elegido, con la misma k_b en el fondo para que a 300 K no se note la diferencia (esa es la primera lección: el HUD lo dice). - **La pinza**: `Y` tira, `U` afloja. Es una dinámica dirigida: un muelle blando (k ≈ 50 kJ/mol/Ų) engancha el átomo elegido a un punto que se mueve a velocidad constante (0,01–0,05 Å/ps, ajustable con `,`/`.`). El HUD muestra la fuerza en la pinza, r y E del enlace. - **La gráfica**: un panel con E(r) del modelo activo (parábola o Morse) y **el punto vivo** moviéndose sobre ella; en Morse, la línea horizontal de D_e y la marca de F_max. Al superarla, el enlace cede: el punto se va por la meseta y en 3D el átomo se separa. - **Lo que se ve además**: subir T con ↑ hace que el punto oscile más ancho; en Morse la oscilación es asimétrica y **el enlace medio se alarga** (dilatación térmica, emergente); en el muelle no. Al soltar y empujar de vuelta (`U` y luego la pinza en sentido contrario), Morse recombina siempre y sin coste: demasiado fácil, y con las cargas mal (tecla `K` colorea las cargas parciales para que se vea el «protón» suelto). - **Números**: F_max medida contra a·D_e/2; dilatación térmica ⟨r⟩(T) en Morse; en el muelle, ninguna. **1 sesión.** Puede hacerse ya, no depende de nada. --- ## 3. F10b — `f10_enlace.py`: la molécula con electrones, por la tubería de F9 - **La pieza clave es `PythonForce` de OpenMM 8.6**: una fuerza cuya energía y gradiente los calcula una función Python a la que se le pasa el estado (posiciones) y que devuelve energía en kJ/mol y fuerzas en kJ/mol/nm. Dentro de esa función se llama al potencial con electrones. La documentación avisa de que es lenta por la sobrecarga de Python; con 3–8 átomos es irrelevante. - **Dos motores, tecla o argumento `--motor`**: - **`mace`**: MACE-OFF24 (modelo medio o pequeño) vía `pip install mace-torch` sobre PyTorch. Aprendido de cientos de miles de cálculos cuánticos de moléculas orgánicas (conjunto SPICE). Elementos: H, C, N, O, F, P, S, Cl, Br, I. Da energía y fuerzas; **no da cargas**. Cerca del equilibrio es casi cuántico; en la disociación extrapola fuera de sus datos (se declara). Existe también la integración oficial `openmm-ml` (`createMixedSystem`, con `TorchForce`), pensada para meter una molécula ML dentro de un sistema clásico: es la puerta a «un ATP con electrones dentro de una bomba clásica», fuera de este plan. - **`xtb`**: GFN2-xTB vía `tblite` (Grimme), un método cuántico de enlace fuerte, aproximado y rapidísimo: resuelve electrones de verdad en milisegundos para 10 átomos. Da energía, fuerzas, **cargas de Mulliken y órdenes de enlace de Wiberg**: el visor puede pintar la carga de cada átomo cambiando y el grosor del enlace bajando de 1 a 0 mientras tiras. **Ver morir el enlace.** Matiz: la versión de capa cerrada da mal el límite a distancia larga en rupturas homolíticas; `tblite` tiene extensión de espín polarizado que lo corrige. - **La pinza**: `CustomExternalForce` armónica a un punto objetivo que el servidor mueve entre lotes (parámetro global). Integrador de Langevin a 0,5 fs sin SHAKE (los enlaces tienen que poder vibrar y romperse), termostato suave (1/ps) para absorber el calor del tirón. - **La tubería**: la misma de `plan_F9_directo.md`, sección 3 (`/info`, `/frame`, `/orden`), en el puerto 8323, con campos extra en el JSON de medidores: E, r del enlace, fuerza en la pinza, cargas por átomo y orden de enlace (xTB). `directo.html` acepta `?servidor=8323` y pinta esos extras (color por carga, grosor por orden de enlace, panel E(r) con la curva de referencia de F10c). - **Velocidad esperada** (a medir en la primera sesión): | Motor | Átomos | Coste por paso | Pasos/s | ps/s | |---|---|---|---|---| | xTB (CPU) | 3–8 | 5–20 ms | 50–200 | 0,03–0,1 | | MACE-OFF24 pequeño (CPU) | 3–8 | 30–100 ms | 10–30 | 0,005–0,015 | | MACE-OFF24 medio (GPU CUDA) | 3–8 | ~10 ms | ~100 | ~0,05 | Es lento, y basta: tirar 1 Å a 0,02 Å/ps son 50 ps, o sea de un minuto a un cuarto de hora de reloj según motor, con el enlace vibrando y el punto subiendo por la curva a la vista. **No necesita la GPU**: se puede hacer mientras corre una producción de F5. - **Entorno** (decidir en la primera sesión, probando): PyTorch 2.14 se instala en el Python 3.14 por defecto pero **solo CPU** en Windows; para CUDA hay que crear un entorno con el Python 3.12 instalado (`py -3.12 -m venv .venv-f10`) y ahí `torch` con CUDA + `mace-torch`. `tblite` se distribuye por conda-forge (micromamba, sin tocar el Python del sistema) o en WSL; si no hay forma limpia en Windows, el escalón xTB se hace en F10c fuera de línea y F10b se queda con MACE. Alternativa de reserva: ANI-2x (`torchani`, pip). - **Honestidad del escalón**: MACE **aprende** la química de sus datos; en la ruptura está extrapolando. xTB la **calcula**, pero aproximada. La verdad de referencia es F10c. Los tres se superponen en la misma gráfica. - **2–3 sesiones.** --- ## 4. F10c — `f10_referencia.py`: la curva de verdad - Barrido de r entre 0,6 y 4 Å para el enlace elegido, con la molécula relajada en el resto de coordenadas (barrido relajado), con xTB y, si hay acceso a DFT (PySCF en WSL, o Psi4), con DFT. Salida: `data/curva__.csv`. Los dos visores la leen y la pintan como línea de referencia sobre la parábola y sobre Morse. - **Números**: D_e, r₀ y a ajustados sobre la curva calculada y comparados con la tabla espectroscópica de la sección 1; la fuerza máxima; y el rango de r en el que la parábola se aparta menos de kT de la curva real (la «zona del muelle»). - **1 sesión.** --- ## 5. Cambios en el laboratorio (hechos hoy y pendientes) | Pieza | Estado | Detalle | |---|---|---| | `abrir_laboratorio.bat` | **hecho** | si existe `f10_enlace.py`, lo arranca en su ventana (puerto 8323); `abrir_laboratorio.bat sindirecto` omite F9 y F10 (`sinf9` sigue valiendo) | | `index.html` | **hecho** | tarjeta F10 «en preparación» con enlace a este plan; el botón «abrir» se añade cuando exista `enlace.html` | | `README.md`, hoja de ruta (sección 10 del doc 0b), `plan_F9_directo.md` §7b | **hecho** | enlaces cruzados | | `enlace.html` | pendiente (F10a) | copia del motor F2 + Morse + pinza + gráfica | | `f10_enlace.py` | pendiente (F10b) | `PythonForce` + `--motor mace\|xtb` + tubería de F9 en 8323 + `--grabar` | | `directo.html?servidor=` | pendiente (F9a/F10b) | elegir puerto; pintar cargas y orden de enlace | | `f10_referencia.py` + `data/curva_*.csv` | pendiente (F10c) | barrido relajado | | `.venv-f10` (Python 3.12 + torch CUDA + mace-torch) o micromamba con `tblite` | pendiente (F10b) | fuera del git (`.gitignore`) | --- ## 6. Riesgos y decisiones - **Instalación**: es la primera fase que depende de paquetes pesados (PyTorch ~2 GB; conda-forge para tblite). Se aísla en un entorno propio y no toca el Python con el que corre F5. Si nada de eso funciona limpiamente en Windows, F10a y F10c bastan para la lección y F10b espera. - **CUDA en Python 3.14**: no hay ruedas para Windows a fecha de hoy (solo CPU); de ahí el entorno 3.12. Para 3–8 átomos la CPU es suficiente de todos modos. - **Extrapolación de MACE** en la disociación: se declara y se contrasta con F10c. - **Límite de disociación de xTB** en capa cerrada: usar la variante de espín polarizado o aceptar que la meseta queda alta y decirlo. - **Calentamiento por el tirón**: la pinza mete energía; el termostato la saca; si se tira demasiado rápido el enlace se rompe «por temperatura» y no por fuerza. Velocidad de la pinza ≤ 0,05 Å/ps. - **No es reactividad general**: F10 rompe *un* enlace tirando. Formar enlaces nuevos entre moléculas, reacciones con barrera, catálisis: siguen fuera, salvo como demostraciones puntuales con el mismo montaje (por ejemplo, acercar H· al ·OH y ver recombinar con cargas correctas). - **GPU compartida**: F10 no la necesita; puede convivir con la producción de F5. --- ## 7. Orden y estimación | Paso | Entregable | Sesiones | Depende de | |---|---|---|---| | 1 | **F10a** `enlace.html` (muelle vs Morse, pinza, gráfica, dilatación térmica) | 1 | nada | | 2 | **F10c** curva de referencia con xTB (o DFT) y su lectura en `enlace.html` | 1 | instalar `tblite` en un entorno aparte | | 3 | **F10b** `f10_enlace.py` con `--motor mace` y/o `xtb` por la tubería de F9; `directo.html?servidor=8323` con cargas y orden de enlace | 2–3 | tubería F9a (o copiarla); entorno `.venv-f10` | | 4 | cuaderno: entrada con F_max medida, dilatación térmica, comparación con el AFM | ½ | 1–3 | Total: **4–5 sesiones**. F10a puede empezar hoy mismo; F10b conviene después de F9a para heredar la tubería ya probada. --- ## 8b. Y después: simular un átomo de verdad (hacia una F11) La pregunta natural tras F10 es «¿y un átomo entero, con su cuántica?». El mapa honesto, con lo que se puede y lo que no en esta máquina: | Qué | Se puede resolver | Cómo, aquí | |---|---|---| | **Un electrón** (H, He⁺, Li²⁺…, H₂⁺) | **Exacto**, con lápiz o con cien líneas | Ya lo hacemos en F10. Los orbitales del hidrógeno tienen fórmula cerrada: se pueden dibujar 1s, 2s, 2p, 3d… con sus nodos, y se *ve* por qué el átomo no se colapsa | | **Cualquier potencial central, un electrón** | **Exacto numéricamente** | Integrando la ecuación radial por el método de Numerov y buscando los autovalores por tiro. Son ~150 líneas de JavaScript y da los niveles de energía de verdad | | **Varios electrones** (He, Li, C…) | Aproximado: Hartree-Fock o DFT | Un campo autoconsistente sencillo (Hartree + intercambio aproximado) sobre la misma malla radial es asequible y daría las energías de ionización de los primeros elementos con un error de pocas unidades porcentuales | | **Precisión de investigación** | HF/DFT/post-HF de verdad | PySCF, Psi4, ORCA — y ninguno se instala con pip en Windows: hacen falta WSL, conda-forge o el instalador nativo | O sea: **lo más instructivo se puede hacer entero en el navegador y sin instalar nada**, igual que F2 y F10. La escalera natural sería: los orbitales del hidrógeno dibujados (fórmula exacta) → el solver radial numérico, que reproduce esos niveles y luego funciona para cualquier potencial → el campo autoconsistente para el helio y el litio, donde aparece el apantallamiento entre electrones y con él **la tabla periódica**. Ese último paso es el que explica por qué el sodio cede un electrón y el cloro lo coge, que es donde empezó todo esto en F7b. ## 8c. Lo que se probó a instalar y no está disponible (05/09/2026) | Paquete | Qué daría | Resultado con pip | |---|---|---| | `tblite` | GFN2-xTB: cuántico semiempírico, con cargas y órdenes de enlace | sin rueda para Windows (ni 3.14 ni 3.12) | | `xtb` | lo mismo, otra interfaz | sin rueda para Windows | | `pyscf` | HF, DFT, post-HF de verdad | sin rueda para Windows | | `torchani` | ANI-2x, potencial de aprendizaje automático | **sí** instalable en Python 3.12, arrastra PyTorch (~2,5 GB) | ## 8. Referencias - OpenMM `PythonForce` — [API](https://docs.openmm.org/latest/api-python/generated/openmm.openmm.PythonForce.html) (OpenMM 8.5+; energía en kJ/mol, fuerzas en kJ/mol/nm; subconjuntos de partículas y parámetros globales). - OpenMM-ML — [GitHub](https://github.com/openmm/openmm-ml) · [MACEPotentialImpl](https://openmm.github.io/openmm-ml/dev/generated/openmmml.models.macepotential.MACEPotentialImpl.html) · [OpenMM 8 y potenciales ML (arXiv 2023)](https://arxiv.org/pdf/2310.03121). - MACE — [instalación (`mace-torch`)](https://mace-docs.readthedocs.io/en/latest/guide/installation.html) · [modelos fundacionales (MACE-OFF23/24)](https://mace-docs.readthedocs.io/en/latest/guide/foundation_models.html) · [MACE-OFF, JACS 2024](https://pubs.acs.org/doi/10.1021/jacs.4c07099) · [interfaz con OpenMM](https://mace-docs.readthedocs.io/en/latest/guide/openmm.html). - GFN2-xTB / tblite — [uso desde Python y ASE](https://tblite.readthedocs.io/en/stable/users/ase.html) · [API Python](https://tblite.readthedocs.io/en/latest/api/python.html) · [xtb-python](https://xtb-python.readthedocs.io/en/latest/ase-calculator.html). - PyTorch y Python 3.14 en Windows — [ruedas CUDA no disponibles para cp314 (issue)](https://github.com/pytorch/pytorch/issues/169929) · [torch en PyPI](https://pypi.org/project/torch/). - Fuerza de ruptura de un enlace covalente — Grandbois, Beyer, Rief, Clausen-Schaumann, Gaub, *How Strong Is a Covalent Bond?*, Science 1999: [artículo](https://www.science.org/doi/10.1126/science.283.5408.1727). - Cuaderno: P13 (rigidez y muelles), P15 (Arrhenius), P24 (enlaces fijos vs forma emergente).