Skip to content

Commit 77e430a

Browse files
committed
Add support for simulate metal plate
1 parent cf466e5 commit 77e430a

File tree

4 files changed

+46
-79
lines changed

4 files changed

+46
-79
lines changed

pyqhe/core/structure.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ def __init__(self,
336336
# dimension as 'universal grid'.
337337
self._universal_grid = None
338338
self.dim = None
339+
self.bound_dirichlet = None
339340
# Structure's properties
340341
self.temp = temp
341342
self.fi = None
@@ -410,6 +411,10 @@ def _prepare_structure_stroage(self, delta=1, spline_storage=False):
410411
self.cb_meff = np.broadcast_to(cb_meff, self.dim)
411412
self.doping = np.broadcast_to(doping, self.dim)
412413

414+
def add_dirichlet_boundary(self, bound: np.ndarray):
415+
if bound.shape == tuple(self.dim):
416+
self.bound_dirichlet = bound
417+
413418

414419
class Structure3D:
415420
"""Class for modeling 3d material structure.

pyqhe/equation/poisson.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,12 @@ def __init__(self,
8585
self.eps = eps
8686
else:
8787
raise ValueError('The dimension of eps is not match.')
88-
self.bound_dirichlet = bound_dirichlet
88+
if bound_dirichlet is None:
89+
self.bound_dirichlet = None
90+
elif bound_dirichlet.shape == tuple(self.dim) or len(self.dim) == 1:
91+
self.bound_dirichlet = bound_dirichlet
92+
else:
93+
raise ValueError('The dimension of eps is not match.')
8994

9095
def build_d_matrix(self, loc):
9196
"""Build 1D time independent Schrodinger equation kinetic operator.
@@ -123,10 +128,11 @@ def calc_poisson(self, **kwargs):
123128

124129
if self.bound_dirichlet is not None:
125130
delta = self.grid[0][1] - self.grid[0][0]
131+
# adjust coefficient let matrix looks good
126132
bound_b = self.bound_dirichlet * self.eps / delta**2
127133
bound_a = np.zeros_like(b_vec)
128-
bound_loc = np.flatnonzero(self.bound_dirichlet)
129-
bound_a[bound_loc] = self.eps.flatten()[bound_loc] / delta**2
134+
bound_loc = np.flatnonzero(~np.isnan(self.bound_dirichlet))
135+
bound_a[bound_loc] = 1 * self.eps.flatten()[bound_loc] / delta**2
130136
# tensor contraction
131137
bound_mat = np.diag(bound_a)
132138
a_mat[bound_loc] = bound_mat[bound_loc]
@@ -162,17 +168,18 @@ def calc_poisson(self, **kwargs):
162168
top_plate = (yv <= 0.1 + delta / 2) * (yv >= 0.1 - delta / 2)
163169
bottom_plate = (yv <= -0.1 + delta /2) * (yv >= -0.1 -delta / 2)
164170
length = (xv <= 0.5) * (xv >= -0.5)
165-
bound = np.zeros_like(xv)
171+
bound = np.empty_like(xv)
172+
bound[:] = np.nan
166173
bound[top_plate * length] = 1
167174
bound[bottom_plate * length] = -1
168-
sol = PoissonFDM([x, y], np.zeros_like(bound),
169-
np.ones_like(bound) * const.eps0, bound)
175+
sol = PoissonFDM([x, y], np.zeros_like(xv),
176+
np.ones_like(xv) * const.eps0, bound)
170177
# sol = PoissonFDM([x, y], bound * 10,
171178
# np.ones_like(bound) * const.eps0)
172179
v_p = sol.calc_poisson()
173180
# v potential
174181
plt.pcolormesh(xv, yv, v_p)
175182
plt.show()
176183
# e field
177-
plt.pcolormesh(xv, yv, np.sqrt(sol.e_field[0]**2 + sol.e_field[1]**2))
184+
plt.pcolormesh(xv, yv, np.sqrt(sol.e_field[0]**2 + sol.e_field[1]**2))
178185
# %%

pyqhe/pytest/test_quantum_well_2d.py

Lines changed: 21 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@ def calc_omega(thickness=10, tol=5e-5):
5151
layer_list.append(Layer(20, 0.24, 0.0, name='barrier'))
5252

5353
model = Structure2D(layer_list, width=50, temp=10, delta=1)
54+
# add boundary condition
55+
grid = model.universal_grid
56+
delta = grid[0][1] - grid[0][0]
57+
xv, yv = np.meshgrid(*grid, indexing='ij')
58+
top_plate = (yv <= 15) * (yv >= 10)
59+
bottom_plate = (yv <= 65) * (yv >= 60)
60+
bound = np.empty_like(xv)
61+
bound[:] = np.nan
62+
bound[top_plate] = 0.05
63+
# bound[bottom_plate] = 0
64+
model.add_dirichlet_boundary(bound)
5465
# instance of class SchrodingerPoisson
5566
schpois = SchrodingerPoisson(
5667
model,
@@ -74,50 +85,6 @@ def calc_omega(thickness=10, tol=5e-5):
7485
antialiased=False)
7586
plt.show()
7687

77-
# # fit a normal distribution to the data
78-
# def gaussian(x, amplitude, mean, stddev):
79-
# return amplitude * np.exp(-(x - mean)**2 / 2 / stddev**2)
80-
81-
# popt, _ = optimize.curve_fit(gaussian,
82-
# res.grid,
83-
# res.electron_density,
84-
# p0=[1, np.mean(res.grid), 10])
85-
# wf2 = res.wave_function[0] * np.conj(
86-
# res.wave_function[0]) # only ground state
87-
# symmetry_axis = popt[1]
88-
# # calculate standard deviation
89-
# # the standard deviation of the charge distribution from its center, from PRL, 127, 056801 (2021)
90-
# charge_distribution = res.electron_density / np.trapz(
91-
# res.electron_density, res.grid)
92-
# sigma = np.sqrt(
93-
# np.trapz(charge_distribution * (res.grid - symmetry_axis)**2, res.grid))
94-
# plt.plot(res.grid,
95-
# wf2,
96-
# label=r'$|\Psi(z)|^2$')
97-
# plt.plot(res.grid,
98-
# charge_distribution,
99-
# label='Charge distribution',
100-
# color='r')
101-
# plt.axvline(symmetry_axis - sigma, ls='--', color='y')
102-
# plt.axvline(symmetry_axis + sigma, ls='--', color='y')
103-
# plt.xlabel('Position (nm)')
104-
# plt.ylabel('Distribution')
105-
# plt.legend()
106-
# plt.show()
107-
# # plot factor_q verses q
108-
# q_list = np.linspace(0, 1, 20)
109-
# f_fh = []
110-
# f_self = []
111-
# for q in q_list:
112-
# f_fh.append(factor_q_fh(thickness, q))
113-
# f_self.append(factor_q(res.grid, res.wave_function[0], q))
114-
# plt.plot(q_list, f_fh, label='the Fang-Howard wave function')
115-
# plt.plot(q_list, f_self, label='self-consistent wave function', color='r')
116-
# plt.legend()
117-
# plt.xlabel('wave vector q')
118-
# plt.ylabel(r'$F(q)$')
119-
# plt.show()
120-
12188
return res
12289

12390

@@ -140,29 +107,14 @@ def calc_omega(thickness=10, tol=5e-5):
140107
plt.show()
141108
plt.plot(res.grid[0], res.sigma[:, shape[1]] * 20 * 1e14)
142109
# %%
143-
thickness_list = np.linspace(10, 80, 30)
144-
res_list = []
145-
omega_list = []
146-
for thick in thickness_list:
147-
omega, res = calc_omega(thick)
148-
res_list.append(res)
149-
omega_list.append(omega)
150-
151-
152-
# %%
153-
def line(x, a, b):
154-
return a * np.asarray(x) + b
155-
156-
157-
popt1, _ = optimize.curve_fit(line, omega_list[:3], thickness_list[:3])
158-
# %%
159-
plt.plot(np.asarray(omega_list), thickness_list, label='PyQHE')
160-
plt.plot(np.asarray(stick_list) * 7.1, stick_nm, label='Shayegan')
161-
plt.xlabel(r'Layer thickness $\bar{\omega}$ (nm)')
162-
plt.ylabel(r'Geometry thickness $\omega$ (nm)')
163-
plt.legend()
164-
# %%
165-
res_list[-1].plot_quantum_well()
166-
# %%
167-
plt.plot(res.grid, res.params)
110+
xv, yv = np.meshgrid(*res.grid, indexing='ij')
111+
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
112+
surf = ax.plot_surface(xv,
113+
yv,
114+
res.v_potential,
115+
cmap=cm.coolwarm,
116+
linewidth=0,
117+
antialiased=False)
118+
plt.show()
119+
plt.plot(res.grid[1], res.v_potential[shape[0]])
168120
# %%

pyqhe/schrodinger_poisson.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from pyqhe.equation.schrodinger import SchrodingerSolver, SchrodingerShooting
66
from pyqhe.equation.poisson import PoissonSolver, PoissonODE, PoissonFDM
77
from pyqhe.utility.fermi import FermiStatistic
8-
from pyqhe.core.structure import Structure1D
8+
from pyqhe.core.structure import Structure2D
99

1010

1111
class OptimizeResult:
@@ -72,7 +72,7 @@ class SchrodingerPoisson:
7272
"""
7373

7474
def __init__(self,
75-
model: Structure1D,
75+
model: Structure2D,
7676
schsolver: SchrodingerSolver = SchrodingerShooting,
7777
poisolver: PoissonSolver = PoissonFDM,
7878
learning_rate=0.5,
@@ -87,6 +87,8 @@ def __init__(self,
8787
self.doping = model.doping # doping profile
8888
# load grid configure
8989
self.grid = model.universal_grid
90+
# load boundary condition
91+
self.bound_dirichlet = model.bound_dirichlet
9092
# Setup Quantum region
9193
# if quantum_region is not None and len(quantum_region) == 2:
9294
# self.quantum_mask = (self.grid > quantum_region[0]) * (
@@ -102,7 +104,8 @@ def __init__(self,
102104
self.fermi_util = FermiStatistic(self.grid,
103105
self.cb_meff,
104106
self.doping)
105-
self.poi_solver = poisolver(self.grid, self.doping, self.eps)
107+
self.poi_solver = poisolver(self.grid, self.doping, self.eps,
108+
self.bound_dirichlet)
106109
# accumulate charge density
107110
self.accumulate_q = self.doping
108111
for grid in self.grid[::-1]:

0 commit comments

Comments
 (0)