freepaths 2.3.2__tar.gz → 2.3.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.
- {freepaths-2.3.2 → freepaths-2.3.3}/PKG-INFO +1 -1
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/__main__.py +1 -1
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/config.py +2 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/data.py +6 -29
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/default_config.py +4 -1
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/electron.py +2 -4
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/flight.py +0 -26
- freepaths-2.3.3/freepaths/main_mfp_sampling.py +195 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/main_tracing.py +4 -2
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/maps.py +2 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/materials.py +69 -2
- freepaths-2.3.3/freepaths/output_electron_plots.py +218 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/output_info.py +4 -2
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/output_plots.py +72 -524
- freepaths-2.3.3/freepaths/post_computations.py +190 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/run_particle.py +2 -1
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/scatterers.py +0 -16
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths.egg-info/PKG-INFO +1 -1
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths.egg-info/SOURCES.txt +1 -0
- freepaths-2.3.2/freepaths/main_mfp_sampling.py +0 -135
- freepaths-2.3.2/freepaths/post_computations.py +0 -177
- {freepaths-2.3.2 → freepaths-2.3.3}/README.md +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/animation.py +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/interface_smmm.py +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/move.py +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/output_structure.py +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/particle.py +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/particle_types.py +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/phonon.py +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/progress.py +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/scattering.py +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/scattering_interfaces.py +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/scattering_primitives.py +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/scattering_semicircle.py +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/scattering_types.py +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths/sources.py +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths.egg-info/dependency_links.txt +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths.egg-info/entry_points.txt +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths.egg-info/requires.txt +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/freepaths.egg-info/top_level.txt +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/pyproject.toml +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/setup.cfg +0 -0
- {freepaths-2.3.2 → freepaths-2.3.3}/setup.py +0 -0
|
@@ -69,6 +69,8 @@ class Config:
|
|
|
69
69
|
self.electron_mfp = ELECTRON_MFP
|
|
70
70
|
self.mean_mapping_constant = MEAN_MAPPING_CONSTANT
|
|
71
71
|
self.is_carrier_electron = IS_CARRIER_ELECTRON
|
|
72
|
+
self.fermi_level_lower_bound = FERMI_LEVEL_LOWER_BOUND
|
|
73
|
+
self.fermi_level_upper_bound = FERMI_LEVEL_UPPER_BOUND
|
|
72
74
|
|
|
73
75
|
# Animation:
|
|
74
76
|
self.output_path_animation = OUTPUT_PATH_ANIMATION
|
|
@@ -66,12 +66,6 @@ class GeneralData(Data):
|
|
|
66
66
|
self.travel_times = []
|
|
67
67
|
self.mean_free_paths = []
|
|
68
68
|
self.thermal_conductivity = []
|
|
69
|
-
self.interfaces_angles = []
|
|
70
|
-
self.interfaces_transmission_factor = []
|
|
71
|
-
self.interfaces_wavelength = []
|
|
72
|
-
self.interfaces_frequency = []
|
|
73
|
-
self.interfaces_mode = []
|
|
74
|
-
|
|
75
69
|
def save_particle_data(self, pt):
|
|
76
70
|
"""Add information about the particle to the dataset"""
|
|
77
71
|
self.frequencies.append(pt.f)
|
|
@@ -89,11 +83,6 @@ class GeneralData(Data):
|
|
|
89
83
|
self.travel_times.append(flight.travel_time)
|
|
90
84
|
self.mean_free_paths.append(flight.mean_free_path)
|
|
91
85
|
self.thermal_conductivity.append(flight.thermal_conductivity)
|
|
92
|
-
self.interfaces_angles.extend(flight.interfaces_angles)
|
|
93
|
-
self.interfaces_transmission_factor.extend(flight.interfaces_transmission_factor)
|
|
94
|
-
self.interfaces_wavelength.extend(flight.interfaces_wavelength)
|
|
95
|
-
self.interfaces_frequency.extend(flight.interfaces_frequency)
|
|
96
|
-
self.interfaces_mode.extend(flight.interfaces_mode)
|
|
97
86
|
|
|
98
87
|
|
|
99
88
|
def write_into_files(self):
|
|
@@ -105,20 +94,15 @@ class GeneralData(Data):
|
|
|
105
94
|
np.savetxt("Data/All free paths.csv", self.free_paths, fmt='%2.4e', header="L [m]", encoding='utf-8')
|
|
106
95
|
np.savetxt("Data/All initial frequencies.csv", self.frequencies, fmt='%2.4e', header="f [Hz]", encoding='utf-8')
|
|
107
96
|
np.savetxt("Data/All exit angles.csv", self.exit_angles, fmt='%.4f', header="Angle [rad]", encoding='utf-8')
|
|
108
|
-
|
|
109
|
-
|
|
97
|
+
if cf.holes:
|
|
98
|
+
np.savetxt("Data/All hole diffuse scattering angles.csv", self.hole_diff_scattering_angles, fmt='%.4f', header="Angle [rad]", encoding='utf-8')
|
|
99
|
+
np.savetxt("Data/All hole specular scattering angles.csv", self.hole_spec_scattering_angles, fmt='%.4f', header="Angle [rad]", encoding='utf-8')
|
|
110
100
|
np.savetxt("Data/All initial angles.csv", self.initial_angles, fmt='%.4f', header="Angle [rad]", encoding='utf-8')
|
|
111
101
|
np.savetxt("Data/All group velocities.csv", self.group_velocities, fmt='%.4f', header="Vg [m//s]", encoding='utf-8')
|
|
112
102
|
np.savetxt("Data/All travel times.csv", self.travel_times, fmt='%2.4e', header="Travel time [s]", encoding='utf-8')
|
|
113
103
|
np.savetxt("Data/All mean free paths.csv", self.mean_free_paths, fmt='%2.4e', header="MFPs [m]", encoding='utf-8')
|
|
114
104
|
np.savetxt("Data/All thermal conductivities.csv", self.thermal_conductivity, fmt='%2.4e', header="K [W/mK]", encoding='utf-8')
|
|
115
105
|
np.savetxt("Data/All initial energies.csv", self.initial_energies, fmt='%2.4e', header="Energy [J]", encoding='utf-8')
|
|
116
|
-
np.savetxt("Data/All interfaces angles.csv", self.interfaces_angles, fmt='%.4f', header="Angle [rad]", encoding='utf-8')
|
|
117
|
-
np.savetxt("Data/All interfaces transmission factor.csv", self.interfaces_transmission_factor, fmt='%2.4e', header="Transmission factor [%]", encoding='utf-8')
|
|
118
|
-
np.savetxt("Data/All interfaces wavelength.csv", self.interfaces_wavelength, fmt='%.18f', header="Interfaces wavelength [m]", encoding='utf-8')
|
|
119
|
-
np.savetxt("Data/All interfaces frequency.csv", self.interfaces_frequency, fmt='%.18f', header="Interfaces frequency [THz]", encoding='utf-8')
|
|
120
|
-
np.savetxt("Data/All interfaces mode.csv", self.interfaces_mode, fmt='%d', header="Interfaces mode", encoding='utf-8')
|
|
121
|
-
|
|
122
106
|
def dump_data(self):
|
|
123
107
|
"""Return data of a process in the form of a dictionary to be attached to the global data"""
|
|
124
108
|
return {
|
|
@@ -133,11 +117,6 @@ class GeneralData(Data):
|
|
|
133
117
|
'mean_free_paths': self.mean_free_paths,
|
|
134
118
|
'thermal_conductivity': self.thermal_conductivity,
|
|
135
119
|
'initial_energies': self.initial_energies,
|
|
136
|
-
'interfaces_angles': self.interfaces_angles,
|
|
137
|
-
'interfaces_transmission_factor': self.interfaces_transmission_factor,
|
|
138
|
-
'interfaces_wavelength': self.interfaces_wavelength,
|
|
139
|
-
'interfaces_frequency': self.interfaces_frequency,
|
|
140
|
-
'interfaces_mode': self.interfaces_mode,
|
|
141
120
|
}
|
|
142
121
|
|
|
143
122
|
|
|
@@ -298,11 +277,9 @@ class SegmentData(Data):
|
|
|
298
277
|
|
|
299
278
|
def record_time_in_segment(self, coordinate):
|
|
300
279
|
"""Record how long particle stays in different segments"""
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if segment_beginning <= coordinate < segment_end:
|
|
305
|
-
self.time_spent[segment_number] += cf.timestep * 1e6
|
|
280
|
+
segment = int(coordinate // (cf.length / cf.number_of_length_segments))
|
|
281
|
+
if 0 <= segment < cf.number_of_length_segments:
|
|
282
|
+
self.time_spent[segment] += cf.timestep * 1e9
|
|
306
283
|
|
|
307
284
|
def write_into_files(self):
|
|
308
285
|
"""Write data into files"""
|
|
@@ -25,13 +25,16 @@ NUMBER_OF_STABILIZATION_TIMEFRAMES = 5
|
|
|
25
25
|
|
|
26
26
|
# Electron parameters [eV]
|
|
27
27
|
IS_CARRIER_ELECTRON = True
|
|
28
|
-
ENERGY_UPPER_BOUND =
|
|
28
|
+
ENERGY_UPPER_BOUND = 350e-3
|
|
29
29
|
ENERGY_LOWER_BOUND = 0
|
|
30
30
|
ENERGY_STEP = 5e-3
|
|
31
31
|
|
|
32
32
|
ELECTRON_MFP = 10e-9 # [m]
|
|
33
33
|
MEAN_MAPPING_CONSTANT = 5e-6 # [m²]
|
|
34
34
|
|
|
35
|
+
FERMI_LEVEL_LOWER_BOUND = -0.2 # [eV] lower end of post-processing Fermi level sweep
|
|
36
|
+
FERMI_LEVEL_UPPER_BOUND = 0.2 # [eV] upper end of post-processing Fermi level sweep
|
|
37
|
+
|
|
35
38
|
|
|
36
39
|
# Animation:
|
|
37
40
|
OUTPUT_PATH_ANIMATION = False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""This module provides electron class which generates and moves an electron"""
|
|
2
2
|
|
|
3
|
-
from random import choice
|
|
3
|
+
from random import choice, random
|
|
4
4
|
from scipy.constants import h, electron_volt
|
|
5
5
|
from freepaths.config import cf
|
|
6
6
|
from freepaths.particle import Particle
|
|
@@ -72,6 +72,4 @@ class Electron(Particle):
|
|
|
72
72
|
Considering only elasctic scattering between electrons and phonons and thus a constant MFP.
|
|
73
73
|
"""
|
|
74
74
|
mfp = cf.electron_mfp
|
|
75
|
-
self.time_of_internal_scattering = mfp / self.speed
|
|
76
|
-
|
|
77
|
-
# self.time_of_internal_scattering = -np.log(random()) * (mfp/self.speed)
|
|
75
|
+
self.time_of_internal_scattering = -np.log(random()) * (mfp / self.speed)
|
|
@@ -43,12 +43,6 @@ class Flight:
|
|
|
43
43
|
self.hole_diff_scattering_angles = []
|
|
44
44
|
self.hole_spec_scattering_angles = []
|
|
45
45
|
self.thermal_conductivity = 0.0
|
|
46
|
-
self.interfaces_angles = []
|
|
47
|
-
self.interfaces_transmission_factor = []
|
|
48
|
-
self.interfaces_wavelength = []
|
|
49
|
-
self.interfaces_frequency = []
|
|
50
|
-
self.interfaces_mode = []
|
|
51
|
-
|
|
52
46
|
@property
|
|
53
47
|
def mean_free_path(self):
|
|
54
48
|
"""Mean value of all free flights"""
|
|
@@ -74,26 +68,6 @@ class Flight:
|
|
|
74
68
|
"""Save angle of specular scattering from the hole"""
|
|
75
69
|
self.hole_spec_scattering_angles.append(angle)
|
|
76
70
|
|
|
77
|
-
def save_interfaces_angles(self, angle):
|
|
78
|
-
"""Save a transmission event angle"""
|
|
79
|
-
self.interfaces_angles.append(angle)
|
|
80
|
-
|
|
81
|
-
def save_interfaces_transmission_factor(self, alpha_total):
|
|
82
|
-
"""Save a transmission event"""
|
|
83
|
-
self.interfaces_transmission_factor.append(alpha_total)
|
|
84
|
-
|
|
85
|
-
def save_interfaces_wavelength(self):
|
|
86
|
-
"""Save a wavelength event"""
|
|
87
|
-
self.interfaces_wavelength.append(self.particle.wavelength)
|
|
88
|
-
|
|
89
|
-
def save_interfaces_frequency(self):
|
|
90
|
-
"""Save a frequency event"""
|
|
91
|
-
self.interfaces_frequency.append(self.particle.f)
|
|
92
|
-
|
|
93
|
-
def save_interfaces_mode (self):
|
|
94
|
-
"""Save mode"""
|
|
95
|
-
self.interfaces_mode.append(self.particle.branch_number)
|
|
96
|
-
|
|
97
71
|
def restart(self):
|
|
98
72
|
"""Restart the flight after a scattering event"""
|
|
99
73
|
self.time_since_previous_scattering = 0.0
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""Module to run thermal conductivity calculations by integrating the phonon dispersion"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import numpy as np
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
import shutil
|
|
8
|
+
import scipy
|
|
9
|
+
import math
|
|
10
|
+
import logging
|
|
11
|
+
import signal
|
|
12
|
+
import traceback
|
|
13
|
+
import multiprocessing
|
|
14
|
+
from colorama import Fore, Style
|
|
15
|
+
|
|
16
|
+
# Modules:
|
|
17
|
+
from freepaths.animation import create_animation
|
|
18
|
+
from freepaths.config import cf
|
|
19
|
+
from freepaths.run_particle import run_particle
|
|
20
|
+
from freepaths.phonon import Phonon
|
|
21
|
+
from freepaths.flight import Flight
|
|
22
|
+
from freepaths.particle_types import ParticleType
|
|
23
|
+
from freepaths.data import ScatteringData, GeneralData, SegmentData, PathData, TriangleScatteringData
|
|
24
|
+
from freepaths.materials import Si, SiC, Graphite, SiGe
|
|
25
|
+
from freepaths.maps import ScatteringMap
|
|
26
|
+
from freepaths.progress import Progress
|
|
27
|
+
from freepaths.output_info import output_general_information, output_scattering_information, output_parameter_warnings
|
|
28
|
+
from freepaths.output_plots import plot_data
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _branch_worker(branch_number, shared_list):
|
|
32
|
+
"""Entry point for each worker process. Catches exceptions so they are logged
|
|
33
|
+
rather than silently swallowed by the multiprocessing machinery."""
|
|
34
|
+
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
35
|
+
try:
|
|
36
|
+
_run_branch(branch_number, shared_list)
|
|
37
|
+
except Exception:
|
|
38
|
+
logging.exception("Branch worker %d crashed", branch_number)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _run_branch(branch_number, shared_list):
|
|
42
|
+
"""Run all phonons for one polarization branch and push results to shared_list.
|
|
43
|
+
|
|
44
|
+
Each call processes NUMBER_OF_PARTICLES phonons sampled uniformly across the
|
|
45
|
+
dispersion of the given branch. The k-vector integration weight (d_k_vector)
|
|
46
|
+
is the width of the k-space interval assigned to each phonon, so the sum of
|
|
47
|
+
flight.thermal_conductivity over all phonons approximates the branch integral
|
|
48
|
+
in Phys. Rev. 132 2461 (1963).
|
|
49
|
+
|
|
50
|
+
Branch 0 also drives the shared progress bar; the other two run silently.
|
|
51
|
+
Results are appended to shared_list as a dict of dump_data() payloads so that
|
|
52
|
+
the main process can merge them with the standard read_data() mechanism.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
# Each worker builds its own material instance so the dispersion is
|
|
56
|
+
# sampled with the correct number of points (number_of_particles + 1
|
|
57
|
+
# intervals → number_of_particles midpoints):
|
|
58
|
+
if cf.media == "Si":
|
|
59
|
+
material = Si(cf.temp, num_points=cf.number_of_particles + 1)
|
|
60
|
+
elif cf.media == "SiGe":
|
|
61
|
+
material = SiGe(cf.temp, num_points=cf.number_of_particles + 1)
|
|
62
|
+
elif cf.media == "SiC":
|
|
63
|
+
material = SiC(cf.temp, num_points=cf.number_of_particles + 1)
|
|
64
|
+
elif cf.media == "Graphite":
|
|
65
|
+
material = Graphite(cf.temp, num_points=cf.number_of_particles + 1)
|
|
66
|
+
else:
|
|
67
|
+
logging.error(f"Material {cf.media} is not supported")
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
# Local data structures — merged into the global ones after all workers finish:
|
|
71
|
+
scatter_stats = ScatteringData()
|
|
72
|
+
general_stats = GeneralData()
|
|
73
|
+
segment_stats = SegmentData()
|
|
74
|
+
places_stats = TriangleScatteringData()
|
|
75
|
+
path_stats = PathData()
|
|
76
|
+
scatter_maps = ScatteringMap()
|
|
77
|
+
|
|
78
|
+
total_thermal_conductivity = 0.0
|
|
79
|
+
progress = Progress() if branch_number == 0 else None
|
|
80
|
+
|
|
81
|
+
for index in range(cf.number_of_particles):
|
|
82
|
+
if progress:
|
|
83
|
+
progress.render(index, cf.number_of_particles)
|
|
84
|
+
|
|
85
|
+
# Mid-point k-vector and integration weight for this phonon:
|
|
86
|
+
k_vector = (material.dispersion[index + 1, 0] + material.dispersion[index, 0]) / 2
|
|
87
|
+
d_k_vector = material.dispersion[index + 1, 0] - material.dispersion[index, 0]
|
|
88
|
+
|
|
89
|
+
# Initialise phonon at this k-point and run it through the geometry:
|
|
90
|
+
phonon = Phonon(material, branch_number, index)
|
|
91
|
+
flight = Flight(phonon)
|
|
92
|
+
run_particle(phonon, flight, scatter_stats, places_stats, segment_stats, None, scatter_maps, material)
|
|
93
|
+
|
|
94
|
+
# Mode heat capacity (Ref. PRB 88 155318, 2013):
|
|
95
|
+
omega = 2 * math.pi * phonon.f
|
|
96
|
+
part = scipy.constants.hbar * omega / (scipy.constants.k * cf.temp)
|
|
97
|
+
c_p = scipy.constants.k * part**2 * math.exp(part) / (math.exp(part) - 1)**2
|
|
98
|
+
|
|
99
|
+
# Thermal conductivity contribution from this k-point (Ref. Phys. Rev. 132 2461, 1963):
|
|
100
|
+
mean_relax_time = flight.mean_free_path / phonon.speed
|
|
101
|
+
flight.thermal_conductivity = (1 / (6 * math.pi**2)) * c_p * phonon.speed**2 * mean_relax_time * k_vector**2 * d_k_vector
|
|
102
|
+
total_thermal_conductivity += flight.thermal_conductivity
|
|
103
|
+
|
|
104
|
+
general_stats.save_particle_data(phonon)
|
|
105
|
+
general_stats.save_flight_data(flight)
|
|
106
|
+
|
|
107
|
+
if index < cf.output_trajectories_of_first:
|
|
108
|
+
path_stats.save_particle_path(flight)
|
|
109
|
+
|
|
110
|
+
# Push this branch's results to the shared list for the main process to collect:
|
|
111
|
+
shared_list.append({
|
|
112
|
+
'total_thermal_conductivity': total_thermal_conductivity,
|
|
113
|
+
'scatter_stats': scatter_stats.dump_data(),
|
|
114
|
+
'general_stats': general_stats.dump_data(),
|
|
115
|
+
'segment_stats': segment_stats.dump_data(),
|
|
116
|
+
'places_stats': places_stats.dump_data(),
|
|
117
|
+
'path_stats': path_stats.dump_data(),
|
|
118
|
+
'scatter_maps': scatter_maps.dump_data(),
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def main(input_file, particle_type):
|
|
123
|
+
"""Integrate the phonon dispersion over all three branches in parallel to get
|
|
124
|
+
the bulk thermal conductivity via the MFP-sampling method."""
|
|
125
|
+
|
|
126
|
+
print(f'Mean free path sampling of {Fore.GREEN}{cf.output_folder_name}{Style.RESET_ALL}')
|
|
127
|
+
start_time = time.time()
|
|
128
|
+
|
|
129
|
+
# Spawn one worker process per polarization branch (LA + 2×TA).
|
|
130
|
+
# All three run concurrently; branch 0 reports progress to stdout:
|
|
131
|
+
manager = multiprocessing.Manager()
|
|
132
|
+
shared_list = manager.list()
|
|
133
|
+
|
|
134
|
+
processes = [
|
|
135
|
+
multiprocessing.Process(target=_branch_worker, args=(branch_number, shared_list))
|
|
136
|
+
for branch_number in range(3)
|
|
137
|
+
]
|
|
138
|
+
for process in processes:
|
|
139
|
+
process.start()
|
|
140
|
+
try:
|
|
141
|
+
for process in processes:
|
|
142
|
+
process.join()
|
|
143
|
+
except KeyboardInterrupt:
|
|
144
|
+
for process in processes:
|
|
145
|
+
process.terminate()
|
|
146
|
+
raise
|
|
147
|
+
|
|
148
|
+
# Merge per-branch results into single data structures:
|
|
149
|
+
scatter_stats = ScatteringData()
|
|
150
|
+
general_stats = GeneralData()
|
|
151
|
+
segment_stats = SegmentData()
|
|
152
|
+
places_stats = TriangleScatteringData()
|
|
153
|
+
path_stats = PathData()
|
|
154
|
+
scatter_maps = ScatteringMap()
|
|
155
|
+
total_thermal_conductivity = 0.0
|
|
156
|
+
|
|
157
|
+
for result in shared_list:
|
|
158
|
+
total_thermal_conductivity += result['total_thermal_conductivity']
|
|
159
|
+
scatter_stats.read_data(result['scatter_stats'])
|
|
160
|
+
general_stats.read_data(result['general_stats'])
|
|
161
|
+
segment_stats.read_data(result['segment_stats'])
|
|
162
|
+
places_stats.read_data(result['places_stats'])
|
|
163
|
+
path_stats.read_data(result['path_stats'])
|
|
164
|
+
scatter_maps.read_data(result['scatter_maps'])
|
|
165
|
+
|
|
166
|
+
# Create the output folder and copy input file:
|
|
167
|
+
if not os.path.exists("Results/" + cf.output_folder_name):
|
|
168
|
+
os.makedirs("Results/" + cf.output_folder_name)
|
|
169
|
+
os.makedirs("Results/" + cf.output_folder_name + '/Data')
|
|
170
|
+
if input_file:
|
|
171
|
+
shutil.copy(input_file, "Results/" + cf.output_folder_name)
|
|
172
|
+
os.chdir("Results/" + cf.output_folder_name)
|
|
173
|
+
|
|
174
|
+
# Save data and generate plots:
|
|
175
|
+
general_stats.write_into_files()
|
|
176
|
+
scatter_stats.write_into_files()
|
|
177
|
+
segment_stats.write_into_files()
|
|
178
|
+
if cf.output_scattering_map:
|
|
179
|
+
scatter_maps.write_into_files()
|
|
180
|
+
path_stats.write_into_files()
|
|
181
|
+
|
|
182
|
+
if cf.output_path_animation:
|
|
183
|
+
create_animation()
|
|
184
|
+
|
|
185
|
+
np.savetxt("Data/Thermal conductivity from MFP.csv", np.array([total_thermal_conductivity]), fmt='%2.4e', header="K [W/mK]", encoding='utf-8')
|
|
186
|
+
|
|
187
|
+
sys.stdout.write("\rAnalyzing the data...")
|
|
188
|
+
plot_data(particle_type, cf, mfp_sampling=True)
|
|
189
|
+
|
|
190
|
+
output_general_information(start_time)
|
|
191
|
+
output_scattering_information(scatter_stats)
|
|
192
|
+
output_parameter_warnings(ParticleType.PHONON)
|
|
193
|
+
sys.stdout.write(f'\rSee the results in {Fore.GREEN}Results/{cf.output_folder_name}{Style.RESET_ALL}\n')
|
|
194
|
+
sys.stdout.write(f"\rThermal conductivity = {Fore.GREEN}{total_thermal_conductivity:.5f}{Style.RESET_ALL} W/m·K\n")
|
|
195
|
+
sys.stdout.write(f"\r{Fore.BLUE}Thank you for using FreePATHS{Style.RESET_ALL}\n\n")
|
|
@@ -280,10 +280,12 @@ def main(input_file, particle_type):
|
|
|
280
280
|
sys.stdout.write("\rSaving raw data...")
|
|
281
281
|
general_stats.write_into_files()
|
|
282
282
|
scatter_stats.write_into_files()
|
|
283
|
-
|
|
283
|
+
if cf.holes:
|
|
284
|
+
places_stats.write_into_files()
|
|
284
285
|
segment_stats.write_into_files()
|
|
285
286
|
thermal_maps.write_into_files()
|
|
286
|
-
|
|
287
|
+
if cf.output_scattering_map:
|
|
288
|
+
scatter_maps.write_into_files()
|
|
287
289
|
path_stats.write_into_files()
|
|
288
290
|
if particle_type is ParticleType.ELECTRON:
|
|
289
291
|
electron_computations.write_into_file()
|
|
@@ -119,6 +119,8 @@ class ThermalMaps(Maps):
|
|
|
119
119
|
|
|
120
120
|
def calculate_pixel_volumes(self, number_of_pixels_x, number_of_pixels_y):
|
|
121
121
|
"""Calculate a map showing if the pixel contains material (1) or a hole (0)"""
|
|
122
|
+
if not cf.holes:
|
|
123
|
+
return np.ones((number_of_pixels_y, number_of_pixels_x))
|
|
122
124
|
pixel_volume_ratios = np.zeros((number_of_pixels_y, number_of_pixels_x))
|
|
123
125
|
for x_index in range(number_of_pixels_x):
|
|
124
126
|
for y_index in range(number_of_pixels_y):
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
import numpy as np
|
|
5
|
-
from scipy.constants import electron_volt, electron_mass
|
|
5
|
+
from scipy.constants import electron_volt, electron_mass, k as k_B, h as h_planck
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class Material(ABC):
|
|
@@ -74,6 +74,16 @@ class Si(Material):
|
|
|
74
74
|
coeffs = above_50K_coeffs
|
|
75
75
|
self.heat_capacity = np.polyval(coeffs, self.temp)
|
|
76
76
|
|
|
77
|
+
# --- Impurity scattering via Matthiessen's rule (disabled; re-enable when ready) ---
|
|
78
|
+
# Phonon-limited electron MFP at 300K, calibrated to match lightly doped Si literature
|
|
79
|
+
# phonon_limited_electron_mfp = 10e-9 # [m]
|
|
80
|
+
# Caughey-Thomas ionized-impurity parameters for electrons, Masetti et al. (1983)
|
|
81
|
+
# _ct_mu_max = 1417e-4 # [m²/V·s]
|
|
82
|
+
# _ct_mu_min = 52.2e-4 # [m²/V·s]
|
|
83
|
+
# _ct_n_ref = 9.68e22 # [m⁻³] (= 9.68e16 cm⁻³)
|
|
84
|
+
# _ct_alpha = 0.680
|
|
85
|
+
# ---------------------------------------------------------------------------------
|
|
86
|
+
|
|
77
87
|
def assign_electrical_properties(self, fermi_level):
|
|
78
88
|
"""Assign differents electrical properties to the material."""
|
|
79
89
|
self.effective_electron_dos_mass = 1.18 * electron_mass # [kg] at 300K for pure Si, supposed constant for all temperatures (~1-5% error)
|
|
@@ -87,6 +97,17 @@ class Si(Material):
|
|
|
87
97
|
else:
|
|
88
98
|
self.fermi_level = -0.037 * electron_volt # [J]
|
|
89
99
|
|
|
100
|
+
# def carrier_density(self):
|
|
101
|
+
# """Electron carrier density from Fermi level using Boltzmann approximation [m⁻³]"""
|
|
102
|
+
# nc = 2 * (2 * np.pi * self.effective_electron_dos_mass * k_B * self.temp / h_planck**2) ** 1.5
|
|
103
|
+
# return nc * np.exp(self.fermi_level / (k_B * self.temp))
|
|
104
|
+
|
|
105
|
+
# def effective_electron_mfp(self):
|
|
106
|
+
# """Effective electron MFP combining phonon and impurity scattering via Matthiessen's rule"""
|
|
107
|
+
# n = self.carrier_density()
|
|
108
|
+
# mu = self._ct_mu_min + (self._ct_mu_max - self._ct_mu_min) / (1 + (n / self._ct_n_ref) ** self._ct_alpha)
|
|
109
|
+
# return self.phonon_limited_electron_mfp * mu / self._ct_mu_max
|
|
110
|
+
|
|
90
111
|
|
|
91
112
|
class Vacuum:
|
|
92
113
|
def __init__(self, temp=300):
|
|
@@ -110,7 +131,18 @@ class SiC:
|
|
|
110
131
|
Heat capacity - Collins et al. Journal of Applied Physics 68, 6510 (1990)
|
|
111
132
|
"""
|
|
112
133
|
|
|
113
|
-
|
|
134
|
+
# --- Impurity scattering via Matthiessen's rule (disabled; re-enable when ready) ---
|
|
135
|
+
# 4H-SiC electrical properties:
|
|
136
|
+
# Electron effective masses - Ioffe database / Persson & Lindefelt, PRB 54, 10257 (1996)
|
|
137
|
+
# CT parameters - Roschke & Schwierz, IEEE Trans. Electron Devices 48, 1442 (2001)
|
|
138
|
+
# phonon_limited_electron_mfp = 7e-9 # [m] estimate, needs experimental calibration
|
|
139
|
+
# _ct_mu_max = 947e-4 # [m²/V·s]
|
|
140
|
+
# _ct_mu_min = 40e-4 # [m²/V·s]
|
|
141
|
+
# _ct_n_ref = 2.0e23 # [m⁻³] (= 2.0e17 cm⁻³)
|
|
142
|
+
# _ct_alpha = 0.61
|
|
143
|
+
# ---------------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
def __init__(self, temp, num_points=1000, fermi_level=None):
|
|
114
146
|
self.name = "SiC"
|
|
115
147
|
self.density = 3215 # [kg/m^3]
|
|
116
148
|
self.default_speed = 6500 # [m/s] Need to change this probably...
|
|
@@ -118,6 +150,27 @@ class SiC:
|
|
|
118
150
|
self.assign_phonon_dispersion(num_points)
|
|
119
151
|
self.assign_heat_capacity()
|
|
120
152
|
|
|
153
|
+
# def assign_electrical_properties(self, fermi_level):
|
|
154
|
+
# """Assign electrical properties for 4H-SiC"""
|
|
155
|
+
# self.effective_electron_dos_mass = 1.0 * electron_mass # [kg] 4H-SiC, Ioffe
|
|
156
|
+
# self.effective_electron_mass = 0.36 * electron_mass # [kg] conductivity mass
|
|
157
|
+
# self.effective_hole_dos_mass = 0.6 * electron_mass
|
|
158
|
+
# self.effective_hole_mass = 0.6 * electron_mass
|
|
159
|
+
# self.fermi_level = fermi_level # None = no CT correction applied
|
|
160
|
+
|
|
161
|
+
# def carrier_density(self):
|
|
162
|
+
# """Electron carrier density from Fermi level using Boltzmann approximation [m⁻³]"""
|
|
163
|
+
# nc = 2 * (2 * np.pi * self.effective_electron_dos_mass * k_B * self.temp / h_planck**2) ** 1.5
|
|
164
|
+
# return nc * np.exp(self.fermi_level / (k_B * self.temp))
|
|
165
|
+
|
|
166
|
+
# def effective_electron_mfp(self):
|
|
167
|
+
# """Effective electron MFP combining phonon and impurity scattering via Matthiessen's rule"""
|
|
168
|
+
# if self.fermi_level is None:
|
|
169
|
+
# return self.phonon_limited_electron_mfp
|
|
170
|
+
# n = self.carrier_density()
|
|
171
|
+
# mu = self._ct_mu_min + (self._ct_mu_max - self._ct_mu_min) / (1 + (n / self._ct_n_ref) ** self._ct_alpha)
|
|
172
|
+
# return self.phonon_limited_electron_mfp * mu / self._ct_mu_max
|
|
173
|
+
|
|
121
174
|
def assign_phonon_dispersion(self, num_points):
|
|
122
175
|
"""Assign phonon dispersion"""
|
|
123
176
|
|
|
@@ -220,6 +273,13 @@ class SiGe(Material):
|
|
|
220
273
|
Effective masses – linearly interpolated between Si and Ge at x=0.2 (Schaffler, 2001)
|
|
221
274
|
"""
|
|
222
275
|
|
|
276
|
+
# --- Impurity scattering via Matthiessen's rule (disabled; re-enable when ready) ---
|
|
277
|
+
# phonon_limited_electron_mfp = 7e-9 # [m] estimate; alloy scattering reduces from pure Si
|
|
278
|
+
# def effective_electron_mfp(self):
|
|
279
|
+
# """Phonon-limited only — CT params not established for SiGe alloy"""
|
|
280
|
+
# return self.phonon_limited_electron_mfp
|
|
281
|
+
# ---------------------------------------------------------------------------------
|
|
282
|
+
|
|
223
283
|
def __init__(self, temp, num_points=1000):
|
|
224
284
|
self.name = "SiGe"
|
|
225
285
|
self.default_speed = 3700 # [m/s] – average LA/TA
|
|
@@ -275,6 +335,13 @@ class Diamond(Material):
|
|
|
275
335
|
Dispersion - Ref. PRB 58 12899 (1998)
|
|
276
336
|
"""
|
|
277
337
|
|
|
338
|
+
# --- Impurity scattering via Matthiessen's rule (disabled; re-enable when ready) ---
|
|
339
|
+
# phonon_limited_electron_mfp = 20e-9 # [m] estimate; CT params for n-type diamond not established
|
|
340
|
+
# def effective_electron_mfp(self):
|
|
341
|
+
# """Phonon-limited only — CT params for n-type diamond not established"""
|
|
342
|
+
# return self.phonon_limited_electron_mfp
|
|
343
|
+
# ---------------------------------------------------------------------------------
|
|
344
|
+
|
|
278
345
|
def __init__(self, temp, num_points=1000):
|
|
279
346
|
self.name = "Diamond"
|
|
280
347
|
self.density = 3500 # [kg/m^3]
|