freepaths 2.4.1__tar.gz → 2.4.3__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 (41) hide show
  1. {freepaths-2.4.1 → freepaths-2.4.3}/PKG-INFO +1 -1
  2. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/__main__.py +1 -1
  3. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/data.py +17 -0
  4. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/flight.py +31 -0
  5. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/output_plots.py +83 -14
  6. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/run_particle.py +1 -0
  7. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths.egg-info/PKG-INFO +1 -1
  8. {freepaths-2.4.1 → freepaths-2.4.3}/README.md +0 -0
  9. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/animation.py +0 -0
  10. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/config.py +0 -0
  11. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/default_config.py +0 -0
  12. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/electron.py +0 -0
  13. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/interface_smmm.py +0 -0
  14. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/main_mfp_sampling.py +0 -0
  15. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/main_tracing.py +0 -0
  16. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/maps.py +0 -0
  17. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/materials.py +0 -0
  18. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/move.py +0 -0
  19. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/options.py +0 -0
  20. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/output_electron_plots.py +0 -0
  21. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/output_info.py +0 -0
  22. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/output_structure.py +0 -0
  23. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/particle.py +0 -0
  24. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/phonon.py +0 -0
  25. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/post_computations.py +0 -0
  26. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/progress.py +0 -0
  27. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/scatterers.py +0 -0
  28. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/scattering.py +0 -0
  29. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/scattering_interfaces.py +0 -0
  30. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/scattering_primitives.py +0 -0
  31. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/scattering_semicircle.py +0 -0
  32. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/scattering_types.py +0 -0
  33. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths/sources.py +0 -0
  34. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths.egg-info/SOURCES.txt +0 -0
  35. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths.egg-info/dependency_links.txt +0 -0
  36. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths.egg-info/entry_points.txt +0 -0
  37. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths.egg-info/requires.txt +0 -0
  38. {freepaths-2.4.1 → freepaths-2.4.3}/freepaths.egg-info/top_level.txt +0 -0
  39. {freepaths-2.4.1 → freepaths-2.4.3}/pyproject.toml +0 -0
  40. {freepaths-2.4.1 → freepaths-2.4.3}/setup.cfg +0 -0
  41. {freepaths-2.4.1 → freepaths-2.4.3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freepaths
3
- Version: 2.4.1
3
+ Version: 2.4.3
4
4
  Summary: Phonon Monte Carlo simulator
5
5
  Home-page: https://github.com/anufrievroman/freepaths
6
6
  Author: Roman Anufriev
@@ -7,7 +7,7 @@ from colorama import Fore, Style
7
7
 
8
8
  from freepaths.options import SimulationMode
9
9
 
10
- __version__ = "2.4.1"
10
+ __version__ = "2.4.3"
11
11
 
12
12
  colorama.init()
13
13
 
@@ -70,6 +70,14 @@ class GeneralData(Data):
70
70
  self.travel_times = []
71
71
  self.mean_free_paths = []
72
72
  self.thermal_conductivity = []
73
+ # Per-phonon internal and boundary scattering rates (MFP-sampling mode only),
74
+ # the two channels of the total rate v/MFP resolved as a function of frequency:
75
+ self.internal_scattering_rates = []
76
+ self.boundary_scattering_rates = []
77
+ # Per-phonon dispersion branch index (0=LA, 1=TA1, 2=TA2, ...). In MFP-sampling
78
+ # mode each phonon keeps its launch branch (no rethermalization), so this lets the
79
+ # scattering-rate spectra be resolved per branch. Row-aligned with the arrays above.
80
+ self.branches = []
73
81
  def save_particle_data(self, pt, mode=None):
74
82
  """Add information about the particle to the dataset"""
75
83
  from freepaths.options import SimulationMode
@@ -78,6 +86,7 @@ class GeneralData(Data):
78
86
  else:
79
87
  self.final_frequencies.append(pt.f)
80
88
  self.group_velocities.append(pt.speed)
89
+ self.branches.append(pt.branch_number)
81
90
 
82
91
  def save_flight_data(self, flight, mode=None):
83
92
  """Add information about the particle flight to the dataset"""
@@ -95,6 +104,8 @@ class GeneralData(Data):
95
104
  self.mean_free_paths.append(flight.mean_free_path)
96
105
  if mode is SimulationMode.PHONON_MFP_SAMPLING:
97
106
  self.thermal_conductivity.append(flight.thermal_conductivity)
107
+ self.internal_scattering_rates.append(flight.internal_scattering_rate)
108
+ self.boundary_scattering_rates.append(flight.boundary_scattering_rate)
98
109
 
99
110
  def write_into_files(self, mode=None):
100
111
  import os
@@ -121,6 +132,9 @@ class GeneralData(Data):
121
132
  np.savetxt("Data/All mean free paths.csv", self.mean_free_paths, fmt='%2.4e', header="MFPs [m]", encoding='utf-8')
122
133
  if mode is SimulationMode.PHONON_MFP_SAMPLING:
123
134
  np.savetxt("Data/All thermal conductivities.csv", self.thermal_conductivity, fmt='%2.4e', header="K [W/mK]", encoding='utf-8')
135
+ np.savetxt("Data/All internal scattering rates.csv", self.internal_scattering_rates, fmt='%2.4e', header="1/tau_internal [1/s]", encoding='utf-8')
136
+ np.savetxt("Data/All boundary scattering rates.csv", self.boundary_scattering_rates, fmt='%2.4e', header="1/tau_boundary [1/s]", encoding='utf-8')
137
+ np.savetxt("Data/All branches.csv", self.branches, fmt='%d', header="Branch index (0=LA, 1=TA1, 2=TA2)", encoding='utf-8')
124
138
  def dump_data(self):
125
139
  """Return data of a process in the form of a dictionary to be attached to the global data"""
126
140
  return {
@@ -136,6 +150,9 @@ class GeneralData(Data):
136
150
  'travel_times': self.travel_times,
137
151
  'mean_free_paths': self.mean_free_paths,
138
152
  'thermal_conductivity': self.thermal_conductivity,
153
+ 'internal_scattering_rates': self.internal_scattering_rates,
154
+ 'boundary_scattering_rates': self.boundary_scattering_rates,
155
+ 'branches': self.branches,
139
156
  'initial_energies': self.initial_energies,
140
157
  }
141
158
 
@@ -45,6 +45,11 @@ class Flight:
45
45
  self.free_paths = []
46
46
  self._mfp_sum = 0.0
47
47
  self._mfp_count = 0
48
+ # Per-channel counts of the diffuse events that terminate a free-path segment,
49
+ # used to split the total scattering rate v/MFP into internal (phonon-phonon +
50
+ # impurity) and boundary (diffuse surface) spectra in the MFP-sampling mode:
51
+ self._internal_count = 0
52
+ self._boundary_count = 0
48
53
  self.hole_diff_scattering_angles = []
49
54
  self.hole_spec_scattering_angles = []
50
55
  self.thermal_conductivity = 0.0
@@ -53,6 +58,24 @@ class Flight:
53
58
  """Mean value of all free flights"""
54
59
  return self._mfp_sum / self._mfp_count if self._mfp_count else 0
55
60
 
61
+ @property
62
+ def internal_scattering_rate(self):
63
+ """Per-phonon internal (phonon-phonon + impurity) scattering rate [1/s]:
64
+ number of internal events divided by the total flight time (mfp_sum / speed).
65
+ Summed with boundary_scattering_rate this recovers the total rate v/MFP, so the
66
+ two split the total scattering-rate spectrum into physical channels."""
67
+ if self._mfp_sum <= 0:
68
+ return 0.0
69
+ return self.particle.speed * self._internal_count / self._mfp_sum
70
+
71
+ @property
72
+ def boundary_scattering_rate(self):
73
+ """Per-phonon boundary (diffuse surface) scattering rate [1/s]: number of
74
+ diffuse boundary events divided by the total flight time (mfp_sum / speed)."""
75
+ if self._mfp_sum <= 0:
76
+ return 0.0
77
+ return self.particle.speed * self._boundary_count / self._mfp_sum
78
+
56
79
  @property
57
80
  def mean_free_path_sem(self):
58
81
  """Standard error of the mean free path (MFP sampling mode only). Assumes
@@ -74,6 +97,14 @@ class Flight:
74
97
  if not cf.low_memory_usage:
75
98
  self.free_paths.append(self.free_path)
76
99
 
100
+ def record_scattering_channel(self, is_internal, is_boundary):
101
+ """Tally the channel of a diffuse event that ends a free-path segment, so the
102
+ total scattering rate can later be split into internal and boundary spectra."""
103
+ if is_internal:
104
+ self._internal_count += 1
105
+ if is_boundary:
106
+ self._boundary_count += 1
107
+
77
108
  def save_hole_diff_scattering_angle(self, angle):
78
109
  """Save angle of diffuse scattering from the hole"""
79
110
  self.hole_diff_scattering_angles.append(angle)
@@ -266,37 +266,106 @@ def plot_velocity_distribution():
266
266
 
267
267
 
268
268
  def plot_scattering_rate_vs_frequency():
269
- """Plot phonon scattering rate (1/τ = v/MFP) vs frequency with theoretical internal rate"""
269
+ """Plot phonon scattering rate (1/τ = v/MFP) vs frequency, resolved per dispersion
270
+ branch (color + marker shape: LA / TA1 / TA2) and per channel (marker fill:
271
+ filled = Total, open = Boundary, small dot = Internal), with the theoretical
272
+ internal rate overlaid. MFP-sampling mode."""
273
+ from matplotlib.lines import Line2D
274
+
270
275
  mfps = np.loadtxt("Data/All mean free paths.csv")
271
276
  frequencies = np.loadtxt("Data/All final frequencies.csv")
272
277
  speeds = np.loadtxt("Data/All group velocities.csv")
273
278
  mask = mfps > 0
274
279
  scattering_rates = speeds[mask] / mfps[mask]
280
+ f_masked = frequencies[mask]
281
+
282
+ # Per-channel MC rates and per-phonon branch index written by GeneralData in
283
+ # MFP-sampling mode (internal + boundary ≈ total). Optional so the plot still
284
+ # works on older result folders:
285
+ internal_rates = boundary_rates = branches = None
286
+ try:
287
+ internal_rates = np.loadtxt("Data/All internal scattering rates.csv")
288
+ boundary_rates = np.loadtxt("Data/All boundary scattering rates.csv")
289
+ except OSError:
290
+ pass
291
+ try:
292
+ branches = np.atleast_1d(np.loadtxt("Data/All branches.csv")).astype(int)
293
+ except OSError:
294
+ pass
275
295
 
276
296
  # Theoretical internal scattering rate from material model:
277
297
  material = get_media_class(cf.media)(cf.temp)
278
- f_range = np.linspace(frequencies[mask].min(), frequencies[mask].max(), 500)
279
- omega_range = 2 * np.pi * f_range
280
- tau_internal = np.array([material.phonon_relaxation_time(w) for w in omega_range])
281
-
298
+ f_range = np.linspace(f_masked.min(), f_masked.max(), 500)
299
+ tau_internal = np.array([material.phonon_relaxation_time(w) for w in 2 * np.pi * f_range])
282
300
  step_rate_ns = 1e-9 / cf.timestep # 1/TIMESTEP in ns⁻¹
283
301
 
302
+ branch_names = material.dispersion_branch_names
303
+ branch_colors = ['royalblue', '#5ec962', 'crimson', 'darkorange'] # one per branch
304
+ # Channel -> marker shape (+ a slightly different alpha to help separate overlaps):
305
+ channel_names = ['Total', 'Boundary', 'Internal']
306
+ channel_markers = ['o', 's', '^']
307
+ channel_alphas = [0.9, 0.6, 0.5]
308
+
284
309
  fig, ax = plt.subplots()
285
- ax.plot(frequencies[mask] * 1e-12, scattering_rates * 1e-9, '.', markersize=2, c='royalblue', label='MC total')
286
- ax.plot(f_range * 1e-12, 1e-9 / tau_internal, '--', linewidth=0.8, c='deeppink', label='Internal (theory)')
310
+ have_channels = internal_rates is not None and boundary_rates is not None
311
+
312
+ if branches is not None and have_channels:
313
+ # Branch-resolved: branch -> color, channel -> marker shape + alpha.
314
+ br = branches[mask]
315
+ intr, bnd = internal_rates[mask], boundary_rates[mask]
316
+ present = [b for b in range(len(branch_names)) if np.any(br == b)]
317
+ for b in present:
318
+ sel = br == b
319
+ color = branch_colors[b % 4]
320
+ fx = f_masked[sel] * 1e-12
321
+ channel_data = [scattering_rates[sel], bnd[sel], intr[sel]]
322
+ for cdata, marker, alpha in zip(channel_data, channel_markers, channel_alphas):
323
+ ax.scatter(fx, cdata * 1e-9, s=8, marker=marker, color=color,
324
+ alpha=alpha, linewidths=0)
325
+
326
+ # Two proxy legends: branch (color), channel (marker shape):
327
+ branch_handles = [Line2D([], [], linestyle='None', marker='o', markersize=4,
328
+ markerfacecolor=branch_colors[b % 4],
329
+ markeredgecolor=branch_colors[b % 4], label=branch_names[b]) for b in present]
330
+ branch_handles.append(Line2D([], [], color='gray', linestyle='--', label='Internal (Theory)'))
331
+ channel_handles = [Line2D([], [], linestyle='None', marker=m, markersize=4,
332
+ markerfacecolor='gray', markeredgecolor='gray', label=n)
333
+ for m, n in zip(channel_markers, channel_names)]
334
+ # Two horizontal legends side by side above the axes (branch on the left,
335
+ # channel on the right), clear of the data:
336
+ leg1 = ax.legend(handles=branch_handles, loc='lower left', bbox_to_anchor=(0, 1.02),
337
+ ncol=len(branch_handles), fontsize=5.5, title='Branch', title_fontsize=6,
338
+ columnspacing=0.8, handletextpad=0.3)
339
+ ax.add_artist(leg1)
340
+ ax.legend(handles=channel_handles, loc='lower right', bbox_to_anchor=(1, 1.02),
341
+ ncol=len(channel_handles), fontsize=5.5, title='Channel', title_fontsize=6,
342
+ columnspacing=0.8, handletextpad=0.3)
343
+
344
+ # Consolidated CSV in long format, sorted by (branch, frequency):
345
+ order = np.lexsort((f_masked, br))
346
+ legend = ", ".join(f"{i}={name}" for i, name in enumerate(branch_names))
347
+ spectra = np.column_stack((br, f_masked, scattering_rates, intr, bnd))[order]
348
+ np.savetxt("Data/Distribution of scattering rates.csv", spectra,
349
+ fmt=['%d', '%2.4e', '%2.4e', '%2.4e', '%2.4e'], delimiter=',',
350
+ header=f"branch ({legend}), f [Hz], Total (MC) [1/s], "
351
+ "Internal (MC) [1/s], Boundary (MC) [1/s]", encoding='utf-8')
352
+ else:
353
+ # Fallback for older result folders without branch/channel data: total only.
354
+ ax.plot(f_masked * 1e-12, scattering_rates * 1e-9, '.', markersize=2, c='black', label='Total (MC)')
355
+ ax.legend(fontsize=7)
356
+ order = np.argsort(f_masked)
357
+ np.savetxt("Data/Distribution of scattering rates.csv",
358
+ np.column_stack((f_masked, scattering_rates))[order],
359
+ fmt='%2.4e', delimiter=',', header="f [Hz], Total (MC) [1/s]", encoding='utf-8')
360
+
361
+ ax.plot(f_range * 1e-12, 1e-9 / tau_internal, '--', linewidth=0.8, c='gray')
287
362
  ax.set_xlabel('Frequency (THz)')
288
363
  ax.set_ylabel(r'Scattering rate (ns$^{-1}$)')
289
- ax.legend()
290
364
  ax.text(0.98, 0.04, f'Step rate = {step_rate_ns:.1f} ns$^{{-1}}$',
291
- transform=ax.transAxes, ha='right', va='bottom',
292
- fontsize=7, color='gray')
365
+ transform=ax.transAxes, ha='right', va='bottom', fontsize=7, color='gray')
293
366
  fig.savefig('Distribution of phonon scattering rates.pdf', format='pdf', bbox_inches="tight")
294
367
  plt.close(fig)
295
368
 
296
- mc_data = np.vstack((frequencies[mask] * 1e-12, scattering_rates * 1e-9)).T
297
- np.savetxt('Data/Distribution of phonon scattering rates.csv', mc_data, fmt='%1.3e', delimiter=",",
298
- header="Frequency (THz),MC total scattering rate (1/ns)")
299
-
300
369
 
301
370
  def plot_energy_distribution():
302
371
  """Plot distribution of energy"""
@@ -56,6 +56,7 @@ def run_particle(particle, flight, scatter_stats, places_stats, segment_stats, t
56
56
 
57
57
  # If diffuse scattering has occurred, reset particle free path:
58
58
  if scattering_types.is_diffuse or scattering_types.is_internal:
59
+ flight.record_scattering_channel(scattering_types.is_internal, scattering_types.is_diffuse)
59
60
  flight.save_free_paths()
60
61
  flight.restart()
61
62
  # An inelastic (anharmonic) internal event rethermalizes the phonon: new
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freepaths
3
- Version: 2.4.1
3
+ Version: 2.4.3
4
4
  Summary: Phonon Monte Carlo simulator
5
5
  Home-page: https://github.com/anufrievroman/freepaths
6
6
  Author: Roman Anufriev
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes