schrodinger-simulation

schrodinger's playground
git clone git://popovic.xyz/schrodinger-simulation.git
Log | Files | Refs | README | LICENSE

main.py (9031B)


      1 #!/usr/bin/python3.9
      2 
      3 from dolfin import *
      4 import numpy as np
      5 import matplotlib.pyplot as plt
      6 from scipy.special import factorial, hermite, eval_laguerre, genlaguerre
      7 import matplotlib.patches as mpatches
      8 
      9 def harmonic_oscillator(xmin, xmax, mesh, V):
     10 
     11     xmin=xmin
     12     xmax=xmax
     13     mesh=mesh
     14     V=V
     15 
     16     def boundary(x, on_bnd):
     17         return on_bnd
     18 
     19     bc = DirichletBC(V, Constant(0.), boundary)
     20 
     21     u = TrialFunction(V)
     22     v = TestFunction(V)
     23 
     24     pot_ = Expression('0.5*pow(x[0], 2)', degree=2)
     25     pot = interpolate(pot_, V)
     26 
     27     a = 1/2*inner(grad(u), grad(v))*dx + pot*u*v*dx
     28     m = u*v*dx
     29 
     30     A = PETScMatrix()
     31     M = PETScMatrix()
     32 
     33     assemble(a, tensor=A)
     34     assemble(m, tensor=M)
     35 
     36     bc.apply(A)
     37 
     38     # create eigensolver
     39     eigensolver = SLEPcEigenSolver(A, M)
     40     eigensolver.parameters['spectrum'] = 'smallest magnitude'
     41 
     42     # solve for eigenvalues
     43     values = 10
     44     eigensolver.solve(values)
     45 
     46     u = Function(V)
     47 
     48     # plot
     49     fig = plt.figure(figsize=[10, 10])
     50     En = []
     51     for i in range(0, values+1):
     52         E, E_c, R, R_c = eigensolver.get_eigenpair(i)
     53         En.append(E)
     54         x = np.linspace(xmin, xmax, len(R))
     55 
     56         I = np.trapz(R*R, x)
     57   #      print(I)
     58 
     59         #plot eigenfunction
     60         plt.plot(x, 20*R*R+E, c='blue')
     61         plt.plot(x, R_c+E, lw=0.5, c='black')
     62         plt.annotate(f'$E_{ { i } } = {round(En[i], 3)}$', (xmax-0.1, 0.05+E), fontsize=14)
     63         plt.annotate(f'$|\Psi _{ { i } }|²$', (xmin, 0.1+E), fontsize=14)
     64 
     65     plt.plot(x, 1/2*x**2, c='red')
     66     plt.ylim(0, max(En)+0.5)
     67     plt.title('Harmonic Oscillator $V(x) =\\frac{1}{2}x^2 $', fontsize=20)
     68     plt.xticks([])
     69     plt.yticks([])
     70 
     71 
     72 
     73     # plot analytical solutions
     74 
     75     def harm_osc(n, x):
     76         return 1/sqrt(sqrt(np.pi)*(2**n)*factorial(n)) * hermite(n)(x) * np.exp(-1/2*x**2)
     77 
     78     x = np.linspace(xmin, xmax, 70)
     79 
     80   #  print(En)
     81 
     82     E = []
     83     for n in range(0, values+1):
     84 
     85         E.append(n+1/2)
     86         psi = harm_osc(n, x)
     87         I = np.trapz(psi*psi, x)
     88        # print(I)
     89         plt.scatter(x, psi*psi+En[n], s=20, c='green', marker="x")
     90 
     91     numerical = mpatches.Patch(color='blue', label='numerical solution', linestyle='-')
     92     analytical = mpatches.Patch(color='green', label='analytical solution', linestyle='-')
     93     plt.legend(handles = [numerical, analytical], loc='upper left')
     94  #   plt.show()
     95    # plt.savefig('./plots/harmonic_oscillator.png')
     96 
     97     return E, En
     98 
     99 def box(xmin, xmax, mesh, V):
    100     xmin=xmin
    101     xmax=xmax
    102     mesh=mesh
    103     V=V
    104 
    105     def boundary(x, on_bnd):
    106         return on_bnd
    107 
    108     bc = DirichletBC(V, Constant(0.), boundary)
    109 
    110     u = TrialFunction(V)
    111     v = TestFunction(V)
    112 
    113     pot_ = Expression('x[0] == xmin || x[0] == xmax ? 1000 : 0', xmin=xmin, xmax=xmax, degree=2)
    114 
    115     pot = interpolate(pot_, V)
    116 
    117     a = 1/2*inner(grad(u), grad(v))*dx + pot*u*v*dx
    118     m = u*v*dx
    119 
    120     A = PETScMatrix()
    121     M = PETScMatrix()
    122 
    123     assemble(a, tensor=A)
    124     assemble(m, tensor=M)
    125 
    126     bc.apply(A)
    127 
    128     # create eigensolver
    129     eigensolver = SLEPcEigenSolver(A, M)
    130     eigensolver.parameters['spectrum'] = 'smallest magnitude'
    131 
    132     # solve for eigenvalues
    133     values = 6
    134     eigensolver.solve(values)
    135 
    136     u = Function(V)
    137 
    138     # plot
    139     fig = plt.figure(figsize=[10, 10])
    140     En = []
    141     for i in range(0, values+1):
    142         E, E_c, R, R_c = eigensolver.get_eigenpair(i)
    143         En.append(E)
    144         x = np.linspace(xmin, xmax, len(R))
    145 
    146         I = np.trapz(R*R, x)
    147    #     print(I)
    148 
    149         #plot eigenfunction
    150         plt.plot(x, 10*R*R+E, c='blue')
    151         plt.plot(x, R_c+E, lw=0.5, c='black')
    152         plt.annotate(f'$E_{ { i } } = {round(En[i], 3)}$', (xmax-0.1, 0.05+E), fontsize=14)
    153         plt.annotate(f'$|\Psi _{ { i } }|²$', (xmin, 0.05+E), fontsize=14)
    154 
    155     plt.axvline(x=xmin, c='red')
    156     plt.axvline(x=xmax, c='red')
    157     plt.ylim(0, max(En)+0.5)
    158     plt.title('Particle in \'infinite\' well', fontsize=20)
    159     plt.xticks([])
    160     plt.yticks([])
    161 
    162 
    163 
    164     # plot analytical solutions
    165     x = np.linspace(xmin, xmax, 70)
    166 
    167  #   print(En)
    168 
    169     for i in range(values+1, values+2):
    170         E, E_c, R, R_c = eigensolver.get_eigenpair(i)
    171     #    En.append(E)
    172 
    173     E = []
    174     for n in range(1, values+2):
    175 
    176        # particle in a box
    177         E.append(n**2 * np.pi**2 / (2*100))
    178         kn = n*np.pi/10
    179         if n%2 == 0:
    180             psi = sqrt(2/10) * np.sin(kn*x)
    181         if n%2 == 1:
    182             psi = sqrt(2/10) * np.cos(kn*x)
    183 
    184 
    185        # plot
    186 
    187         I = np.trapz(psi*psi, x)
    188        # print(I)
    189         plt.scatter(x, 0.5*psi*psi+En[n-1], s=20, c='green', marker="x")
    190 
    191 
    192 
    193   #  plt.tight_layout()
    194 
    195     numerical = mpatches.Patch(color='blue', label='numerical solution', linestyle='-')
    196     analytical = mpatches.Patch(color='green', label='analytical solution', linestyle='-')
    197     plt.legend(handles = [numerical, analytical], loc='upper left')
    198   #  plt.show()
    199    # plt.savefig('./plots/box_potential.png')
    200 
    201     return E, En
    202 
    203 def morse(xmin, xmax, mesh, V):
    204     xmin=xmin
    205     xmax=xmax
    206     mesh=mesh
    207     V=V
    208     D = 12
    209     a = 1
    210 
    211     def boundary(x, on_bnd):
    212         return on_bnd
    213 
    214     bc = DirichletBC(V, Constant(0.), boundary)
    215 
    216     u = TrialFunction(V)
    217     v = TestFunction(V)
    218 
    219     pot_ = Expression('D*pow((1-exp(a*(x[0]-x0))), 2)', D=D, a=a, x0=0, degree=2)
    220   #  pot_ = Expression('D*(exp(-2*a*(x[0]-x0))-2*exp(-a*(x[0]-x0)))', D=D, a=a, x0=0, degree=2)
    221 
    222     pot = interpolate(pot_, V)
    223 
    224     a = 1/2*inner(grad(u), grad(v))*dx + pot*u*v*dx
    225     m = u*v*dx
    226 
    227     A = PETScMatrix()
    228     M = PETScMatrix()
    229 
    230     assemble(a, tensor=A)
    231     assemble(m, tensor=M)
    232 
    233     bc.apply(A)
    234 
    235     # create eigensolver
    236     eigensolver = SLEPcEigenSolver(A, M)
    237     eigensolver.parameters['spectrum'] = 'smallest magnitude'
    238 
    239     # solve for eigenvalues
    240     values = 3
    241     eigensolver.solve(values)
    242 
    243     u = Function(V)
    244 
    245     # plot
    246     fig = plt.figure(figsize=[10, 10])
    247     En = []
    248     for i in range(0, values+1):
    249         E, E_c, R, R_c = eigensolver.get_eigenpair(i)
    250         En.append(E)
    251         x = np.linspace(xmin, xmax, len(R))
    252 
    253         I = np.trapz(R*R, x)
    254        # print(I)
    255 
    256         #plot eigenfunction
    257         plt.plot(x, 20*R*R+E, c='blue')
    258         plt.plot(x, R_c+E, lw=0.5, c='black')
    259         plt.annotate(f'$E_{ { i } } = {round(En[i], 3)}$', (xmax-0.1, 0.1+E), fontsize=14)
    260         plt.annotate(f'$|\Psi _{ { i } }|²$', (xmin, 0.2+E), fontsize=14)
    261 
    262     plt.plot(x, 12*(1-np.exp(-x))**2, c='red')
    263  #   plt.plot(x, 12*(np.exp(-2*x)-2*np.exp(-x)))
    264     plt.ylim(0, max(En)+2)
    265   #  plt.title('Particle in \'infinite\' well', fontsize=20)
    266     plt.title('Morse Potential $V(x) = D \cdot (1 - e^{- \\alpha (x - x0)})^2$', fontsize=20)
    267     plt.xticks([])
    268     plt.yticks([])
    269 
    270 
    271 
    272 
    273 
    274     # plot analytical solutions
    275     x = np.linspace(xmin, xmax, 70)
    276 
    277   #  print(En)
    278 
    279     E = []
    280 
    281     for n in range(0, values+1):
    282        # morse
    283 
    284         w = np.sqrt(2*12)/(2*np.pi)
    285         l = np.sqrt(2*D)
    286         z = 2*l*np.exp(-x)
    287         N = np.sqrt(factorial(n)*(2*l-2*n-1)/factorial(2*l-n-1))
    288         psi = N * z**(l-n-1/2) * np.exp(-z/2) * genlaguerre(n, 2*l-2*n-1)(z)
    289 
    290         E.append(-1/2*(l-n-1/2)**2 + 12)
    291       #  print(E)
    292       #  print(En)
    293 
    294        # plot
    295 
    296         I = np.trapz(psi*psi, x)
    297        # print(I)
    298         plt.scatter(x, psi*psi+En[n], s=20, c='green', marker="x")
    299 
    300 
    301 
    302   #  plt.tight_layout()
    303 
    304     numerical = mpatches.Patch(color='blue', label='numerical solution', linestyle='-')
    305     analytical = mpatches.Patch(color='green', label='analytical solution', linestyle='-')
    306     plt.legend(handles = [numerical, analytical], loc='upper left')
    307  #   plt.show()
    308 #    plt.savefig('./plots/morse_potential.png')
    309 
    310     return E, En
    311 
    312 def main():
    313 
    314     xmin = -5; xmax = 5
    315     s_h = []
    316     s_b = []
    317     s_m = []
    318     meshfinesse = []
    319 
    320     for n in range(20, 300, 20):
    321         meshfinesse.append(n)
    322         mesh = IntervalMesh(n, xmin, xmax)
    323         V = FunctionSpace(mesh, 'CG', 1)
    324         E_h_an, E_h_num = harmonic_oscillator(xmin, xmax, mesh, V)
    325         E_b_an, E_b_num = box(xmin, xmax, mesh, V)
    326         E_m_an, E_m_num = morse(xmin, xmax, mesh, V)
    327 
    328         d_h = np.subtract(E_h_an, E_h_num)/E_h_an
    329         s_h.append(np.average(np.abs(d_h)))
    330 
    331         d_b = np.subtract(E_b_an, E_b_num)/E_b_an
    332         s_b.append(np.average(np.abs(d_b)))
    333 
    334   #  print(E_m_an, E_m_num)
    335         d_m = np.subtract(E_m_an, E_m_num)/E_m_an
    336         s_m.append(np.average(np.abs(d_m)))
    337 
    338     s_h = np.array(s_h)
    339     s_b = np.array(s_b)
    340     s_m = np.array(s_m)
    341     print(s_h)
    342     print(s_b)
    343     print(s_m)
    344 
    345     fig = plt.figure()
    346     plt.plot(meshfinesse, 100*s_h, marker='x', c='green', label='harmonic oscillator')
    347     plt.plot(meshfinesse, 100*s_b, marker='x', c='red', label='box potential')
    348     plt.plot(meshfinesse, 100*s_m, marker='x', c='blue', label='morse potential')
    349     plt.title('Mittlerer prozentualer Fehler der Eigenwerte', fontsize=20)
    350     plt.ylabel('MAPE (%)')
    351     plt.xlabel('number of intervalls (meshsize = 10)')
    352 #    plt.xticks(np.arange(0, 1, 0.1))
    353 #    plt.gca().invert_xaxis()
    354 #    plt.xscale('log')
    355 
    356     plt.legend()
    357     plt.show()
    358 
    359 
    360 
    361 if __name__ == "__main__":
    362     main()