/
scattering_primitives.py
269 lines (199 loc) · 8.96 KB
/
scattering_primitives.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
"""Module that provides scattering functions on basic objects like various walls"""
from math import pi, cos, sin, tan, exp, sqrt, atan, asin, acos
from random import random
from numpy import sign
from freepaths.move import move
from freepaths.scattering_types import Scattering
def specularity(angle, roughness, wavelength):
"""Calculate probability of specular scattering with Soffer's equation"""
return exp(-16 * pi**2 * roughness**2 * ((cos(angle))**2) / wavelength**2)
def no_new_scattering(ph, cf):
"""
Check if new angles do not immediately lead to a new top/bottom or sidewall scattering event.
Such additional scatering event may cause troubles because at this stage we already checked the domain boundaries.
Thus, this check is necessary to prevent phonons leaving the structure boundaries.
"""
x, y, z = move(ph, cf.timestep)
return abs(z) < cf.thickness / 2 and abs(x) < cf.width / 2 and cf.length > y > 0
def random_scattering(ph):
"""Scattering in a random direction"""
ph.theta = -pi + random()*2*pi
ph.phi = asin(2*random() - 1)
return Scattering.DIFFUSE
def vertical_surface_left_scattering(ph, roughness, cf, is_diffuse=False):
"""Scattering from a vertical surface to the left"""
# Calculate angle to the surface and specular scattering probability:
a = acos(cos(ph.phi)*sin(abs(ph.theta)))
p = specularity(a, roughness, ph.wavelength)
# Specular scattering:
if random() < p and not is_diffuse:
ph.theta = - ph.theta
return Scattering.SPECULAR
# Diffuse scattering:
else:
for attempt in range(10):
# Lambert cosine distribution:
ph.theta = -pi/2 + asin(2*random() - 1)
ph.phi = asin((asin(2*random() - 1))/(pi/2))
# Accept the angles if they do not cause new scattering:
if no_new_scattering(ph, cf):
return Scattering.DIFFUSE
def vertical_surface_right_scattering(ph, roughness, cf, is_diffuse=False):
"""Scattering from a vertical surface to the left"""
# Calculate angle to the surface and specular scattering probability:
a = acos(cos(ph.phi)*sin(abs(ph.theta))) # Angle to the surface
p = specularity(a, roughness, ph.wavelength)
# Specular scattering:
if random() < p and not is_diffuse:
ph.theta = - ph.theta
return Scattering.SPECULAR
# Diffuse scattering:
else:
for attempt in range(10):
# Lambert cosine distribution:
ph.theta = +pi/2 + asin(2*random() - 1)
ph.phi = asin((asin(2*random() - 1))/(pi/2))
# Accept the angles if they do not cause new scattering:
if no_new_scattering(ph, cf):
return Scattering.DIFFUSE
def horizontal_surface_down_scattering(ph, roughness, is_diffuse=False):
"""Scattering from a horizontal surface down"""
# Calculate angle to the surface and specular scattering probability:
a = acos(cos(ph.phi)*cos(ph.theta))
p = specularity(a, roughness, ph.wavelength)
# Specular scattering:
if random() < p and not is_diffuse:
ph.theta = sign(ph.theta)*pi - ph.theta
return Scattering.SPECULAR
# Diffuse scattering:
else:
# Lambert cosine distribution:
rand_sign = sign((2*random() - 1))
ph.theta = rand_sign*pi/2 + rand_sign*acos(random())
ph.phi = asin((asin(2*random() - 1))/(pi/2))
return Scattering.DIFFUSE
def horizontal_surface_up_scattering(ph, roughness, is_diffuse=False):
"""Scattering from a horizontal surface up"""
# Calculate angle to the surface and specular scattering probability:
a = acos(cos(ph.phi)*cos(ph.theta))
p = specularity(a, roughness, ph.wavelength)
# Specular scattering:
if random() < p and not is_diffuse:
ph.theta = sign(ph.theta)*pi - ph.theta
return Scattering.SPECULAR
# Diffuse scattering:
else:
# Lambert cosine distribution:
ph.theta = asin(2*random() - 1)
ph.phi = asin((asin(2*random() - 1))/(pi/2))
return Scattering.DIFFUSE
def inclined_surfaces_down_scattering(ph, beta, x, x0, roughness):
"""Scattering from a inclined surfaces pointing down like V"""
# Calculate angle to the surface and specular scattering probability:
a = acos(cos(ph.phi)*cos(ph.theta - sign(x - x0)*(pi/2 - beta)))
p = specularity(a, roughness, ph.wavelength)
# Specular scattering:
if random() < p:
ph.theta = - ph.theta + sign(x - x0)*2*beta
return Scattering.SPECULAR
# Diffuse scattering:
else:
rand_sign = sign((2*random() - 1))
ph.theta = rand_sign*pi - rand_sign*asin(random()) - sign(x - x0)*(pi/2 - beta)
ph.phi = asin((asin(2*random() - 1))/(pi/2))
return Scattering.DIFFUSE
def inclined_surfaces_up_scattering(ph, beta, x, x0, roughness):
"""Scattering from a inclined surfaces pointing up like ^"""
# Calculate angle to the surface and specular scattering probability:
a = acos(cos(ph.phi)*cos(ph.theta + sign(x - x0)*(pi/2 - beta)))
p = specularity(a, roughness, ph.wavelength)
# Specular scattering:
if random() < p:
ph.theta = - ph.theta - sign(x - x0)*2*beta
return Scattering.SPECULAR
# Diffuse scattering:
else:
# Lambert cosine distribution:
ph.theta = asin(2*random() - 1) + sign(x - x0)*(pi/2 - beta)
ph.phi = asin((asin(2*random() - 1))/(pi/2))
return Scattering.DIFFUSE
def in_plane_surface_scattering(ph, roughness):
"""Scattering from the ceiling or floor surfaces downwards"""
# Calculate angle to the surface and specular scattering probability:
a = pi/2 - abs(ph.phi)
p = specularity(a, roughness, ph.wavelength)
# Specular scattering:
if random() < p:
ph.phi = - ph.phi
return Scattering.SPECULAR
# Diffuse scattering:
else:
# Random distribution:
# theta = -pi + random()*2*pi
# phi = -asin(random())
# Lambert cosine distribution:
ph.theta = - pi + random()*2*pi
ph.phi = - sign(ph.phi) * random()*pi/2
return Scattering.DIFFUSE
def circle_outer_scattering(ph, tangent_theta, y, y0, roughness, cf):
"""Scattering from the outer surface of the circle"""
# Calculate angle to the surface and specular scattering probability:
a = acos(cos(ph.phi)*cos(ph.theta + sign(y - y0)*tangent_theta))
p = specularity(a, roughness, ph.wavelength)
# Specular scattering:
if random() < p:
ph.theta = - ph.theta - pi + 2*tangent_theta
return Scattering.SPECULAR
# Diffuse scattering:
else:
for attempt in range(10):
# Random distribution:
# theta = tangent_theta - (-pi/2 + pi*random()) - pi*(y < y0)
# phi = asin(2*random() - 1)
# Lambert cosine distribution:
ph.theta = tangent_theta - (asin(2*random() - 1)) - pi*(y < y0)
ph.phi = asin((asin(2*random() - 1))/(pi/2))
# Accept the angles only if they do not immediately cause new scattering:
if no_new_scattering(ph, cf):
return Scattering.DIFFUSE
def circle_inner_scattering(ph, tangent_theta, y, y0, roughness):
"""Scattering from the inner surface of the circle"""
a = atan(tan((pi/2 - ph.theta) + tangent_theta) * cos(ph.phi))
p = specularity(a, roughness, ph.wavelength)
# Specular scattering:
if random() < p:
ph.theta = - ph.theta - pi + 2*tangent_theta
return Scattering.SPECULAR
# Diffuse scattering:
else:
ph.theta = tangent_theta - asin(2*random()-1) + pi*(y >= y0)
ph.phi = asin((asin(2*random() - 1))/(pi/2))
return Scattering.DIFFUSE
def circle_inclined_inner_scattering(ph, tangent_theta, y, y0, roughness):
"""
Scattering from the inner inclined surface of the circle.
This is used for pillars with inclide walls.
THIS IS NOT TESTED AND NOT WORKING.
"""
a = atan(tan((pi/2 - ph.theta) + tangent_theta) * cos(ph.phi)) # - (pi / 2 - pillar.wall_angle)))
p = specularity(a, roughness, ph.wavelength)
# Specular scattering:
if random() < p:
# If phonon moves from the center of the pillar to the wall:
if distance_from_pillar_center > distance_from_pillar_center_original:
# If theta does not reflect back:
if ph.phi < pi/2 - 2 * pillar.wall_angle:
ph.phi = ph.phi # - (pi / 2 - pillar.wall_angle)
# Regular reflection:
else:
ph.theta = - ph.theta - pi + 2*tangent_theta
ph.phi = ph.phi # - (pi / 2 - pillar.wall_angle)
# If phonon strikes the wall as it goes towards the center:
else:
ph.phi = -sign(ph.phi) * ph.phi #- 2 * pillar.wall_angle
return Scattering.SPECULAR
# Diffuse scattering:
else:
ph.theta = tangent_theta - asin(2*random()-1) + pi*(y >= y0)
ph.phi = asin((asin(2*random() - 1))/(pi/2)) # - (pi / 2 - pillar.wall_angle)
return Scattering.DIFFUSE