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.
Files changed (34) hide show
  1. {freepaths-2.0 → freepaths-2.1}/PKG-INFO +15 -5
  2. {freepaths-2.0 → freepaths-2.1}/freepaths/__main__.py +1 -1
  3. {freepaths-2.0 → freepaths-2.1}/freepaths/animation.py +2 -2
  4. {freepaths-2.0 → freepaths-2.1}/freepaths/config.py +3 -1
  5. {freepaths-2.0 → freepaths-2.1}/freepaths/data.py +8 -0
  6. {freepaths-2.0 → freepaths-2.1}/freepaths/default_config.py +2 -0
  7. {freepaths-2.0 → freepaths-2.1}/freepaths/main_mfp_sampling.py +3 -2
  8. {freepaths-2.0 → freepaths-2.1}/freepaths/main_tracing.py +1 -1
  9. {freepaths-2.0 → freepaths-2.1}/freepaths/output_info.py +46 -36
  10. {freepaths-2.0 → freepaths-2.1}/freepaths/output_plots.py +10 -4
  11. {freepaths-2.0 → freepaths-2.1}/freepaths/output_structure.py +30 -1
  12. freepaths-2.0/freepaths/holes.py → freepaths-2.1/freepaths/scatterers.py +117 -7
  13. {freepaths-2.0 → freepaths-2.1}/freepaths/scattering.py +16 -30
  14. {freepaths-2.0 → freepaths-2.1}/freepaths/scattering_types.py +6 -2
  15. {freepaths-2.0 → freepaths-2.1}/freepaths.egg-info/PKG-INFO +15 -5
  16. {freepaths-2.0 → freepaths-2.1}/freepaths.egg-info/SOURCES.txt +1 -1
  17. {freepaths-2.0 → freepaths-2.1}/setup.py +3 -3
  18. {freepaths-2.0 → freepaths-2.1}/README.md +0 -0
  19. {freepaths-2.0 → freepaths-2.1}/freepaths/flight.py +0 -0
  20. {freepaths-2.0 → freepaths-2.1}/freepaths/maps.py +0 -0
  21. {freepaths-2.0 → freepaths-2.1}/freepaths/materials.py +0 -0
  22. {freepaths-2.0 → freepaths-2.1}/freepaths/move.py +0 -0
  23. {freepaths-2.0 → freepaths-2.1}/freepaths/phonon.py +0 -0
  24. {freepaths-2.0 → freepaths-2.1}/freepaths/progress.py +0 -0
  25. {freepaths-2.0 → freepaths-2.1}/freepaths/run_phonon.py +0 -0
  26. {freepaths-2.0 → freepaths-2.1}/freepaths/scattering_primitives.py +0 -0
  27. {freepaths-2.0 → freepaths-2.1}/freepaths/scattering_semicircle.py +0 -0
  28. {freepaths-2.0 → freepaths-2.1}/freepaths/sources.py +0 -0
  29. {freepaths-2.0 → freepaths-2.1}/freepaths.egg-info/dependency_links.txt +0 -0
  30. {freepaths-2.0 → freepaths-2.1}/freepaths.egg-info/entry_points.txt +0 -0
  31. {freepaths-2.0 → freepaths-2.1}/freepaths.egg-info/requires.txt +0 -0
  32. {freepaths-2.0 → freepaths-2.1}/freepaths.egg-info/top_level.txt +0 -0
  33. {freepaths-2.0 → freepaths-2.1}/pyproject.toml +0 -0
  34. {freepaths-2.0 → freepaths-2.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: freepaths
3
- Version: 2.0
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.8
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,7 @@ from colorama import Fore, Style
7
7
  import freepaths.main_tracing
8
8
  import freepaths.main_mfp_sampling
9
9
 
10
- __version__ = "2.0"
10
+ __version__ = "2.1"
11
11
 
12
12
  colorama.init()
13
13
 
@@ -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 draw_structure
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 = draw_structure(cf, color_back=cf.output_structure_color)
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.holes import *
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' Workers finished: {finished_workers.value}/{cf.num_workers}'
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
- info1 = (
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
- info2 = (
84
- f'\n{sc_on_holes:.2f}% - scattering on hole walls ',
85
- f'({sc_on_holes_diff:.2f}% - diffuse, ',
86
- f'{sc_on_holes_spec:.2f}% - specular)',
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
- info3 = (
95
- f'\n{sc_on_pill:.2f}% - scattering on pillar walls ',
96
- f'({sc_on_pill_diff:.2f}% - diffuse, ',
97
- f'{sc_on_pill_spec:.2f}% - specular)'
98
- )
99
-
100
- # Write info into a text file:
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(info1)
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 draw_structure
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 structures:
388
- patches = draw_structure(cf, color_holes='white', color_back=cf.output_structure_color)
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 = draw_structure(cf, color_holes='black', color_back='royalblue')
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 draw_structure(cf, color_holes="white", color_back="gray"):
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
- return (abs(x - self.x0) <= self.size_x / 2) and (abs(y - self.y0) <= self.size_y / 2)
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
- # Coordinate y of the intersection with the hole side:
92
- y1 = (self.y0 - y) + cos(ph.theta) * (
93
- self.size_x / 2 - abs(self.x0 - x)
94
- ) / abs(sin(ph.theta))
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
1
+ Metadata-Version: 2.2
2
2
  Name: freepaths
3
- Version: 2.0
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.8
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.8',
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