Energy level diagram
Example four shows the calculation of an energy level diagram of Ni$^{2+}$ as a function of the cubic crystal field parameter. Temperature leads to an occupation of excited states according to Bolzman statistics. It then becomes important to know the excited states. Here we calculate a graph showing these states as a function of the crystal-filed parameters. Although crystal field theory is often a highly oversimplified theory not accurate enough to describe a system well, degeneracies are given by symmetry and the here obtained energy level diagram will mostly change quantitative, but not qualitative when going to more involved methods of calculating material properties.
The text output written to screen does not show much interesting information.
- Energy_Level_Diagram.out
--> In this example we shall plot the energy-level diagram (so-called 'Tanabe-Sugano Diagram') --> Energy levels will be printed a function of 10Dq parameter, representing the crystal field strength --> Setting up the d-shell --> d8 (2 holes) configuration has 45 possible states --> Initial Hamiltonian: H_e-e + H_SO + H_Zeeman --> H0 = F0dd*OppF0 + F2dd*OppF2 + F4dd*OppF4 + zeta_3d*Oppldots + Bz*(2*OppSz + OppLz); ================================= --> List of parameters: --> F2dd = 11.142 --> F4dd = 6.874 --> F0dd = U+(F2dd+F4dd)*2/63 --> zeta_3d = 0.081 --> Bz = 0.000001 ================================= --> Diagonalising... --> Adding the Crystal field as a perturbation: H' = H0 + x * H_CF --> start increasing x ... 10Dq = 0.000 0.010 0.020 0.030 0.040 0.050 0.060 0.070 0.080 0.090 0.100 0.110 0.120 0.130 0.140 0.150 0.160 0.170 0.180 0.190 0.200 0.210 0.220 0.230 0.240 0.250 0.260 0.270 0.280 0.290 0.300 0.310 0.320 0.330 0.340 0.350 0.360 0.370 0.380 0.390 0.400 0.410 0.420 0.430 0.440 0.450 0.460 0.470 0.480 0.490 0.500 0.510 0.520 0.530 0.540 0.550 0.560 0.570 0.580 0.590 0.600 0.610 0.620 0.630 0.640 0.650 0.660 0.670 0.680 0.690 0.700 0.710 0.720 0.730 0.740 0.750 0.760 0.770 0.780 0.790 0.800 0.810 0.820 0.830 0.840 0.850 0.860 0.870 0.880 0.890 0.900 0.910 0.920 0.930 0.940 0.950 0.960 0.970 0.980 0.990 1.000 1.010 1.020 1.030 1.040 1.050 1.060 1.070 1.080 1.090 1.100 1.110 1.120 1.130 1.140 1.150 1.160 1.170 1.180 1.190 1.200 1.210 1.220 1.230 1.240 1.250 1.260 1.270 1.280 1.290 1.300 1.310 1.320 1.330 1.340 1.350 1.360 1.370 1.380 1.390 1.400 1.410 1.420 1.430 1.440 1.450 1.460 1.470 1.480 1.490 1.500 1.510 1.520 1.530 1.540 1.550 1.560 1.570 1.580 1.590 1.600 1.610 1.620 1.630 1.640 1.650 1.660 1.670 1.680 1.690 1.700 1.710 1.720 1.730 1.740 1.750 1.760 1.770 1.780 1.790 1.800 1.810 1.820 1.830 1.840 1.850 1.860 1.870 1.880 1.890 1.900 1.910 1.920 1.930 1.940 1.950 1.960 1.970 1.980 1.990 2.000 2.010 2.020 2.030 2.040 2.050 2.060 2.070 2.080 2.090 2.100 2.110 2.120 2.130 2.140 2.150 2.160 2.170 2.180 2.190 2.200 2.210 2.220 2.230 2.240 2.250 2.260 2.270 2.280 2.290 2.300 2.310 2.320 2.330 2.340 2.350 2.360 2.370 2.380 2.390 2.400 2.410 2.420 2.430 2.440 2.450 2.460 2.470 2.480 2.490 2.500 2.510 2.520 2.530 2.540 2.550 2.560 2.570 2.580 2.590 2.600 2.610 2.620 2.630 2.640 2.650 2.660 2.670 2.680 2.690 2.700 2.710 2.720 2.730 2.740 2.750 2.760 2.770 2.780 2.790 2.800 2.810 2.820 2.830 2.840 2.850 2.860 2.870 2.880 2.890 2.900 2.910 2.920 2.930 2.940 2.950 2.960 2.970 2.980 2.990 3.000
The energy level diagram of Ni$^{2+}$. i.e. the energy of the 45 eigen-states of 8 electrons in a $d$-shell as a function of the crystal-field splitting.
But the pdf created (see above) shows the energy level diagram. For $10Dq=0$ one has spherical symmetry and retrieves the atomic energy levels. We plotted the term symbols next to the level energies. Increasing the crystal field leads to a splitting of these levels and at some point to a mixing.
For completeness, the actual code is:
- Energy_Level_Diagram.Quanty
-- Minimize the output of the program, i.e. for longer calculations -- the program tells the user what it is doing. For these short -- examples the result is instant. Verbosity(0) print("--> In this example we shall plot the energy-level diagram (so-called 'Tanabe-Sugano Diagram')") print("--> Energy levels will be printed a function of 10Dq parameter, representing the crystal field strength") print("--> Setting up the d-shell") -- number of available fermionic spin-orbitals (d-shell in the present case) NFermion=10; -- number of bosonic states (phonons, etc...) NBoson=0; -- indices for the "spin-dn" states IndexDn_3d={0,2,4,6,8}; -- indices for the "spin-up" states IndexUp_3d={1,3,5,7,9}; -- function to print Term symbols function LSJ2term (S2,L2,J2) L = math.floor(0.5 * (math.sqrt(math.abs(L2) * 4 +1) - 1) + 0.5) if L == 0 then str1="S" elseif L == 1 then str1="P" elseif L == 2 then str1="D" elseif L == 3 then str1="F" elseif L == 4 then str1="G" elseif L == 5 then str1="H" elseif L == 6 then str1="I" elseif L == 7 then str1="K" elseif L == 8 then str1="L" elseif L == 9 then str1="M" elseif L == 10 then str1="N" elseif L == 11 then str1="O" elseif L == 12 then str1="Q" elseif L == 13 then str1="R" elseif L == 14 then str1="T" elseif L == 15 then str1="U" elseif L == 16 then str1="V" elseif L == 17 then str1="W" elseif L == 18 then str1="X" elseif L == 19 then str1="Y" elseif L == 20 then str1="Z" else str1="?" end mult = math.floor(math.sqrt(S2 * 4 +1)+0.5) twoJ = math.floor((math.sqrt(math.abs(J2) * 4 +1) - 1) + 0.5) if( 2*math.floor((twoJ+0.5)/2)==twoJ ) then return "^{"..mult.."}"..str1.."_{"..(twoJ/2).."}" else return "^{"..mult.."}"..str1.."_{"..twoJ.."/2}" end end -- Here we define the operators in the space of chosen orbitals OppSx =NewOperator("Sx" ,NFermion, IndexUp_3d, IndexDn_3d) OppSy =NewOperator("Sy" ,NFermion, IndexUp_3d, IndexDn_3d) OppSz =NewOperator("Sz" ,NFermion, IndexUp_3d, IndexDn_3d) OppSsqr =NewOperator("Ssqr" ,NFermion, IndexUp_3d, IndexDn_3d) OppSplus=NewOperator("Splus",NFermion, IndexUp_3d, IndexDn_3d) OppSmin =NewOperator("Smin" ,NFermion, IndexUp_3d, IndexDn_3d) OppLx =NewOperator("Lx" ,NFermion, IndexUp_3d, IndexDn_3d) OppLy =NewOperator("Ly" ,NFermion, IndexUp_3d, IndexDn_3d) OppLz =NewOperator("Lz" ,NFermion, IndexUp_3d, IndexDn_3d) OppLsqr =NewOperator("Lsqr" ,NFermion, IndexUp_3d, IndexDn_3d) OppLplus=NewOperator("Lplus",NFermion, IndexUp_3d, IndexDn_3d) OppLmin =NewOperator("Lmin" ,NFermion, IndexUp_3d, IndexDn_3d) OppJx =NewOperator("Jx" ,NFermion, IndexUp_3d, IndexDn_3d) OppJy =NewOperator("Jy" ,NFermion, IndexUp_3d, IndexDn_3d) OppJz =NewOperator("Jz" ,NFermion, IndexUp_3d, IndexDn_3d) OppJsqr =NewOperator("Jsqr" ,NFermion, IndexUp_3d, IndexDn_3d) OppJplus=NewOperator("Jplus",NFermion, IndexUp_3d, IndexDn_3d) OppJmin =NewOperator("Jmin" ,NFermion, IndexUp_3d, IndexDn_3d) -- Spin-orbit coupling Oppldots=NewOperator("ldots",NFermion, IndexUp_3d, IndexDn_3d) -- e-e Coulomb repulsion OppF0 =NewOperator("U", NFermion, IndexUp_3d, IndexDn_3d, {1,0,0}) OppF2 =NewOperator("U", NFermion, IndexUp_3d, IndexDn_3d, {0,1,0}) OppF4 =NewOperator("U", NFermion, IndexUp_3d, IndexDn_3d, {0,0,1}) -- Expansion of the effective electron-ion potential in the certain symmetry ("Oh" group in this case) -- This operator splits the states: it lowers one manifold on 0.4 [e.u.] and lifts the other on 0.6 [e.u.] -- in the limit of very strong CF we can call these manifolds as T2g and Eg Akm = PotentialExpandedOnClm("Oh", 2, {0.6,-0.4}) OpptenDq = NewOperator("CF", NFermion, IndexUp_3d, IndexDn_3d, Akm) -- We can define the operator which will count the number of particles in each manifold Akm = PotentialExpandedOnClm("Oh", 2, {1,0}) OppNeg = NewOperator("CF", NFermion, IndexUp_3d, IndexDn_3d, Akm) Akm = PotentialExpandedOnClm("Oh", 2, {0,1}) OppNt2g = NewOperator("CF", NFermion, IndexUp_3d, IndexDn_3d, Akm) print("--> d8 (2 holes) configuration has 45 possible states") print("") -- Here we set up the parameters for the operators, defined above. Npsi=45; U = 0.000 F2dd = 11.142 F4dd = 6.874 F0dd = U+(F2dd+F4dd)*2/63 zeta_3d = 0.081 Bz = 0.000001 print("") print("--> Initial Hamiltonian: H_e-e + H_SO + H_Zeeman") print("--> H0 = F0dd*OppF0 + F2dd*OppF2 + F4dd*OppF4 + zeta_3d*Oppldots + Bz*(2*OppSz + OppLz);") print("") print("=================================") print("--> List of parameters:"); print("--> F2dd = 11.142") print("--> F4dd = 6.874") print("--> F0dd = U+(F2dd+F4dd)*2/63") print("--> zeta_3d = 0.081") print("--> Bz = 0.000001") print("=================================") print("") -- Setting up the initial Hamiltonian in the 2nd quantised form -- Note: here we do not have the CF Hamiltonian0 = F0dd*OppF0 + F2dd*OppF2 + F4dd*OppF4 + zeta_3d*Oppldots + Bz*(2*OppSz + OppLz); -- We shall now diagonalise the Hamiltonian, looking for the states with the specific occupation (8 in this case). StartRestrictions = {NFermion, NBoson, {"1111111111",8,8}} print("--> Diagonalising...") -- Diagonalisation of the initial Hamiltonian (no CF) psiList = Eigensystem(Hamiltonian0, StartRestrictions, Npsi) TermSymbol={} ; TermSymbol[0]=""; E0={} ;E0[0]=0 for i =1,Npsi do S2=psiList[i] * OppSsqr * psiList[i] L2=psiList[i] * OppLsqr * psiList[i] J2=psiList[i] * OppJsqr * psiList[i] E0[i]=psiList[i] * Hamiltonian0 * psiList[i] TermSymbol[i]=LSJ2term(S2,L2,J2) end -- Open up the file, where the data will be stored file = assert(io.open("EnergyLevelDiagram", "w")) print("") print("--> Adding the Crystal field as a perturbation: H' = H0 + x * H_CF") print("--> start increasing x ...") io.write("10Dq = ") -- We add the CF to the Hamiltonian and start varying its strength. Each time we update the H, we re-diagonalise it. for i=0, 300 do tenDq = 0.01*i file:write(string.format("%14.7E ",tenDq)) io.write(string.format("%5.3f ",tenDq)) io.flush() Hamiltonian=Hamiltonian0 + tenDq * OpptenDq Eigensystem(Hamiltonian, psiList) for key,value in pairs(psiList) do energy = value * Hamiltonian * value file:write(string.format("%14.7E ",energy)) end file:write("\n") end file:close() io.write("\n") -- The output will be supplied to the gnuplot for consequent visualisation -- Starting the preparation of the gnuplot-input file gnuplotInput = [[ set terminal postscript portrait enhanced color "Times" 12 set autoscale # scale axes automatically set xtic auto # set xtics automatically set ytic auto # set ytics automatically set style line 1 lt 1 lw 1 lc rgb "#000000" set xlabel "10Dq (eV)" font "Times,12" set ylabel "Energy (eV)" font "Times,12" offset -3 set out 'EnergyLevelDiagram.ps' set size 1.0, 0.625 ]] -- write the gnuplot script to a file file = io.open("EnergyLevelDiagram.gnuplot", "w") file:write(gnuplotInput) for i=1,Npsi do if (abs(E0[i]-E0[i-1]) > 0.1 and TermSymbol[i] ~= TermSymbol[i-1]) then file:write("set label \""..TermSymbol[i].."\" at -0.3,",E0[i]," font \"Times,14\"\n") end end gnuplotInput=[[ plot for [i=2:46] "EnergyLevelDiagram" using 1:i notitle with lines ls 1 ]] file:write(gnuplotInput) file:close() -- call gnuplot to execute the script os.execute("gnuplot EnergyLevelDiagram.gnuplot ; ps2pdf EnergyLevelDiagram.ps ; ps2eps EnergyLevelDiagram.ps ; mv EnergyLevelDiagram.eps temp.eps ; eps2eps temp.eps EnergyLevelDiagram.eps ; rm temp.eps")