5.4. Kvantna fizika

5.4.1. Pravokutna potencijalna jama

Donji kod je napravljen za Sage sustav. Možete ga izvršiti ako instalirate Sage ili online na sagemathcloud-u. Dobra je vježba konvertirati to u Jupyter/Python.

Isprogramirat ćemo funkciju koja računa kvantnomehaničke valne funkcije i pripadajuće energije vezanih stanja čestice mase \(m\) u pravokutnoj potencijalnoj jami dubine \(V\) i širine \(L\). Koristimo oznake s Wikipedijine stranice.

sage: var('a k m L V E A B G H')
(a, k, m, L, V, E, A, B, G, H)
sage: import scipy.linalg as la
sage: import numpy as np
sage: import functools

Definiramo valnu funkciju za potencijalnu jamu širine \(L\).

sage: # components of piecewise wave function
sage: psi1(G, a, x) = G*exp(a*x)
sage: psi2(A, B, k, x) = A*sin(k*x) + B*cos(k*x)
sage: psi3(H, a, x) = H*exp(-a*x)
sage: # and a complete wave function
sage: def psi(x, coefs=(0,0,0,0), wns=(0, 0), L=1, norm=1, shift=0):
....:     """ coefs = (A, B, G, H)
....:         wns = (k, a)
....:     """
....:     if x < -L/2:
....:         psi = psi1(coefs[2], wns[1], x)
....:     elif x > L/2:
....:         psi = psi3(coefs[3], wns[1], x)
....:     else:
....:         psi = psi2(coefs[0], coefs[1], wns[0], x)
....:     return norm*psi + shift

Valni brojevi u unutrašnjosti \(k\) i izvan jame \(a\).

sage: # hbar=1
sage: wns = {k: sqrt(2*m*E), a: sqrt(2*m*(V-E))}

Rubni uvjeti na lijevom i desnom rubu jame su da tamo valna funkcija i njena prva derivacija budu neprekidne.

sage: boundary_conditions = [
....:     psi1(G, a, -L/2) == psi2(A, B, k, -L/2),
....:     psi3(H, a,  L/2) == psi2(A, B, k,  L/2),
....:     diff(psi1(G, a, x), x).subs(x=-L/2) == diff(psi2(A, B, k, x),
....:                                                    x).subs(x=-L/2),
....:     diff(psi3(H, a, x), x).subs(x= L/2) == diff(psi2(A, B, k, x),
....:                                                    x).subs(x= L/2)
....:                       ]

Ovi rubni uvjeti predstavljaju homogeni linearni sustav jednadžbi s nepoznanicama A, B, G i H. Energije vezanih stanja su određene zahtjevom da taj sustav ima rješenje. Konkretno, pretvorit ćemo rubne uvjete u matričnu jednadžbu oblika

\[\begin{split}M \left( \begin{array}{c} A \\ B \\ G \\ H \end{array} \right) = 0\end{split}\]

a energije su onda dane kao rješenja obične jednadžbe \(\det M = 0\). Zbog trenutnog buga u Sageovoj rutini za determinantu matrica 4x4 i većih (trebao bi biti ispravljen u nadolazećoj verziji Sagea) definiramo svoju rutinu za izračun determinante:

sage: def mydet(m):
....:     "Evaluates determinant of 4x4 matrix m. (Sage's det() has a bug.)"
....:     d = 0
....:     for c in range(4):
....:         ci = range(4)
....:         ci.pop(c)
....:         sub = m.matrix_from_rows_and_columns([1,2,3], ci)
....:         d += (-1)^(c) * m[0,c] * det(sub)
....:     return d

Elemente matrice \(M\) dobivamo metodom simboličkog izraza coef(), a jednadžbu \(\det M = 0\) rješavamo algoritmom u kojem krečemo od liste intervala [(0,V)], koja sadrži u početku samo jedan interval (0, V) i postupamo ovako:

  1. Maknemo prvi interval (a, b) iz liste (metoda pop()) i tražimo u njemu nultočku. To može imati dva ishoda
  1. Našli smo nultočku (taj ishod je zagarantiran u prvom koraku jer jedno vezano stanje uvijek postoji). Zapišemo nađenu energiju, a listu intervala nadopunimo s dva nova intervala: (a, E) i (E, b).
  2. Nismo našli nultočku. Ne radimo ništa (interval je već maknut s popisa).
  1. Ponavljamo postupak iz točke 1. dok ne iscrpimo sve intervale. (Radi jednoznačnosti, svi intervali gore su u samom kodu zapravo za malu vrijednost eps udaljeni od navedenih rubova.)
sage: def energies(system = {m: 1/2, L: 1, V: 25}, eps=1e-3):
....:     bcs = [c.subs(wns).subs(system) for c in boundary_conditions]
....:     # Preparing linear system  M*coefs = 0
....:     M = matrix([[(eq.lhs()-eq.rhs()).coefficient(v) for v in (A,B,G,H)]
....:                 for eq in bcs])
....:     dt = mydet(M)
....:     res = []
....:     # Energies of bound states are all solutions of det(M) = 0
....:     ints = [(eps, system[V]-eps)]
....:     while ints:
....:         int = ints.pop(0)
....:         try:
....:             e = find_root(dt, int[0], int[1])
....:             res.append(e)
....:             ints.extend([(int[0], e-eps), (e+eps, int[1])])
....:         except:
....:             pass
....:     res.sort()
....:     return res

Za provjeru odredit ćemo energije i odgovarajuće vrijednosti bezdimenzionalne varijable \(v=kL/2\) za slučaj sa spomenute stranice na Wikipediji:

sage: ens = energies(system = {m: 1/2, L: 1, V: 80}); ens
[6.557920590133058, 25.767519828716722, 55.56613192980969]
sage: # compare with wikipedia values = 1.28, 2.54, 3.73
sage: [(k*L/2).subs(wns).subs({m: 1/2, L: 1, V: 80}).subs(E=en).n()
....:      for en in ens]
[1.28042186311124, 2.53808588451596, 3.72713468799458]

Sada definiramo funkciju koja za datu energiju određuje koeficijente valnih funkcija A, B, G, H. Sustav jednadžbi rubnih uvjeta nije dovoljno određen (\(\det M = 0\)!). Dodatni uvjet koji bi potpuno odredio sustav je uvjet normalizacije valnih funkcija (integral gustoće vjerojatnosti \(|\psi(x)|^2\) po cijelom prostoru mora biti jedan), ali dodavanje tog uvjeta bi učinilo jednadžbe nelinearnima pa ćemo raditi na način da privremeno fiksiramo normalizaciju valne funkcije uvjetom \(\psi(L//2)=1\), što nam fiksira vrijednost koeficijenta H i onda za preostale koeficijente rješavamo nehomogenu linearnu matričnu jednadžbu

\[\begin{split}M_{\rm red} \left( \begin{array}{c} A \\ B \\ G \end{array} \right) = F_{\rm red}\end{split}\]
sage: def coefs(en, system = {m: 1/2, L: 1, V: 25}):
....:     """Returns (A, B, G, H) for given energy."""
....:     # Fixing H by normalization psi(L/2)=1
....:     hval = numerical_approx(exp(a*L/2).subs(wns).subs(system).subs(
....:                 {E: en}))
....:     bcred = [c.subs(H=hval).subs(wns).subs(system).subs({E: en})
....:                                      for c in boundary_conditions]
....:     # Preparing linear system  Mred * coefs[:-1] - Fred = 0
....:     Mred = matrix([[(eq.lhs()-eq.rhs()).coeff(v) for v in (A,B,G)]
....:                                      for eq in bcred])
....:     Fred = vector([(eq.lhs()-eq.rhs()).subs({A:0, B:0, G:0})
....:                                      for eq in bcred])
....:     cfs = la.lstsq(Mred, -Fred)[0].tolist()
....:     cfs.append(hval)
....:     return tuple(cfs)

Sada definiramo funkciju koja kombinira gornje funkcije, normalizira dobivene valne funkcije i vraća listu [( \(E_1\), \(\psi_1\)), ( \(E_2\), \(\psi_2\)), ...] gdje su \(\psi_i(x)\) funkcije jednog argumenta (\(x\)) dobivene parcijalnim izvrijednjavanjem (cf. currying ) na početku definirane funkcije psi za različite vrijednosti njenih opcionalnih argumenata, pomoću modula functools. (Normalizirane funkcije još množimo factorom scale radi čitlijvijeg crtanja.)

sage: def sqwell(system = {m: 1/2, L: 1, V: 25}, scale=5.):
....:     ens = energies(system)
....:     print "Energies = %s" % str(ens)
....:     res = []
....:     for en in ens:
....:         cfs = coefs(en, system)
....:         #print cfs
....:         norm = 1/sqrt(numerical_integral(lambda x: psi(x, cfs,
....:             (k.subs(wns).subs(system).subs(E=en), a.subs(wns).subs(
....:             system).subs(E=en)), L=1)**2, (-oo, oo))[0])
....:         #print en,
....:         fun = functools.partial(psi, coefs=cfs,
....:               wns=(k.subs(wns).subs({m: 1/2, L: 1, V: 25}).subs(E=en),
....:               a.subs(wns).subs(system).subs(E=en)),
....:                        L=system[L], norm=norm*scale)
....:         #print fun(0)
....:         res.append((en, fun))
....:     return res
sage: def square_well(system = {m: 1/2, L: 1, V: 25}, span=2, scale=5.):
....:     """span = horizontal span of figure in widhts of well
....:        scale = scaling factor for wavefunctions for visibility
....:     """
....:     colors = ['indigo', 'firebrick', 'darkolivegreen', 'plum']
....:     sol = sqwell(system)
....:     P = line([[-span*system[L]/2, system[V]], [-system[L]/2,
....:           system[V]], [-system[L]/2, 0],
....:             [system[L]/2, 0], [system[L]/2, system[V]],
....:                  [span*system[L]/2, system[V]]],
....:               color='darkslategray', thickness=10, alpha=0.3)
....:     for (en, fun), k in zip(sol,range(len(sol))):
....:         P += line([[-span*system[L]/2, en], [span*system[L]/2, en]],
....:                              color=colors[k], thickness=0.4)
....:         P += plot(lambda x: fun(x, shift=en), (-span*system[L]/2,
....:                      +span*system[L]/2), color=colors[k])
....:     P.show()
sage: square_well(system = {m: 1/2, L: 1, V: 80})
Energies = [6.557920590133058, 25.767519828716722, 55.56613192980968]
../_images/square_well.png