freepaths 2.4.2__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.2 → freepaths-2.4.3}/PKG-INFO +1 -1
  2. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/__main__.py +1 -1
  3. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/data.py +7 -0
  4. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/output_plots.py +76 -32
  5. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths.egg-info/PKG-INFO +1 -1
  6. {freepaths-2.4.2 → freepaths-2.4.3}/README.md +0 -0
  7. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/animation.py +0 -0
  8. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/config.py +0 -0
  9. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/default_config.py +0 -0
  10. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/electron.py +0 -0
  11. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/flight.py +0 -0
  12. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/interface_smmm.py +0 -0
  13. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/main_mfp_sampling.py +0 -0
  14. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/main_tracing.py +0 -0
  15. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/maps.py +0 -0
  16. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/materials.py +0 -0
  17. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/move.py +0 -0
  18. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/options.py +0 -0
  19. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/output_electron_plots.py +0 -0
  20. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/output_info.py +0 -0
  21. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/output_structure.py +0 -0
  22. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/particle.py +0 -0
  23. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/phonon.py +0 -0
  24. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/post_computations.py +0 -0
  25. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/progress.py +0 -0
  26. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/run_particle.py +0 -0
  27. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/scatterers.py +0 -0
  28. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/scattering.py +0 -0
  29. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/scattering_interfaces.py +0 -0
  30. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/scattering_primitives.py +0 -0
  31. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/scattering_semicircle.py +0 -0
  32. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/scattering_types.py +0 -0
  33. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths/sources.py +0 -0
  34. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths.egg-info/SOURCES.txt +0 -0
  35. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths.egg-info/dependency_links.txt +0 -0
  36. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths.egg-info/entry_points.txt +0 -0
  37. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths.egg-info/requires.txt +0 -0
  38. {freepaths-2.4.2 → freepaths-2.4.3}/freepaths.egg-info/top_level.txt +0 -0
  39. {freepaths-2.4.2 → freepaths-2.4.3}/pyproject.toml +0 -0
  40. {freepaths-2.4.2 → freepaths-2.4.3}/setup.cfg +0 -0
  41. {freepaths-2.4.2 → 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.2
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.2"
10
+ __version__ = "2.4.3"
11
11
 
12
12
  colorama.init()
13
13
 
@@ -74,6 +74,10 @@ class GeneralData(Data):
74
74
  # the two channels of the total rate v/MFP resolved as a function of frequency:
75
75
  self.internal_scattering_rates = []
76
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 = []
77
81
  def save_particle_data(self, pt, mode=None):
78
82
  """Add information about the particle to the dataset"""
79
83
  from freepaths.options import SimulationMode
@@ -82,6 +86,7 @@ class GeneralData(Data):
82
86
  else:
83
87
  self.final_frequencies.append(pt.f)
84
88
  self.group_velocities.append(pt.speed)
89
+ self.branches.append(pt.branch_number)
85
90
 
86
91
  def save_flight_data(self, flight, mode=None):
87
92
  """Add information about the particle flight to the dataset"""
@@ -129,6 +134,7 @@ class GeneralData(Data):
129
134
  np.savetxt("Data/All thermal conductivities.csv", self.thermal_conductivity, fmt='%2.4e', header="K [W/mK]", encoding='utf-8')
130
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')
131
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')
132
138
  def dump_data(self):
133
139
  """Return data of a process in the form of a dictionary to be attached to the global data"""
134
140
  return {
@@ -146,6 +152,7 @@ class GeneralData(Data):
146
152
  'thermal_conductivity': self.thermal_conductivity,
147
153
  'internal_scattering_rates': self.internal_scattering_rates,
148
154
  'boundary_scattering_rates': self.boundary_scattering_rates,
155
+ 'branches': self.branches,
149
156
  'initial_energies': self.initial_energies,
150
157
  }
151
158
 
@@ -266,62 +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, resolved into the
270
- internal (phonon-phonon + impurity) and boundary (diffuse surface) channels
271
- measured in the simulation, with the theoretical internal rate overlaid."""
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
+
272
275
  mfps = np.loadtxt("Data/All mean free paths.csv")
273
276
  frequencies = np.loadtxt("Data/All final frequencies.csv")
274
277
  speeds = np.loadtxt("Data/All group velocities.csv")
275
278
  mask = mfps > 0
276
279
  scattering_rates = speeds[mask] / mfps[mask]
280
+ f_masked = frequencies[mask]
277
281
 
278
- # Per-channel MC rates written by GeneralData in MFP-sampling mode (internal +
279
- # boundary ≈ total). Optional so the plot still works on older result folders:
280
- internal_rates = boundary_rates = None
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
281
286
  try:
282
287
  internal_rates = np.loadtxt("Data/All internal scattering rates.csv")
283
288
  boundary_rates = np.loadtxt("Data/All boundary scattering rates.csv")
284
289
  except OSError:
285
290
  pass
291
+ try:
292
+ branches = np.atleast_1d(np.loadtxt("Data/All branches.csv")).astype(int)
293
+ except OSError:
294
+ pass
286
295
 
287
296
  # Theoretical internal scattering rate from material model:
288
297
  material = get_media_class(cf.media)(cf.temp)
289
- f_range = np.linspace(frequencies[mask].min(), frequencies[mask].max(), 500)
290
- omega_range = 2 * np.pi * f_range
291
- tau_internal = np.array([material.phonon_relaxation_time(w) for w in omega_range])
292
-
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])
293
300
  step_rate_ns = 1e-9 / cf.timestep # 1/TIMESTEP in ns⁻¹
294
301
 
295
- # Save the consolidated per-phonon spectra (frequency-sorted) to a single CSV so
296
- # the Total/Internal/Boundary channels can be read without joining separate files:
297
- if internal_rates is not None and boundary_rates is not None:
298
- order = np.argsort(frequencies[mask])
299
- spectra = np.column_stack((frequencies[mask], scattering_rates,
300
- internal_rates[mask], boundary_rates[mask]))[order]
301
- np.savetxt("Data/Distribution of scattering rates.csv", spectra,
302
- fmt='%2.4e', delimiter=',',
303
- header="f [Hz], Total (MC) [1/s], Internal (MC) [1/s], Boundary (MC) [1/s]",
304
- encoding='utf-8')
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]
305
308
 
306
309
  fig, ax = plt.subplots()
307
- ax.plot(frequencies[mask] * 1e-12, scattering_rates * 1e-9, '.', markersize=2, c='black', label='Total (MC)')
308
- if internal_rates is not None and boundary_rates is not None:
309
- ax.plot(frequencies[mask] * 1e-12, internal_rates[mask] * 1e-9, '.', markersize=2, c='#5ec962', label='Internal (MC)')
310
- ax.plot(frequencies[mask] * 1e-12, boundary_rates[mask] * 1e-9, '.', markersize=2, c='royalblue', label='Boundary (MC)')
311
- ax.plot(f_range * 1e-12, 1e-9 / tau_internal, '--', linewidth=0.8, c='gray', 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')
312
362
  ax.set_xlabel('Frequency (THz)')
313
363
  ax.set_ylabel(r'Scattering rate (ns$^{-1}$)')
314
- ax.legend()
315
364
  ax.text(0.98, 0.04, f'Step rate = {step_rate_ns:.1f} ns$^{{-1}}$',
316
- transform=ax.transAxes, ha='right', va='bottom',
317
- fontsize=7, color='gray')
365
+ transform=ax.transAxes, ha='right', va='bottom', fontsize=7, color='gray')
318
366
  fig.savefig('Distribution of phonon scattering rates.pdf', format='pdf', bbox_inches="tight")
319
367
  plt.close(fig)
320
368
 
321
- mc_data = np.vstack((frequencies[mask] * 1e-12, scattering_rates * 1e-9)).T
322
- np.savetxt('Data/Distribution of phonon scattering rates.csv', mc_data, fmt='%1.3e', delimiter=",",
323
- header="Frequency (THz),MC total scattering rate (1/ns)")
324
-
325
369
 
326
370
  def plot_energy_distribution():
327
371
  """Plot distribution of energy"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: freepaths
3
- Version: 2.4.2
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
File without changes