freepaths 2.0__tar.gz → 2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {freepaths-2.0 → freepaths-2.1}/PKG-INFO +15 -5
- {freepaths-2.0 → freepaths-2.1}/freepaths/__main__.py +1 -1
- {freepaths-2.0 → freepaths-2.1}/freepaths/animation.py +2 -2
- {freepaths-2.0 → freepaths-2.1}/freepaths/config.py +3 -1
- {freepaths-2.0 → freepaths-2.1}/freepaths/data.py +8 -0
- {freepaths-2.0 → freepaths-2.1}/freepaths/default_config.py +2 -0
- {freepaths-2.0 → freepaths-2.1}/freepaths/main_mfp_sampling.py +3 -2
- {freepaths-2.0 → freepaths-2.1}/freepaths/main_tracing.py +1 -1
- {freepaths-2.0 → freepaths-2.1}/freepaths/output_info.py +46 -36
- {freepaths-2.0 → freepaths-2.1}/freepaths/output_plots.py +10 -4
- {freepaths-2.0 → freepaths-2.1}/freepaths/output_structure.py +30 -1
- freepaths-2.0/freepaths/holes.py → freepaths-2.1/freepaths/scatterers.py +117 -7
- {freepaths-2.0 → freepaths-2.1}/freepaths/scattering.py +16 -30
- {freepaths-2.0 → freepaths-2.1}/freepaths/scattering_types.py +6 -2
- {freepaths-2.0 → freepaths-2.1}/freepaths.egg-info/PKG-INFO +15 -5
- {freepaths-2.0 → freepaths-2.1}/freepaths.egg-info/SOURCES.txt +1 -1
- {freepaths-2.0 → freepaths-2.1}/setup.py +3 -3
- {freepaths-2.0 → freepaths-2.1}/README.md +0 -0
- {freepaths-2.0 → freepaths-2.1}/freepaths/flight.py +0 -0
- {freepaths-2.0 → freepaths-2.1}/freepaths/maps.py +0 -0
- {freepaths-2.0 → freepaths-2.1}/freepaths/materials.py +0 -0
- {freepaths-2.0 → freepaths-2.1}/freepaths/move.py +0 -0
- {freepaths-2.0 → freepaths-2.1}/freepaths/phonon.py +0 -0
- {freepaths-2.0 → freepaths-2.1}/freepaths/progress.py +0 -0
- {freepaths-2.0 → freepaths-2.1}/freepaths/run_phonon.py +0 -0
- {freepaths-2.0 → freepaths-2.1}/freepaths/scattering_primitives.py +0 -0
- {freepaths-2.0 → freepaths-2.1}/freepaths/scattering_semicircle.py +0 -0
- {freepaths-2.0 → freepaths-2.1}/freepaths/sources.py +0 -0
- {freepaths-2.0 → freepaths-2.1}/freepaths.egg-info/dependency_links.txt +0 -0
- {freepaths-2.0 → freepaths-2.1}/freepaths.egg-info/entry_points.txt +0 -0
- {freepaths-2.0 → freepaths-2.1}/freepaths.egg-info/requires.txt +0 -0
- {freepaths-2.0 → freepaths-2.1}/freepaths.egg-info/top_level.txt +0 -0
- {freepaths-2.0 → freepaths-2.1}/pyproject.toml +0 -0
- {freepaths-2.0 → freepaths-2.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: freepaths
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1
|
|
4
4
|
Summary: Phonon Monte Carlo simulator
|
|
5
5
|
Home-page: https://github.com/anufrievroman/freepaths
|
|
6
6
|
Author: Roman Anufriev
|
|
@@ -11,18 +11,28 @@ Classifier: License :: OSI Approved :: GNU General Public License (GPL)
|
|
|
11
11
|
Classifier: Environment :: Console
|
|
12
12
|
Classifier: Natural Language :: English
|
|
13
13
|
Classifier: Operating System :: OS Independent
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
16
14
|
Classifier: Programming Language :: Python :: 3.9
|
|
17
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Classifier: Topic :: Utilities
|
|
19
|
-
Requires-Python: ~=3.
|
|
19
|
+
Requires-Python: ~=3.11
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
Requires-Dist: numpy
|
|
22
22
|
Requires-Dist: matplotlib
|
|
23
23
|
Requires-Dist: scipy
|
|
24
24
|
Requires-Dist: imageio
|
|
25
25
|
Requires-Dist: colorama
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: author-email
|
|
28
|
+
Dynamic: classifier
|
|
29
|
+
Dynamic: description
|
|
30
|
+
Dynamic: description-content-type
|
|
31
|
+
Dynamic: home-page
|
|
32
|
+
Dynamic: license
|
|
33
|
+
Dynamic: requires-dist
|
|
34
|
+
Dynamic: requires-python
|
|
35
|
+
Dynamic: summary
|
|
26
36
|
|
|
27
37
|
# FreePATHS - Free Phonon And Thermal Simulator
|
|
28
38
|
|
|
@@ -8,7 +8,7 @@ import numpy as np
|
|
|
8
8
|
import matplotlib.pyplot as plt
|
|
9
9
|
|
|
10
10
|
from freepaths.config import cf
|
|
11
|
-
from freepaths.output_structure import
|
|
11
|
+
from freepaths.output_structure import draw_structure_top_view
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def generate_frames_xy():
|
|
@@ -28,7 +28,7 @@ def generate_frames_xy():
|
|
|
28
28
|
fig, ax = plt.subplots()
|
|
29
29
|
|
|
30
30
|
# Draw the structure:
|
|
31
|
-
patches =
|
|
31
|
+
patches = draw_structure_top_view(cf, color_back=cf.output_structure_color)
|
|
32
32
|
|
|
33
33
|
for patch in patches:
|
|
34
34
|
ax.add_patch(patch)
|
|
@@ -9,7 +9,7 @@ import logging
|
|
|
9
9
|
from colorama import Fore, Style
|
|
10
10
|
|
|
11
11
|
from freepaths.sources import Distributions
|
|
12
|
-
from freepaths.
|
|
12
|
+
from freepaths.scatterers import *
|
|
13
13
|
|
|
14
14
|
# Import a default input file:
|
|
15
15
|
from freepaths.default_config import *
|
|
@@ -110,10 +110,12 @@ class Config:
|
|
|
110
110
|
self.top_roughness = TOP_ROUGHNESS
|
|
111
111
|
self.bottom_roughness = BOTTOM_ROUGHNESS
|
|
112
112
|
self.pillar_top_roughness = PILLAR_TOP_ROUGHNESS
|
|
113
|
+
self.interface_roughness = INTERFACE_ROUGHNESS
|
|
113
114
|
|
|
114
115
|
# Hole array parameters:
|
|
115
116
|
self.holes = HOLES
|
|
116
117
|
self.pillars = PILLARS
|
|
118
|
+
self.interfaces = INTERFACES
|
|
117
119
|
|
|
118
120
|
# Multiprocessing:
|
|
119
121
|
self.num_workers = NUMBER_OF_PROCESSES
|
|
@@ -126,6 +126,8 @@ class ScatteringData(Data):
|
|
|
126
126
|
self.pillar_specular = np.zeros(cf.number_of_length_segments+1)
|
|
127
127
|
self.hot_side = np.zeros(cf.number_of_length_segments+1)
|
|
128
128
|
self.internal = np.zeros(cf.number_of_length_segments+1)
|
|
129
|
+
self.interfaces_diffuse = np.zeros(cf.number_of_length_segments+1)
|
|
130
|
+
self.interfaces_specular = np.zeros(cf.number_of_length_segments+1)
|
|
129
131
|
self.total = np.zeros(cf.number_of_length_segments+1)
|
|
130
132
|
|
|
131
133
|
def save_scattering_events(self, y, scattering_types):
|
|
@@ -152,6 +154,10 @@ class ScatteringData(Data):
|
|
|
152
154
|
self.pillar_diffuse[segment] += 1 if scattering_types.pillars == Scattering.DIFFUSE else 0
|
|
153
155
|
self.pillar_specular[segment] += 1 if scattering_types.pillars == Scattering.SPECULAR else 0
|
|
154
156
|
|
|
157
|
+
# Scattering on pillars:
|
|
158
|
+
self.interfaces_diffuse[segment] += 1 if scattering_types.interfaces == Scattering.DIFFUSE else 0
|
|
159
|
+
self.interfaces_specular[segment] += 1 if scattering_types.interfaces == Scattering.SPECULAR else 0
|
|
160
|
+
|
|
155
161
|
# Internal scattering and rethermalization on hot side:
|
|
156
162
|
self.hot_side[segment] += 1 if scattering_types.hot_side == Scattering.DIFFUSE else 0
|
|
157
163
|
self.internal[segment] += 1 if scattering_types.internal == Scattering.DIFFUSE else 0
|
|
@@ -181,6 +187,8 @@ class ScatteringData(Data):
|
|
|
181
187
|
'pillar_specular': self.pillar_specular,
|
|
182
188
|
'hot_side': self.hot_side,
|
|
183
189
|
'internal': self.internal,
|
|
190
|
+
'interfaces_diffuse': self.interfaces_diffuse,
|
|
191
|
+
'interfaces_specular': self.interfaces_specular,
|
|
184
192
|
'total': self.total
|
|
185
193
|
}
|
|
186
194
|
|
|
@@ -69,10 +69,12 @@ PILLAR_ROUGHNESS = 2e-9
|
|
|
69
69
|
TOP_ROUGHNESS = 0.2e-9
|
|
70
70
|
BOTTOM_ROUGHNESS = 0.2e-9
|
|
71
71
|
PILLAR_TOP_ROUGHNESS = 0.2e-9
|
|
72
|
+
INTERFACE_ROUGHNESS = 0.2e-9
|
|
72
73
|
|
|
73
74
|
# Holes and pillars:
|
|
74
75
|
HOLES = []
|
|
75
76
|
PILLARS = []
|
|
77
|
+
INTERFACES = []
|
|
76
78
|
|
|
77
79
|
# Multiprocessing:
|
|
78
80
|
NUMBER_OF_PROCESSES = 10
|
|
@@ -15,7 +15,7 @@ from freepaths.config import cf
|
|
|
15
15
|
from freepaths.run_phonon import run_phonon
|
|
16
16
|
from freepaths.phonon import Phonon
|
|
17
17
|
from freepaths.flight import Flight
|
|
18
|
-
from freepaths.data import ScatteringData, GeneralData, SegmentData, PathData
|
|
18
|
+
from freepaths.data import ScatteringData, GeneralData, SegmentData, PathData, TriangleScatteringData
|
|
19
19
|
from freepaths.progress import Progress
|
|
20
20
|
from freepaths.materials import Si, SiC, Graphite
|
|
21
21
|
from freepaths.maps import ScatteringMap, ThermalMaps
|
|
@@ -45,6 +45,7 @@ def main(input_file):
|
|
|
45
45
|
scatter_stats = ScatteringData()
|
|
46
46
|
general_stats = GeneralData()
|
|
47
47
|
segment_stats = SegmentData()
|
|
48
|
+
places_stats = TriangleScatteringData()
|
|
48
49
|
path_stats = PathData()
|
|
49
50
|
scatter_maps = ScatteringMap()
|
|
50
51
|
thermal_maps = ThermalMaps()
|
|
@@ -67,7 +68,7 @@ def main(input_file):
|
|
|
67
68
|
flight = Flight(phonon)
|
|
68
69
|
|
|
69
70
|
# Run this phonon through the structure:
|
|
70
|
-
run_phonon(phonon, flight, scatter_stats, segment_stats, thermal_maps, scatter_maps, material)
|
|
71
|
+
run_phonon(phonon, flight, scatter_stats, places_stats, segment_stats, thermal_maps, scatter_maps, material)
|
|
71
72
|
|
|
72
73
|
# Heat capacity, Ref. PRB 88 155318 (2013):
|
|
73
74
|
omega = 2 * math.pi * phonon.f
|
|
@@ -125,7 +125,7 @@ def worker_process(worker_id, total_phonons, shared_list, output_trajectories_of
|
|
|
125
125
|
def display_workers_finished(finished_workers):
|
|
126
126
|
""" Print out the number of active workers"""
|
|
127
127
|
while True:
|
|
128
|
-
text_to_display = f'
|
|
128
|
+
text_to_display = f' Processes finished: {finished_workers.value}/{cf.num_workers}'
|
|
129
129
|
sys.stdout.write(text_to_display)
|
|
130
130
|
sys.stdout.write(f'\033[{len(text_to_display)}D') # move cursor back
|
|
131
131
|
sys.stdout.flush()
|
|
@@ -15,24 +15,25 @@ def output_general_information(start_time):
|
|
|
15
15
|
travel_times = np.loadtxt("Data/All travel times.csv", encoding='utf-8')
|
|
16
16
|
percentage = int(100 * np.count_nonzero(travel_times) / cf.number_of_phonons)
|
|
17
17
|
|
|
18
|
+
info = [
|
|
19
|
+
f'The simulation finished on {time.strftime("%d %B %Y")}, at {time.strftime("%H:%M")}.',
|
|
20
|
+
f'\nIt took about {int((time.time()-start_time)//60)} min to run.\n',
|
|
21
|
+
f'\nNumber of phonons = {cf.number_of_phonons}',
|
|
22
|
+
f'\nNumber of timesteps = {cf.number_of_timesteps}',
|
|
23
|
+
f'\nLength of a timestep = {cf.timestep} s',
|
|
24
|
+
f'\nTemperature = {cf.temp} K\n',
|
|
25
|
+
f'\nMaterial: {cf.media}\n',
|
|
26
|
+
f'\nLength = {cf.length * 1e9:.1f} nm',
|
|
27
|
+
f'\nWidth = {cf.width * 1e9:.1f} nm',
|
|
28
|
+
f'\nThickness = {cf.thickness * 1e9:.1f} nm\n',
|
|
29
|
+
f'\nSide wall roughness = {cf.side_wall_roughness * 1e9:.1f} nm',
|
|
30
|
+
f'\nHole roughness = {cf.hole_roughness * 1e9:.1f} nm',
|
|
31
|
+
f'\nTop roughness = {cf.top_roughness * 1e9:.1f} nm',
|
|
32
|
+
f'\nBottom roughness = {cf.bottom_roughness * 1e9:.1f} nm',
|
|
33
|
+
f'\nInterface roughness = {cf.interface_roughness * 1e9:.1f} nm\n',
|
|
34
|
+
f'\n{percentage:.0f}% of phonons reached the cold side\n'
|
|
35
|
+
]
|
|
18
36
|
with open("Information.txt", "w+", encoding="utf-8") as file:
|
|
19
|
-
info = (
|
|
20
|
-
f'The simulation finished on {time.strftime("%d %B %Y")}, at {time.strftime("%H:%M")}.',
|
|
21
|
-
f'\nIt took about {int((time.time()-start_time)//60)} min to run.\n',
|
|
22
|
-
f'\nNumber of phonons = {cf.number_of_phonons}',
|
|
23
|
-
f'\nNumber of timesteps = {cf.number_of_timesteps}',
|
|
24
|
-
f'\nLength of a timestep = {cf.timestep} s',
|
|
25
|
-
f'\nTemperature = {cf.temp} K\n',
|
|
26
|
-
f'\nMaterial: {cf.media}\n',
|
|
27
|
-
f'\nLength = {cf.length * 1e9:.1f} nm',
|
|
28
|
-
f'\nWidth = {cf.width * 1e9:.1f} nm',
|
|
29
|
-
f'\nThickness = {cf.thickness * 1e9:.1f} nm\n',
|
|
30
|
-
f'\nSide wall roughness = {cf.side_wall_roughness * 1e9:.1f} nm',
|
|
31
|
-
f'\nHole roughness = {cf.hole_roughness * 1e9:.1f} nm',
|
|
32
|
-
f'\nTop roughness = {cf.top_roughness * 1e9:.1f} nm',
|
|
33
|
-
f'\nBottom roughness = {cf.bottom_roughness * 1e9:.1f} nm\n',
|
|
34
|
-
f'\n{percentage:.0f}% of phonons reached the cold side\n'
|
|
35
|
-
)
|
|
36
37
|
file.writelines(info)
|
|
37
38
|
|
|
38
39
|
|
|
@@ -45,6 +46,7 @@ def output_scattering_information(scatter_stats):
|
|
|
45
46
|
total_topbot = np.sum(scatter_stats.top_diffuse) + np.sum(scatter_stats.top_specular)
|
|
46
47
|
total_hole = np.sum(scatter_stats.hole_diffuse) + np.sum(scatter_stats.hole_specular)
|
|
47
48
|
total_pill = np.sum(scatter_stats.pillar_diffuse) + np.sum(scatter_stats.pillar_specular)
|
|
49
|
+
total_interf = np.sum(scatter_stats.interfaces_diffuse) + np.sum(scatter_stats.interfaces_specular)
|
|
48
50
|
|
|
49
51
|
sc_on_walls = 100*(np.sum(scatter_stats.wall_diffuse) +
|
|
50
52
|
np.sum(scatter_stats.wall_specular)) / total
|
|
@@ -64,7 +66,7 @@ def output_scattering_information(scatter_stats):
|
|
|
64
66
|
retherm = 100*np.sum(scatter_stats.hot_side) / total
|
|
65
67
|
internal = 100*np.sum(scatter_stats.internal) / total
|
|
66
68
|
|
|
67
|
-
|
|
69
|
+
info = [
|
|
68
70
|
f'\n{sc_on_walls:.2f}% - scattering on side walls ',
|
|
69
71
|
f'({sc_on_walls_diff:.2f}% - diffuse, ',
|
|
70
72
|
f'{sc_on_walls_spec:.2f}% - specular)',
|
|
@@ -73,37 +75,45 @@ def output_scattering_information(scatter_stats):
|
|
|
73
75
|
f'{sc_on_topbot_spec:.2f}% - specular)',
|
|
74
76
|
f'\n{retherm:.2f}% - rethermalization at the hot side',
|
|
75
77
|
f'\n{internal:.2f}% - internal scattering processes',
|
|
76
|
-
|
|
78
|
+
]
|
|
77
79
|
|
|
80
|
+
# If scatterers are present, add their information:
|
|
78
81
|
if cf.holes:
|
|
79
82
|
sc_on_holes = 100*(np.sum(scatter_stats.hole_diffuse) +
|
|
80
83
|
np.sum(scatter_stats.hole_specular)) / total
|
|
81
84
|
sc_on_holes_diff = 100*np.sum(scatter_stats.hole_diffuse) / total_hole
|
|
82
85
|
sc_on_holes_spec = 100*np.sum(scatter_stats.hole_specular) / total_hole
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
info1.extend([
|
|
87
|
+
f'\n{sc_on_holes:.2f}% - scattering on hole walls ',
|
|
88
|
+
f'({sc_on_holes_diff:.2f}% - diffuse, ',
|
|
89
|
+
f'{sc_on_holes_spec:.2f}% - specular)']
|
|
90
|
+
)
|
|
88
91
|
|
|
89
92
|
if cf.pillars:
|
|
90
93
|
sc_on_pill = 100*(np.sum(scatter_stats.pillar_diffuse) +
|
|
91
94
|
np.sum(scatter_stats.pillar_specular)) / total
|
|
92
95
|
sc_on_pill_diff = 100*np.sum(scatter_stats.pillar_diffuse) / total_pill
|
|
93
96
|
sc_on_pill_spec = 100*np.sum(scatter_stats.pillar_specular) / total_pill
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
info.extend([
|
|
98
|
+
f'\n{sc_on_pill:.2f}% - scattering on pillar walls ',
|
|
99
|
+
f'({sc_on_pill_diff:.2f}% - diffuse, ',
|
|
100
|
+
f'{sc_on_pill_spec:.2f}% - specular)']
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
if cf. interfaces:
|
|
104
|
+
sc_on_interf = 100*(np.sum(scatter_stats.interfaces_diffuse) +
|
|
105
|
+
np.sum(scatter_stats.interfaces_specular)) / total
|
|
106
|
+
sc_on_interf_diff = 100*np.sum(scatter_stats.interfaces_diffuse) / total_interf
|
|
107
|
+
sc_on_interf_spec = 100*np.sum(scatter_stats.interfaces_specular) / total_interf
|
|
108
|
+
info.extend([
|
|
109
|
+
f'\n{sc_on_interf:.2f}% - scattering on interfaces ',
|
|
110
|
+
f'({sc_on_interf_diff:.2f}% - diffuse, ',
|
|
111
|
+
f'{sc_on_interf_spec:.2f}% - specular)']
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Write the file:
|
|
101
115
|
with open("Information.txt", "a", encoding="utf-8") as file:
|
|
102
|
-
file.writelines(
|
|
103
|
-
if cf.holes:
|
|
104
|
-
file.writelines(info2)
|
|
105
|
-
if cf.pillars:
|
|
106
|
-
file.writelines(info3)
|
|
116
|
+
file.writelines(info)
|
|
107
117
|
|
|
108
118
|
|
|
109
119
|
def output_parameter_warnings():
|
|
@@ -9,7 +9,7 @@ from matplotlib.colors import LogNorm
|
|
|
9
9
|
from matplotlib import font_manager
|
|
10
10
|
|
|
11
11
|
from freepaths.config import cf
|
|
12
|
-
from freepaths.output_structure import
|
|
12
|
+
from freepaths.output_structure import draw_structure_top_view, draw_structure_side_view
|
|
13
13
|
from freepaths.materials import Si, SiC, Graphite
|
|
14
14
|
import matplotlib.pyplot as plt
|
|
15
15
|
|
|
@@ -384,8 +384,8 @@ def plot_trajectories():
|
|
|
384
384
|
# Create XY plot:
|
|
385
385
|
fig, ax = plt.subplots()
|
|
386
386
|
|
|
387
|
-
# Draw
|
|
388
|
-
patches =
|
|
387
|
+
# Draw structure:
|
|
388
|
+
patches = draw_structure_top_view(cf, color_holes='white', color_back=cf.output_structure_color)
|
|
389
389
|
for patch in patches:
|
|
390
390
|
ax.add_patch(patch)
|
|
391
391
|
|
|
@@ -405,6 +405,12 @@ def plot_trajectories():
|
|
|
405
405
|
|
|
406
406
|
# Create YZ plot:
|
|
407
407
|
fig, ax = plt.subplots()
|
|
408
|
+
|
|
409
|
+
# Draw structure:
|
|
410
|
+
patches = draw_structure_side_view(cf, color_holes='white', color_back=cf.output_structure_color)
|
|
411
|
+
for patch in patches:
|
|
412
|
+
ax.add_patch(patch)
|
|
413
|
+
|
|
408
414
|
for index in range(cf.output_trajectories_of_first):
|
|
409
415
|
y_coordinates = np.trim_zeros(data[:, 3 * index + 1], trim='b')
|
|
410
416
|
num_of_points = len(y_coordinates)
|
|
@@ -465,7 +471,7 @@ def plot_structure():
|
|
|
465
471
|
fig, ax = plt.subplots()
|
|
466
472
|
|
|
467
473
|
# Draw structures:
|
|
468
|
-
patches =
|
|
474
|
+
patches = draw_structure_top_view(cf, color_holes='black', color_back='royalblue')
|
|
469
475
|
for patch in patches:
|
|
470
476
|
ax.add_patch(patch)
|
|
471
477
|
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from matplotlib.patches import Rectangle
|
|
4
4
|
from freepaths.config import cf
|
|
5
|
+
from freepaths.scatterers import HorizontalPlane, VerticalPlane
|
|
5
6
|
|
|
6
|
-
def
|
|
7
|
+
def draw_structure_top_view(cf, color_holes="white", color_back="gray"):
|
|
7
8
|
"""Draw shape of the structure using patches from matplotlib"""
|
|
8
9
|
|
|
9
10
|
# Overal shape as gray area:
|
|
@@ -26,6 +27,12 @@ def draw_structure(cf, color_holes="white", color_back="gray"):
|
|
|
26
27
|
patch = pillar.get_patch(color_holes, cf)
|
|
27
28
|
patches.extend(patch if isinstance(patch, list) else [patch])
|
|
28
29
|
|
|
30
|
+
# Interfaces as white patches:
|
|
31
|
+
for interface in cf.interfaces:
|
|
32
|
+
if isinstance(interface, VerticalPlane):
|
|
33
|
+
patch = interface.get_patch(color_holes, cf)
|
|
34
|
+
patches.extend(patch if isinstance(patch, list) else [patch])
|
|
35
|
+
|
|
29
36
|
# Phonon source areas as red patches:
|
|
30
37
|
for source in cf.phonon_sources:
|
|
31
38
|
width_x = 1e6 * cf.width / 49 if source.size_x == 0 else 1e6 * source.size_x
|
|
@@ -38,3 +45,25 @@ def draw_structure(cf, color_holes="white", color_back="gray"):
|
|
|
38
45
|
patches.append(phonon_source_patch)
|
|
39
46
|
|
|
40
47
|
return patches
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def draw_structure_side_view(cf, color_holes="white", color_back="gray"):
|
|
51
|
+
"""Draw shape of the structure using patches from matplotlib"""
|
|
52
|
+
|
|
53
|
+
# Overal shape as gray area:
|
|
54
|
+
patches = [
|
|
55
|
+
Rectangle(
|
|
56
|
+
(0, -1e6 * cf.thickness/2),
|
|
57
|
+
1e6 * cf.length,
|
|
58
|
+
1e6 * cf.thickness,
|
|
59
|
+
facecolor=color_back,
|
|
60
|
+
)
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
# Interfaces as white patches:
|
|
64
|
+
for interface in cf.interfaces:
|
|
65
|
+
if isinstance(interface, HorizontalPlane):
|
|
66
|
+
patch = interface.get_patch(color_holes, cf)
|
|
67
|
+
patches.extend(patch if isinstance(patch, list) else [patch])
|
|
68
|
+
|
|
69
|
+
return patches
|
|
@@ -6,6 +6,7 @@ These classes contain all methods associated with scattering on holes.
|
|
|
6
6
|
|
|
7
7
|
from math import atan
|
|
8
8
|
from numpy import pi, array, linspace, column_stack, vstack
|
|
9
|
+
from random import random
|
|
9
10
|
from matplotlib.patches import Rectangle, Circle, Polygon
|
|
10
11
|
from scipy.spatial import cKDTree
|
|
11
12
|
|
|
@@ -75,23 +76,31 @@ class CircularHole(Hole):
|
|
|
75
76
|
class RectangularHole(Hole):
|
|
76
77
|
"""Shape of a rectangular hole"""
|
|
77
78
|
|
|
78
|
-
def __init__(self, x=0, y=0, size_x=100e-9, size_y=100e-9):
|
|
79
|
+
def __init__(self, x=0, y=0, size_x=100e-9, size_y=100e-9, depth=None):
|
|
79
80
|
self.x0 = x
|
|
80
81
|
self.y0 = y
|
|
81
82
|
self.size_x = size_x
|
|
82
83
|
self.size_y = size_y
|
|
84
|
+
self.depth = depth
|
|
83
85
|
|
|
84
86
|
def is_inside(self, x, y, z, cf):
|
|
85
|
-
"""Check if phonon with given coordinates traverses the boundary"""
|
|
86
|
-
|
|
87
|
+
"""Check if phonon with given coordinates traverses the boundary. It also depens on in the hole is complete or partial."""
|
|
88
|
+
if self.depth and z:
|
|
89
|
+
return (abs(x - self.x0) <= self.size_x / 2) and (abs(y - self.y0) <= self.size_y / 2) and (z > cf.thickness/2 - self.depth)
|
|
90
|
+
else:
|
|
91
|
+
return (abs(x - self.x0) <= self.size_x / 2) and (abs(y - self.y0) <= self.size_y / 2)
|
|
92
|
+
|
|
87
93
|
|
|
88
94
|
def scatter(self, ph, scattering_types, x, y, z, cf):
|
|
89
95
|
"""Calculate the new direction after scattering on the hole"""
|
|
90
96
|
|
|
91
|
-
#
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
97
|
+
# If phonon arrives from below, then it's bottom scattering:
|
|
98
|
+
if self.depth and ph.z < (cf.thickness/2 - self.depth):
|
|
99
|
+
scattering_types.holes = in_plane_surface_scattering(ph, cf.top_roughness)
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
# Otherwise, calculate coordinate y of the intersection with the hole side:
|
|
103
|
+
y1 = (self.y0 - y) + cos(ph.theta) * (self.size_x / 2 - abs(self.x0 - x)) / abs(sin(ph.theta))
|
|
95
104
|
|
|
96
105
|
# Scattering on the left wall:
|
|
97
106
|
if abs(y1) <= self.size_y / 2 and x < self.x0:
|
|
@@ -611,3 +620,104 @@ class CircularPillar():
|
|
|
611
620
|
def get_patch(self, color_holes, cf):
|
|
612
621
|
"""Create a patch in the shape of the hole to use in the plots"""
|
|
613
622
|
return Circle((1e6 * self.x0, 1e6 * self.y0), 1e6 * self.diameter / 2, facecolor=color_holes,)
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
class Interface:
|
|
626
|
+
def is_crossed(self, ph, x, y, z) -> bool:
|
|
627
|
+
"""
|
|
628
|
+
Check if phonon with given coordinates traverses the plane.
|
|
629
|
+
It returns True or False depending whether x, y, z are on the other side.
|
|
630
|
+
"""
|
|
631
|
+
pass
|
|
632
|
+
|
|
633
|
+
def scatter(self, ph, scattering_types, x, y, z, cf):
|
|
634
|
+
"""
|
|
635
|
+
Calculate the new direction after scattering on the interface wall.
|
|
636
|
+
It returns ScatteringTypes object with the scattering type that occured.
|
|
637
|
+
"""
|
|
638
|
+
pass
|
|
639
|
+
|
|
640
|
+
def is_transmitted(self) -> bool:
|
|
641
|
+
"""
|
|
642
|
+
Check if phonon transmitted without scattering, taking into account the probability.
|
|
643
|
+
It return True if transmission occured or False if it must scatter.
|
|
644
|
+
"""
|
|
645
|
+
pass
|
|
646
|
+
|
|
647
|
+
def get_patch(self, color_holes, cf):
|
|
648
|
+
"""
|
|
649
|
+
Create a patch in the shape of a thin line to use in the plots.
|
|
650
|
+
It returns the matplotlib.patches objects like Rectangle etc.
|
|
651
|
+
"""
|
|
652
|
+
pass
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
class VerticalPlane(Interface):
|
|
656
|
+
"""Vertical plane that represents an interface"""
|
|
657
|
+
|
|
658
|
+
def __init__(self, position_x=0, transmission=0):
|
|
659
|
+
self.position_x = position_x
|
|
660
|
+
self.transmission = transmission
|
|
661
|
+
|
|
662
|
+
def is_crossed(self, ph, x, y, z):
|
|
663
|
+
"""Check if phonon with traverses the vertical plane at given coordinate"""
|
|
664
|
+
return (ph.x < self.position_x < x) or (ph.x > self.position_x > x)
|
|
665
|
+
|
|
666
|
+
def is_transmitted(self):
|
|
667
|
+
"""Check if phonon traverses the plane given the transmission probability"""
|
|
668
|
+
return random() < self.transmission
|
|
669
|
+
|
|
670
|
+
def scatter(self, ph, scattering_types, x, y, z, cf):
|
|
671
|
+
"""Calculate the new direction after scattering on the interface wall"""
|
|
672
|
+
|
|
673
|
+
# Scattering on the left wall:
|
|
674
|
+
if x < self.position_x:
|
|
675
|
+
scattering_types.interfaces = vertical_surface_left_scattering(ph, cf.interface_roughness, cf)
|
|
676
|
+
|
|
677
|
+
# Scattering on the right wall:
|
|
678
|
+
else:
|
|
679
|
+
scattering_types.interfaces = vertical_surface_right_scattering(ph, cf.interface_roughness, cf)
|
|
680
|
+
|
|
681
|
+
def get_patch(self, color_holes, cf):
|
|
682
|
+
"""Create a patch in the shape of a thin line to use in the plots"""
|
|
683
|
+
return Rectangle(
|
|
684
|
+
(1e6 * self.position_x, 0),
|
|
685
|
+
1e6 * 0.005*cf.width, 1e6 * cf.length,
|
|
686
|
+
facecolor=color_holes,
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
class HorizontalPlane(Interface):
|
|
692
|
+
"""Horizontal plane that represents an interface"""
|
|
693
|
+
|
|
694
|
+
def __init__(self, position_z=0, transmission=0):
|
|
695
|
+
self.position_z = position_z
|
|
696
|
+
self.transmission = transmission
|
|
697
|
+
|
|
698
|
+
def is_crossed(self, ph, x, y, z):
|
|
699
|
+
"""Check if phonon with traverses the vertical plane at given coordinate"""
|
|
700
|
+
return (ph.z < self.position_z < z) or (ph.z > self.position_z > z)
|
|
701
|
+
|
|
702
|
+
def is_transmitted(self):
|
|
703
|
+
"""Check if phonon traverses the plane given the transmission probability"""
|
|
704
|
+
return random() < self.transmission
|
|
705
|
+
|
|
706
|
+
def scatter(self, ph, scattering_types, x, y, z, cf):
|
|
707
|
+
"""Calculate the new direction after scattering on the interface wall"""
|
|
708
|
+
|
|
709
|
+
# Scattering on the left wall:
|
|
710
|
+
if x < self.position_z:
|
|
711
|
+
scattering_types.interfaces = horizontal_surface_up_scattering(ph, cf.interface_roughness, cf)
|
|
712
|
+
|
|
713
|
+
# Scattering on the right wall:
|
|
714
|
+
else:
|
|
715
|
+
scattering_types.interfaces = horizontal_surface_down_scattering(ph, cf.interface_roughness, cf)
|
|
716
|
+
|
|
717
|
+
def get_patch(self, color_holes, cf):
|
|
718
|
+
"""Create a patch in the shape of a thin line to use in the plots"""
|
|
719
|
+
return Rectangle(
|
|
720
|
+
(0, 1e6 * self.position_z),
|
|
721
|
+
1e6 * cf.length, 1e6 * 0.005*cf.thickness,
|
|
722
|
+
facecolor=color_holes,
|
|
723
|
+
)
|
|
@@ -23,56 +23,40 @@ def reinitialization(ph, scattering_types):
|
|
|
23
23
|
x, y, _ = move(ph, cf.timestep)
|
|
24
24
|
|
|
25
25
|
if cf.hot_side_position_bottom and y < 0:
|
|
26
|
-
scattering_types.hot_side = horizontal_surface_up_scattering(
|
|
27
|
-
ph, cf.side_wall_roughness, is_diffuse=True
|
|
28
|
-
)
|
|
26
|
+
scattering_types.hot_side = horizontal_surface_up_scattering(ph, cf.side_wall_roughness, is_diffuse=True)
|
|
29
27
|
|
|
30
28
|
if cf.hot_side_position_top and y > cf.length:
|
|
31
|
-
scattering_types.hot_side = horizontal_surface_down_scattering(
|
|
32
|
-
ph, cf.side_wall_roughness, is_diffuse=True
|
|
33
|
-
)
|
|
29
|
+
scattering_types.hot_side = horizontal_surface_down_scattering(ph, cf.side_wall_roughness, is_diffuse=True)
|
|
34
30
|
|
|
35
31
|
if cf.hot_side_position_right and x > cf.width / 2:
|
|
36
|
-
scattering_types.hot_side = vertical_surface_left_scattering(
|
|
37
|
-
ph, cf.side_wall_roughness, cf, is_diffuse=True
|
|
38
|
-
)
|
|
32
|
+
scattering_types.hot_side = vertical_surface_left_scattering(ph, cf.side_wall_roughness, cf, is_diffuse=True)
|
|
39
33
|
|
|
40
34
|
if cf.hot_side_position_left and x < -cf.width / 2:
|
|
41
|
-
scattering_types.hot_side = vertical_surface_right_scattering(
|
|
42
|
-
ph, cf.side_wall_roughness, cf, is_diffuse=True
|
|
43
|
-
)
|
|
35
|
+
scattering_types.hot_side = vertical_surface_right_scattering(ph, cf.side_wall_roughness, cf, is_diffuse=True)
|
|
44
36
|
|
|
45
37
|
|
|
46
38
|
def scattering_on_right_sidewall(ph, scattering_types, x, y, z):
|
|
47
39
|
"""Scatter phonon if it reached right side wall"""
|
|
48
40
|
if x > cf.width / 2:
|
|
49
|
-
scattering_types.walls = vertical_surface_left_scattering(
|
|
50
|
-
ph, cf.side_wall_roughness, cf
|
|
51
|
-
)
|
|
41
|
+
scattering_types.walls = vertical_surface_left_scattering(ph, cf.side_wall_roughness, cf)
|
|
52
42
|
|
|
53
43
|
|
|
54
44
|
def scattering_on_left_sidewall(ph, scattering_types, x, y, z):
|
|
55
45
|
"""Scatter phonon if it reached left side wall"""
|
|
56
46
|
if x < -cf.width / 2:
|
|
57
|
-
scattering_types.walls = vertical_surface_right_scattering(
|
|
58
|
-
ph, cf.side_wall_roughness, cf
|
|
59
|
-
)
|
|
47
|
+
scattering_types.walls = vertical_surface_right_scattering(ph, cf.side_wall_roughness, cf)
|
|
60
48
|
|
|
61
49
|
|
|
62
50
|
def scattering_on_top_sidewall(ph, scattering_types, x, y, z):
|
|
63
51
|
"""Check if the phonon hits top side wall and output new vector"""
|
|
64
52
|
if y > cf.length:
|
|
65
|
-
scattering_types.walls = horizontal_surface_down_scattering(
|
|
66
|
-
ph, cf.side_wall_roughness
|
|
67
|
-
)
|
|
53
|
+
scattering_types.walls = horizontal_surface_down_scattering(ph, cf.side_wall_roughness)
|
|
68
54
|
|
|
69
55
|
|
|
70
56
|
def scattering_on_bottom_sidewall(ph, scattering_types, x, y, z):
|
|
71
57
|
"""Check if the phonon hits bottom side wall and output new vector"""
|
|
72
58
|
if y < 0.0:
|
|
73
|
-
scattering_types.walls = horizontal_surface_up_scattering(
|
|
74
|
-
ph, cf.side_wall_roughness
|
|
75
|
-
)
|
|
59
|
+
scattering_types.walls = horizontal_surface_up_scattering(ph, cf.side_wall_roughness)
|
|
76
60
|
|
|
77
61
|
|
|
78
62
|
def floor_scattering(ph, scattering_types, x, y, z):
|
|
@@ -93,15 +77,11 @@ def ceiling_scattering(ph, scattering_types, x, y, z):
|
|
|
93
77
|
is_under_pillar = distance_from_pillar_center < pillar.diameter / 2
|
|
94
78
|
if is_under_pillar:
|
|
95
79
|
if z > pillar.height + cf.thickness / 2:
|
|
96
|
-
scattering_types.top_bottom = in_plane_surface_scattering(
|
|
97
|
-
ph, cf.top_roughness
|
|
98
|
-
)
|
|
80
|
+
scattering_types.top_bottom = in_plane_surface_scattering(ph, cf.top_roughness)
|
|
99
81
|
return
|
|
100
82
|
# Regular scattering if phonon is not under any of the pillars:
|
|
101
83
|
if ph.z < cf.thickness / 2:
|
|
102
|
-
scattering_types.top_bottom = in_plane_surface_scattering(
|
|
103
|
-
ph, cf.top_roughness
|
|
104
|
-
)
|
|
84
|
+
scattering_types.top_bottom = in_plane_surface_scattering(ph, cf.top_roughness)
|
|
105
85
|
|
|
106
86
|
else:
|
|
107
87
|
scattering_types.top_bottom = in_plane_surface_scattering(ph, cf.top_roughness)
|
|
@@ -147,5 +127,11 @@ def surface_scattering(ph, scattering_types, triangle_scattering_places):
|
|
|
147
127
|
if scattering_types.pillars is not None:
|
|
148
128
|
break
|
|
149
129
|
|
|
130
|
+
# Scattering on interfaces:
|
|
131
|
+
if cf.interfaces:
|
|
132
|
+
for interface in cf.interfaces:
|
|
133
|
+
if interface.is_crossed(ph, x, y, z) and interface.is_transmitted():
|
|
134
|
+
interface.scatter(ph, scattering_types, x, y, z, cf)
|
|
135
|
+
|
|
150
136
|
# Correct angle if it became more than 180 degrees:
|
|
151
137
|
ph.correct_angle()
|
|
@@ -24,6 +24,7 @@ class ScatteringTypes:
|
|
|
24
24
|
self.walls = None
|
|
25
25
|
self.internal = None
|
|
26
26
|
self.hot_side = None
|
|
27
|
+
self.interfaces = None
|
|
27
28
|
|
|
28
29
|
@property
|
|
29
30
|
def is_diffuse(self):
|
|
@@ -32,7 +33,8 @@ class ScatteringTypes:
|
|
|
32
33
|
self.pillars == Scattering.DIFFUSE,
|
|
33
34
|
self.top_bottom == Scattering.DIFFUSE,
|
|
34
35
|
self.walls == Scattering.DIFFUSE,
|
|
35
|
-
self.hot_side == Scattering.DIFFUSE
|
|
36
|
+
self.hot_side == Scattering.DIFFUSE,
|
|
37
|
+
self.interfaces == Scattering.DIFFUSE])
|
|
36
38
|
|
|
37
39
|
@property
|
|
38
40
|
def is_internal(self):
|
|
@@ -57,7 +59,8 @@ class ScatteringTypes:
|
|
|
57
59
|
self.top_bottom,
|
|
58
60
|
self.walls,
|
|
59
61
|
self.internal,
|
|
60
|
-
self.hot_side
|
|
62
|
+
self.hot_side,
|
|
63
|
+
self.interfaces])
|
|
61
64
|
|
|
62
65
|
def reset(self):
|
|
63
66
|
"""Reset all scattering types to None"""
|
|
@@ -67,6 +70,7 @@ class ScatteringTypes:
|
|
|
67
70
|
self.walls = None
|
|
68
71
|
self.internal = None
|
|
69
72
|
self.hot_side = None
|
|
73
|
+
self.interfaces = None
|
|
70
74
|
|
|
71
75
|
|
|
72
76
|
class ScatteringPlaces:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: freepaths
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1
|
|
4
4
|
Summary: Phonon Monte Carlo simulator
|
|
5
5
|
Home-page: https://github.com/anufrievroman/freepaths
|
|
6
6
|
Author: Roman Anufriev
|
|
@@ -11,18 +11,28 @@ Classifier: License :: OSI Approved :: GNU General Public License (GPL)
|
|
|
11
11
|
Classifier: Environment :: Console
|
|
12
12
|
Classifier: Natural Language :: English
|
|
13
13
|
Classifier: Operating System :: OS Independent
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
16
14
|
Classifier: Programming Language :: Python :: 3.9
|
|
17
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Classifier: Topic :: Utilities
|
|
19
|
-
Requires-Python: ~=3.
|
|
19
|
+
Requires-Python: ~=3.11
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
Requires-Dist: numpy
|
|
22
22
|
Requires-Dist: matplotlib
|
|
23
23
|
Requires-Dist: scipy
|
|
24
24
|
Requires-Dist: imageio
|
|
25
25
|
Requires-Dist: colorama
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: author-email
|
|
28
|
+
Dynamic: classifier
|
|
29
|
+
Dynamic: description
|
|
30
|
+
Dynamic: description-content-type
|
|
31
|
+
Dynamic: home-page
|
|
32
|
+
Dynamic: license
|
|
33
|
+
Dynamic: requires-dist
|
|
34
|
+
Dynamic: requires-python
|
|
35
|
+
Dynamic: summary
|
|
26
36
|
|
|
27
37
|
# FreePATHS - Free Phonon And Thermal Simulator
|
|
28
38
|
|
|
@@ -7,7 +7,6 @@ freepaths/config.py
|
|
|
7
7
|
freepaths/data.py
|
|
8
8
|
freepaths/default_config.py
|
|
9
9
|
freepaths/flight.py
|
|
10
|
-
freepaths/holes.py
|
|
11
10
|
freepaths/main_mfp_sampling.py
|
|
12
11
|
freepaths/main_tracing.py
|
|
13
12
|
freepaths/maps.py
|
|
@@ -19,6 +18,7 @@ freepaths/output_structure.py
|
|
|
19
18
|
freepaths/phonon.py
|
|
20
19
|
freepaths/progress.py
|
|
21
20
|
freepaths/run_phonon.py
|
|
21
|
+
freepaths/scatterers.py
|
|
22
22
|
freepaths/scattering.py
|
|
23
23
|
freepaths/scattering_primitives.py
|
|
24
24
|
freepaths/scattering_semicircle.py
|
|
@@ -25,17 +25,17 @@ setuptools.setup(
|
|
|
25
25
|
},
|
|
26
26
|
install_requires=['numpy', 'matplotlib', 'scipy', 'imageio', 'colorama'],
|
|
27
27
|
version=version,
|
|
28
|
-
python_requires='~=3.
|
|
28
|
+
python_requires='~=3.11',
|
|
29
29
|
classifiers=[
|
|
30
30
|
"Development Status :: 4 - Beta",
|
|
31
31
|
"License :: OSI Approved :: GNU General Public License (GPL)",
|
|
32
32
|
"Environment :: Console",
|
|
33
33
|
"Natural Language :: English",
|
|
34
34
|
"Operating System :: OS Independent",
|
|
35
|
-
"Programming Language :: Python :: 3.7",
|
|
36
|
-
"Programming Language :: Python :: 3.8",
|
|
37
35
|
"Programming Language :: Python :: 3.9",
|
|
38
36
|
"Programming Language :: Python :: 3.10",
|
|
37
|
+
"Programming Language :: Python :: 3.11",
|
|
38
|
+
"Programming Language :: Python :: 3.12",
|
|
39
39
|
"Topic :: Utilities",
|
|
40
40
|
],
|
|
41
41
|
packages=["freepaths"],
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|