freepaths 2.3.3__tar.gz → 2.3.4__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.3 → freepaths-2.3.4}/PKG-INFO +1 -1
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/__main__.py +5 -5
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/config.py +5 -1
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/data.py +32 -22
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/electron.py +1 -1
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/main_mfp_sampling.py +55 -19
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/main_tracing.py +48 -30
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/maps.py +8 -45
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/materials.py +4 -0
- freepaths-2.3.4/freepaths/options.py +10 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/output_electron_plots.py +44 -23
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/output_info.py +37 -10
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/output_plots.py +55 -45
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/phonon.py +1 -1
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/post_computations.py +13 -4
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/run_particle.py +1 -1
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/scattering_interfaces.py +1 -1
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/scattering_primitives.py +1 -1
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths.egg-info/PKG-INFO +1 -1
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths.egg-info/SOURCES.txt +1 -1
- freepaths-2.3.3/freepaths/particle_types.py +0 -5
- {freepaths-2.3.3 → freepaths-2.3.4}/README.md +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/animation.py +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/default_config.py +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/flight.py +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/interface_smmm.py +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/move.py +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/output_structure.py +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/particle.py +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/progress.py +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/scatterers.py +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/scattering.py +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/scattering_semicircle.py +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/scattering_types.py +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths/sources.py +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths.egg-info/dependency_links.txt +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths.egg-info/entry_points.txt +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths.egg-info/requires.txt +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/freepaths.egg-info/top_level.txt +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/pyproject.toml +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/setup.cfg +0 -0
- {freepaths-2.3.3 → freepaths-2.3.4}/setup.py +0 -0
|
@@ -5,9 +5,9 @@ import argparse
|
|
|
5
5
|
import colorama
|
|
6
6
|
from colorama import Fore, Style
|
|
7
7
|
|
|
8
|
-
from freepaths.
|
|
8
|
+
from freepaths.options import SimulationMode
|
|
9
9
|
|
|
10
|
-
__version__ = "2.3.
|
|
10
|
+
__version__ = "2.3.4"
|
|
11
11
|
|
|
12
12
|
colorama.init()
|
|
13
13
|
|
|
@@ -45,15 +45,15 @@ def run():
|
|
|
45
45
|
try:
|
|
46
46
|
if args.sampling:
|
|
47
47
|
import freepaths.main_mfp_sampling
|
|
48
|
-
freepaths.main_mfp_sampling.main(args.input_file,
|
|
48
|
+
freepaths.main_mfp_sampling.main(args.input_file, SimulationMode.PHONON_MFP_SAMPLING)
|
|
49
49
|
|
|
50
50
|
elif args.electron:
|
|
51
51
|
import freepaths.main_tracing
|
|
52
|
-
freepaths.main_tracing.main(args.input_file,
|
|
52
|
+
freepaths.main_tracing.main(args.input_file, SimulationMode.ELECTRON)
|
|
53
53
|
|
|
54
54
|
else:
|
|
55
55
|
import freepaths.main_tracing
|
|
56
|
-
freepaths.main_tracing.main(args.input_file,
|
|
56
|
+
freepaths.main_tracing.main(args.input_file, SimulationMode.PHONON_TRACING)
|
|
57
57
|
|
|
58
58
|
except KeyboardInterrupt:
|
|
59
59
|
print(f"\n{Fore.RED}Simulation interrupted by user.{Style.RESET_ALL}")
|
|
@@ -3,6 +3,7 @@ Module that reads the user input file, provides default values,
|
|
|
3
3
|
checks the validity of the parameters and converts the variables into enums
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
import os
|
|
6
7
|
import sys
|
|
7
8
|
import argparse
|
|
8
9
|
import logging
|
|
@@ -32,8 +33,11 @@ args = parser.parse_args()
|
|
|
32
33
|
|
|
33
34
|
# If a file is provided, overwrite the default values:
|
|
34
35
|
if args.input_file:
|
|
36
|
+
input_path = args.input_file
|
|
37
|
+
if not os.path.isfile(input_path) and not input_path.endswith('.py') and os.path.isfile(input_path + '.py'):
|
|
38
|
+
input_path = input_path + '.py'
|
|
35
39
|
try:
|
|
36
|
-
exec(open(
|
|
40
|
+
exec(open(input_path, encoding='utf-8').read(), globals())
|
|
37
41
|
except FileNotFoundError:
|
|
38
42
|
logging.error("Input file does not exist. Check if you are in the right folder and the file name is correct.")
|
|
39
43
|
sys.exit()
|
|
@@ -66,43 +66,53 @@ class GeneralData(Data):
|
|
|
66
66
|
self.travel_times = []
|
|
67
67
|
self.mean_free_paths = []
|
|
68
68
|
self.thermal_conductivity = []
|
|
69
|
-
def save_particle_data(self, pt):
|
|
69
|
+
def save_particle_data(self, pt, mode=None):
|
|
70
70
|
"""Add information about the particle to the dataset"""
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
from freepaths.options import SimulationMode
|
|
72
|
+
if mode is SimulationMode.ELECTRON:
|
|
73
|
+
self.initial_energies.append(pt.energy)
|
|
74
|
+
else:
|
|
75
|
+
self.frequencies.append(pt.f)
|
|
76
|
+
self.group_velocities.append(pt.speed)
|
|
77
|
+
|
|
78
|
+
def save_flight_data(self, flight, mode=None):
|
|
76
79
|
"""Add information about the particle flight to the dataset"""
|
|
80
|
+
from freepaths.options import SimulationMode
|
|
77
81
|
if not cf.low_memory_usage:
|
|
78
82
|
self.free_paths.extend(flight.free_paths)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
if mode is SimulationMode.PHONON_TRACING:
|
|
84
|
+
self.initial_angles.append(flight.initial_theta)
|
|
85
|
+
self.exit_angles.append(flight.exit_theta)
|
|
86
|
+
self.hole_diff_scattering_angles.extend(flight.hole_diff_scattering_angles)
|
|
87
|
+
self.hole_spec_scattering_angles.extend(flight.hole_spec_scattering_angles)
|
|
83
88
|
self.travel_times.append(flight.travel_time)
|
|
84
89
|
self.mean_free_paths.append(flight.mean_free_path)
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
if mode is SimulationMode.PHONON_MFP_SAMPLING:
|
|
91
|
+
self.thermal_conductivity.append(flight.thermal_conductivity)
|
|
87
92
|
|
|
88
|
-
def write_into_files(self):
|
|
93
|
+
def write_into_files(self, mode=None):
|
|
89
94
|
import os
|
|
95
|
+
from freepaths.options import SimulationMode
|
|
90
96
|
if not os.path.exists("Data"):
|
|
91
97
|
os.makedirs("Data")
|
|
92
98
|
"""Write all the data into files"""
|
|
93
99
|
if not cf.low_memory_usage:
|
|
94
100
|
np.savetxt("Data/All free paths.csv", self.free_paths, fmt='%2.4e', header="L [m]", encoding='utf-8')
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
np.savetxt("Data/All
|
|
99
|
-
np.savetxt("Data/All
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
if mode is SimulationMode.ELECTRON:
|
|
102
|
+
np.savetxt("Data/All initial energies.csv", self.initial_energies, fmt='%2.4e', header="Energy [J]", encoding='utf-8')
|
|
103
|
+
else:
|
|
104
|
+
np.savetxt("Data/All initial frequencies.csv", self.frequencies, fmt='%2.4e', header="f [Hz]", encoding='utf-8')
|
|
105
|
+
np.savetxt("Data/All group velocities.csv", self.group_velocities, fmt='%.4f', header="Vg [m//s]", encoding='utf-8')
|
|
106
|
+
if mode is SimulationMode.PHONON_TRACING:
|
|
107
|
+
np.savetxt("Data/All exit angles.csv", self.exit_angles, fmt='%.4f', header="Angle [rad]", encoding='utf-8')
|
|
108
|
+
np.savetxt("Data/All initial angles.csv", self.initial_angles, fmt='%.4f', header="Angle [rad]", encoding='utf-8')
|
|
109
|
+
if cf.holes:
|
|
110
|
+
np.savetxt("Data/All hole diffuse scattering angles.csv", self.hole_diff_scattering_angles, fmt='%.4f', header="Angle [rad]", encoding='utf-8')
|
|
111
|
+
np.savetxt("Data/All hole specular scattering angles.csv", self.hole_spec_scattering_angles, fmt='%.4f', header="Angle [rad]", encoding='utf-8')
|
|
102
112
|
np.savetxt("Data/All travel times.csv", self.travel_times, fmt='%2.4e', header="Travel time [s]", encoding='utf-8')
|
|
103
113
|
np.savetxt("Data/All mean free paths.csv", self.mean_free_paths, fmt='%2.4e', header="MFPs [m]", encoding='utf-8')
|
|
104
|
-
|
|
105
|
-
|
|
114
|
+
if mode is SimulationMode.PHONON_MFP_SAMPLING:
|
|
115
|
+
np.savetxt("Data/All thermal conductivities.csv", self.thermal_conductivity, fmt='%2.4e', header="K [W/mK]", encoding='utf-8')
|
|
106
116
|
def dump_data(self):
|
|
107
117
|
"""Return data of a process in the form of a dictionary to be attached to the global data"""
|
|
108
118
|
return {
|
|
@@ -4,7 +4,7 @@ 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
|
|
7
|
-
from freepaths.
|
|
7
|
+
from freepaths.options import ParticleType
|
|
8
8
|
|
|
9
9
|
import numpy as np
|
|
10
10
|
|
|
@@ -19,26 +19,49 @@ from freepaths.config import cf
|
|
|
19
19
|
from freepaths.run_particle import run_particle
|
|
20
20
|
from freepaths.phonon import Phonon
|
|
21
21
|
from freepaths.flight import Flight
|
|
22
|
-
from freepaths.
|
|
22
|
+
from freepaths.options import SimulationMode
|
|
23
23
|
from freepaths.data import ScatteringData, GeneralData, SegmentData, PathData, TriangleScatteringData
|
|
24
24
|
from freepaths.materials import Si, SiC, Graphite, SiGe
|
|
25
25
|
from freepaths.maps import ScatteringMap
|
|
26
|
-
from freepaths.progress import Progress
|
|
27
26
|
from freepaths.output_info import output_general_information, output_scattering_information, output_parameter_warnings
|
|
27
|
+
from freepaths.materials import get_media_class
|
|
28
28
|
from freepaths.output_plots import plot_data
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
def
|
|
31
|
+
def display_all_progress(shared_progress, finished_branches, branch_names):
|
|
32
|
+
"""Render a live progress percentage for each branch on its own line."""
|
|
33
|
+
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
34
|
+
n = len(branch_names)
|
|
35
|
+
width = max(len(name) for name in branch_names)
|
|
36
|
+
try:
|
|
37
|
+
for name in branch_names:
|
|
38
|
+
sys.stdout.write(f' {name.ljust(width)}: 0%\n')
|
|
39
|
+
sys.stdout.flush()
|
|
40
|
+
while True:
|
|
41
|
+
sys.stdout.write(f'\033[{n}A')
|
|
42
|
+
for i, name in enumerate(branch_names):
|
|
43
|
+
sys.stdout.write(f'\r {name.ljust(width)}: {shared_progress[i]:3d}%\n')
|
|
44
|
+
sys.stdout.flush()
|
|
45
|
+
if finished_branches.value == n:
|
|
46
|
+
break
|
|
47
|
+
time.sleep(0.1)
|
|
48
|
+
except Exception:
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _branch_worker(branch_number, shared_list, finished_branches, shared_progress):
|
|
32
53
|
"""Entry point for each worker process. Catches exceptions so they are logged
|
|
33
54
|
rather than silently swallowed by the multiprocessing machinery."""
|
|
34
55
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
35
56
|
try:
|
|
36
|
-
_run_branch(branch_number, shared_list)
|
|
57
|
+
_run_branch(branch_number, shared_list, shared_progress)
|
|
58
|
+
shared_progress[branch_number] = 100
|
|
59
|
+
finished_branches.value += 1
|
|
37
60
|
except Exception:
|
|
38
61
|
logging.exception("Branch worker %d crashed", branch_number)
|
|
39
62
|
|
|
40
63
|
|
|
41
|
-
def _run_branch(branch_number, shared_list):
|
|
64
|
+
def _run_branch(branch_number, shared_list, shared_progress):
|
|
42
65
|
"""Run all phonons for one polarization branch and push results to shared_list.
|
|
43
66
|
|
|
44
67
|
Each call processes NUMBER_OF_PARTICLES phonons sampled uniformly across the
|
|
@@ -47,7 +70,7 @@ def _run_branch(branch_number, shared_list):
|
|
|
47
70
|
flight.thermal_conductivity over all phonons approximates the branch integral
|
|
48
71
|
in Phys. Rev. 132 2461 (1963).
|
|
49
72
|
|
|
50
|
-
Branch
|
|
73
|
+
Branch 2 (TA) drives the shared progress bar; the other two run silently.
|
|
51
74
|
Results are appended to shared_list as a dict of dump_data() payloads so that
|
|
52
75
|
the main process can merge them with the standard read_data() mechanism.
|
|
53
76
|
"""
|
|
@@ -76,11 +99,13 @@ def _run_branch(branch_number, shared_list):
|
|
|
76
99
|
scatter_maps = ScatteringMap()
|
|
77
100
|
|
|
78
101
|
total_thermal_conductivity = 0.0
|
|
79
|
-
|
|
102
|
+
last_pct = -1
|
|
80
103
|
|
|
81
104
|
for index in range(cf.number_of_particles):
|
|
82
|
-
|
|
83
|
-
|
|
105
|
+
pct = 100 * index // cf.number_of_particles
|
|
106
|
+
if pct > last_pct:
|
|
107
|
+
shared_progress[branch_number] = pct
|
|
108
|
+
last_pct = pct
|
|
84
109
|
|
|
85
110
|
# Mid-point k-vector and integration weight for this phonon:
|
|
86
111
|
k_vector = (material.dispersion[index + 1, 0] + material.dispersion[index, 0]) / 2
|
|
@@ -101,8 +126,8 @@ def _run_branch(branch_number, shared_list):
|
|
|
101
126
|
flight.thermal_conductivity = (1 / (6 * math.pi**2)) * c_p * phonon.speed**2 * mean_relax_time * k_vector**2 * d_k_vector
|
|
102
127
|
total_thermal_conductivity += flight.thermal_conductivity
|
|
103
128
|
|
|
104
|
-
general_stats.save_particle_data(phonon)
|
|
105
|
-
general_stats.save_flight_data(flight)
|
|
129
|
+
general_stats.save_particle_data(phonon, SimulationMode.PHONON_MFP_SAMPLING)
|
|
130
|
+
general_stats.save_flight_data(flight, SimulationMode.PHONON_MFP_SAMPLING)
|
|
106
131
|
|
|
107
132
|
if index < cf.output_trajectories_of_first:
|
|
108
133
|
path_stats.save_particle_path(flight)
|
|
@@ -119,24 +144,32 @@ def _run_branch(branch_number, shared_list):
|
|
|
119
144
|
})
|
|
120
145
|
|
|
121
146
|
|
|
122
|
-
def main(input_file,
|
|
147
|
+
def main(input_file, mode: SimulationMode):
|
|
123
148
|
"""Integrate the phonon dispersion over all three branches in parallel to get
|
|
124
149
|
the bulk thermal conductivity via the MFP-sampling method."""
|
|
125
150
|
|
|
126
151
|
print(f'Mean free path sampling of {Fore.GREEN}{cf.output_folder_name}{Style.RESET_ALL}')
|
|
127
152
|
start_time = time.time()
|
|
128
153
|
|
|
129
|
-
|
|
130
|
-
|
|
154
|
+
branch_names = get_media_class(cf.media).branch_names
|
|
155
|
+
n_branches = len(branch_names)
|
|
156
|
+
|
|
157
|
+
# Spawn one worker process per polarization branch, all running concurrently:
|
|
131
158
|
manager = multiprocessing.Manager()
|
|
132
159
|
shared_list = manager.list()
|
|
160
|
+
finished_branches = manager.Value('i', 0)
|
|
161
|
+
shared_progress = manager.list([0] * n_branches)
|
|
133
162
|
|
|
134
163
|
processes = [
|
|
135
|
-
multiprocessing.Process(target=_branch_worker, args=(branch_number, shared_list))
|
|
136
|
-
for branch_number in range(
|
|
164
|
+
multiprocessing.Process(target=_branch_worker, args=(branch_number, shared_list, finished_branches, shared_progress))
|
|
165
|
+
for branch_number in range(n_branches - 1, -1, -1)
|
|
137
166
|
]
|
|
138
167
|
for process in processes:
|
|
139
168
|
process.start()
|
|
169
|
+
|
|
170
|
+
display_process = multiprocessing.Process(target=display_all_progress, args=(shared_progress, finished_branches, branch_names))
|
|
171
|
+
display_process.start()
|
|
172
|
+
|
|
140
173
|
try:
|
|
141
174
|
for process in processes:
|
|
142
175
|
process.join()
|
|
@@ -145,6 +178,9 @@ def main(input_file, particle_type):
|
|
|
145
178
|
process.terminate()
|
|
146
179
|
raise
|
|
147
180
|
|
|
181
|
+
display_process.join(timeout=3)
|
|
182
|
+
display_process.terminate()
|
|
183
|
+
|
|
148
184
|
# Merge per-branch results into single data structures:
|
|
149
185
|
scatter_stats = ScatteringData()
|
|
150
186
|
general_stats = GeneralData()
|
|
@@ -172,7 +208,7 @@ def main(input_file, particle_type):
|
|
|
172
208
|
os.chdir("Results/" + cf.output_folder_name)
|
|
173
209
|
|
|
174
210
|
# Save data and generate plots:
|
|
175
|
-
general_stats.write_into_files()
|
|
211
|
+
general_stats.write_into_files(mode)
|
|
176
212
|
scatter_stats.write_into_files()
|
|
177
213
|
segment_stats.write_into_files()
|
|
178
214
|
if cf.output_scattering_map:
|
|
@@ -185,11 +221,11 @@ def main(input_file, particle_type):
|
|
|
185
221
|
np.savetxt("Data/Thermal conductivity from MFP.csv", np.array([total_thermal_conductivity]), fmt='%2.4e', header="K [W/mK]", encoding='utf-8')
|
|
186
222
|
|
|
187
223
|
sys.stdout.write("\rAnalyzing the data...")
|
|
188
|
-
plot_data(
|
|
224
|
+
plot_data(mode, cf)
|
|
189
225
|
|
|
190
226
|
output_general_information(start_time)
|
|
191
227
|
output_scattering_information(scatter_stats)
|
|
192
|
-
output_parameter_warnings(
|
|
228
|
+
output_parameter_warnings(mode)
|
|
193
229
|
sys.stdout.write(f'\rSee the results in {Fore.GREEN}Results/{cf.output_folder_name}{Style.RESET_ALL}\n')
|
|
194
230
|
sys.stdout.write(f"\rThermal conductivity = {Fore.GREEN}{total_thermal_conductivity:.5f}{Style.RESET_ALL} W/m·K\n")
|
|
195
231
|
sys.stdout.write(f"\r{Fore.BLUE}Thank you for using FreePATHS{Style.RESET_ALL}\n\n")
|
|
@@ -8,6 +8,7 @@ import signal
|
|
|
8
8
|
import multiprocessing
|
|
9
9
|
import traceback
|
|
10
10
|
import logging
|
|
11
|
+
import numpy as np
|
|
11
12
|
from colorama import Fore, Style
|
|
12
13
|
|
|
13
14
|
# Modules:
|
|
@@ -16,13 +17,13 @@ from freepaths.run_particle import run_particle
|
|
|
16
17
|
from freepaths.electron import Electron
|
|
17
18
|
from freepaths.phonon import Phonon
|
|
18
19
|
from freepaths.flight import Flight
|
|
19
|
-
from freepaths.
|
|
20
|
+
from freepaths.options import SimulationMode
|
|
20
21
|
from freepaths.data import ScatteringData, GeneralData, SegmentData, PathData, TriangleScatteringData
|
|
21
22
|
from freepaths.post_computations import ElectronPostComputation
|
|
22
23
|
from freepaths.progress import Progress
|
|
23
24
|
from freepaths.materials import Si, SiC, Graphite, SiGe
|
|
24
25
|
from freepaths.maps import ScatteringMap, ThermalMaps
|
|
25
|
-
from freepaths.output_info import output_general_information, output_scattering_information, output_parameter_warnings
|
|
26
|
+
from freepaths.output_info import output_general_information, output_scattering_information, output_parameter_warnings, output_electron_information
|
|
26
27
|
from freepaths.animation import create_animation
|
|
27
28
|
from freepaths.output_plots import plot_data
|
|
28
29
|
from freepaths.scattering import surface_scattering
|
|
@@ -34,7 +35,7 @@ class ParticleSimulator:
|
|
|
34
35
|
It is meant to be used as a worker for multiprocessing
|
|
35
36
|
"""
|
|
36
37
|
|
|
37
|
-
def __init__(self, worker_id,
|
|
38
|
+
def __init__(self, worker_id, mode: SimulationMode, total_particles, shared_list, output_trajectories_of):
|
|
38
39
|
|
|
39
40
|
# Initialize the material:
|
|
40
41
|
if cf.media == "Si":
|
|
@@ -52,7 +53,7 @@ class ParticleSimulator:
|
|
|
52
53
|
# Save some general information about the process:
|
|
53
54
|
self.worker_id = worker_id
|
|
54
55
|
self.total_particles = total_particles
|
|
55
|
-
self.
|
|
56
|
+
self.mode = mode
|
|
56
57
|
self.result_queue = shared_list
|
|
57
58
|
self.creation_time = time.time()
|
|
58
59
|
self.output_trajectories_of = output_trajectories_of
|
|
@@ -65,7 +66,7 @@ class ParticleSimulator:
|
|
|
65
66
|
self.path_stats = PathData()
|
|
66
67
|
self.places_stats = TriangleScatteringData()
|
|
67
68
|
self.scatter_maps = ScatteringMap()
|
|
68
|
-
self.thermal_maps = ThermalMaps()
|
|
69
|
+
self.thermal_maps = ThermalMaps() if mode is SimulationMode.PHONON_TRACING else None
|
|
69
70
|
|
|
70
71
|
self.total_thermal_conductivity = 0.0
|
|
71
72
|
self.interfaces_transmission_specular = 0
|
|
@@ -74,10 +75,10 @@ class ParticleSimulator:
|
|
|
74
75
|
|
|
75
76
|
def simulate_particle(self, index):
|
|
76
77
|
# Initiate a particle and its flight:
|
|
77
|
-
if self.
|
|
78
|
-
particle = Phonon(self.material)
|
|
79
|
-
else:
|
|
78
|
+
if self.mode is SimulationMode.ELECTRON:
|
|
80
79
|
particle = Electron(self.material)
|
|
80
|
+
else:
|
|
81
|
+
particle = Phonon(self.material)
|
|
81
82
|
flight = Flight(particle)
|
|
82
83
|
particle.flight = flight
|
|
83
84
|
|
|
@@ -85,8 +86,8 @@ class ParticleSimulator:
|
|
|
85
86
|
run_particle(particle, flight, self.scatter_stats, self.places_stats, self.segment_stats, self.thermal_maps, self.scatter_maps, self.material)
|
|
86
87
|
|
|
87
88
|
# Record the properties returned for this particle:
|
|
88
|
-
self.general_stats.save_particle_data(particle)
|
|
89
|
-
self.general_stats.save_flight_data(flight)
|
|
89
|
+
self.general_stats.save_particle_data(particle, self.mode)
|
|
90
|
+
self.general_stats.save_flight_data(flight, self.mode)
|
|
90
91
|
|
|
91
92
|
# Record trajectories of the first N particles:
|
|
92
93
|
if index < self.output_trajectories_of:
|
|
@@ -118,7 +119,7 @@ class ParticleSimulator:
|
|
|
118
119
|
'segment_stats': self.segment_stats.dump_data(),
|
|
119
120
|
'path_stats': self.path_stats.dump_data(),
|
|
120
121
|
'scatter_maps': self.scatter_maps.dump_data(),
|
|
121
|
-
'thermal_maps': self.thermal_maps.dump_data(),
|
|
122
|
+
'thermal_maps': self.thermal_maps.dump_data() if self.thermal_maps is not None else {},
|
|
122
123
|
'execution_time': time.time() - self.creation_time,
|
|
123
124
|
}
|
|
124
125
|
|
|
@@ -126,11 +127,11 @@ class ParticleSimulator:
|
|
|
126
127
|
self.result_queue.append(collected_data)
|
|
127
128
|
|
|
128
129
|
|
|
129
|
-
def worker_process(worker_id,
|
|
130
|
+
def worker_process(worker_id, mode: SimulationMode, total_particles, shared_list, output_trajectories_of, finished_workers):
|
|
130
131
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
131
132
|
try:
|
|
132
133
|
# Create a particle simulator and run the simulation:
|
|
133
|
-
simulator = ParticleSimulator(worker_id,
|
|
134
|
+
simulator = ParticleSimulator(worker_id, mode, total_particles, shared_list, output_trajectories_of)
|
|
134
135
|
simulator.simulate_particles(render_progress=1 if worker_id == 0 else 0)
|
|
135
136
|
|
|
136
137
|
# Declare that the calculation is finished:
|
|
@@ -161,7 +162,7 @@ def display_workers_finished(finished_workers):
|
|
|
161
162
|
pass
|
|
162
163
|
|
|
163
164
|
|
|
164
|
-
def main(input_file,
|
|
165
|
+
def main(input_file, mode: SimulationMode):
|
|
165
166
|
"""
|
|
166
167
|
This is the main function.
|
|
167
168
|
It should be used to simulate electron paths or phonon paths at low temperatures.
|
|
@@ -192,7 +193,7 @@ def main(input_file, particle_type):
|
|
|
192
193
|
for i in range(cf.num_workers):
|
|
193
194
|
worker_particles = workload_per_worker + (1 if i < remaining_particles else 0)
|
|
194
195
|
output_trajectory_of = output_trajectories_per_worker + (1 if i < remaining_output_trajectories else 0)
|
|
195
|
-
process = multiprocessing.Process(target=worker_process, args=(i,
|
|
196
|
+
process = multiprocessing.Process(target=worker_process, args=(i, mode, worker_particles, shared_list, output_trajectory_of, finished_workers))
|
|
196
197
|
processes.append(process)
|
|
197
198
|
process.start()
|
|
198
199
|
|
|
@@ -222,7 +223,7 @@ def main(input_file, particle_type):
|
|
|
222
223
|
segment_stats = SegmentData()
|
|
223
224
|
path_stats = PathData()
|
|
224
225
|
scatter_maps = ScatteringMap()
|
|
225
|
-
thermal_maps = ThermalMaps()
|
|
226
|
+
thermal_maps = ThermalMaps() if mode is SimulationMode.PHONON_TRACING else None
|
|
226
227
|
|
|
227
228
|
# Collect the results:
|
|
228
229
|
sys.stdout.write('\nCollecting data from workers...\r')
|
|
@@ -243,7 +244,8 @@ def main(input_file, particle_type):
|
|
|
243
244
|
segment_stats.read_data(collected_data['segment_stats'])
|
|
244
245
|
path_stats.read_data(collected_data['path_stats'])
|
|
245
246
|
scatter_maps.read_data(collected_data['scatter_maps'])
|
|
246
|
-
thermal_maps
|
|
247
|
+
if thermal_maps is not None:
|
|
248
|
+
thermal_maps.read_data(collected_data['thermal_maps'])
|
|
247
249
|
execution_time_list.append(collected_data['execution_time'])
|
|
248
250
|
|
|
249
251
|
# Give some info about the variability in the worker calculation time:
|
|
@@ -251,17 +253,13 @@ def main(input_file, particle_type):
|
|
|
251
253
|
sys.stdout.write(f'Shortest process execution time: {round(min(execution_time_list))}s\n')
|
|
252
254
|
sys.stdout.write(f'Longest process execution time: {round(max(execution_time_list))}s\n')
|
|
253
255
|
|
|
254
|
-
# Check if the total number of returned particles from the workers corresponds with the number of particles to be simulated:
|
|
255
|
-
if len(general_stats.initial_angles) != cf.number_of_particles:
|
|
256
|
-
sys.stdout.write(f'WARNING: {cf.number_of_particles} were meant to be simulated but only {len(general_stats.initial_angles)} particles were collected from the workers\n')
|
|
257
|
-
|
|
258
256
|
# Run thermal calculations:
|
|
259
|
-
thermal_maps
|
|
260
|
-
|
|
261
|
-
|
|
257
|
+
if thermal_maps is not None:
|
|
258
|
+
thermal_maps.calculate_thermal_conductivity()
|
|
259
|
+
thermal_maps.calculate_heat_flux_modulus()
|
|
262
260
|
|
|
263
261
|
# Run calculations on electrons if needed:
|
|
264
|
-
if
|
|
262
|
+
if mode is SimulationMode.ELECTRON:
|
|
265
263
|
|
|
266
264
|
electron_computations = ElectronPostComputation(general_stats)
|
|
267
265
|
electron_computations.compute()
|
|
@@ -276,18 +274,36 @@ def main(input_file, particle_type):
|
|
|
276
274
|
shutil.copy(input_file, "Results/" + cf.output_folder_name)
|
|
277
275
|
os.chdir("Results/" + cf.output_folder_name)
|
|
278
276
|
|
|
277
|
+
# Try to load pre-computed phonon thermal conductivity to enable ZT calculation:
|
|
278
|
+
if mode is SimulationMode.ELECTRON:
|
|
279
|
+
kappa_ph = None
|
|
280
|
+
if os.path.isfile("Data/Thermal conductivity from MFP.csv"):
|
|
281
|
+
try:
|
|
282
|
+
kappa_ph = float(np.genfromtxt("Data/Thermal conductivity from MFP.csv", skip_header=1))
|
|
283
|
+
except Exception:
|
|
284
|
+
pass
|
|
285
|
+
elif os.path.isfile("Data/Average thermal conductivity.csv"):
|
|
286
|
+
try:
|
|
287
|
+
data = np.genfromtxt("Data/Average thermal conductivity.csv", delimiter=',', skip_header=1)
|
|
288
|
+
kappa_ph = float(np.atleast_1d(data)[1]) # column 1 = av_mat_tc
|
|
289
|
+
except Exception:
|
|
290
|
+
pass
|
|
291
|
+
if kappa_ph is not None:
|
|
292
|
+
electron_computations.compute_zt(kappa_ph)
|
|
293
|
+
|
|
279
294
|
# Save data into files:
|
|
280
295
|
sys.stdout.write("\rSaving raw data...")
|
|
281
|
-
general_stats.write_into_files()
|
|
296
|
+
general_stats.write_into_files(mode)
|
|
282
297
|
scatter_stats.write_into_files()
|
|
283
298
|
if cf.holes:
|
|
284
299
|
places_stats.write_into_files()
|
|
285
300
|
segment_stats.write_into_files()
|
|
286
|
-
thermal_maps
|
|
301
|
+
if thermal_maps is not None:
|
|
302
|
+
thermal_maps.write_into_files(mode)
|
|
287
303
|
if cf.output_scattering_map:
|
|
288
304
|
scatter_maps.write_into_files()
|
|
289
305
|
path_stats.write_into_files()
|
|
290
|
-
if
|
|
306
|
+
if mode is SimulationMode.ELECTRON:
|
|
291
307
|
electron_computations.write_into_file()
|
|
292
308
|
|
|
293
309
|
# Generate animation of particle paths:
|
|
@@ -296,12 +312,14 @@ def main(input_file, particle_type):
|
|
|
296
312
|
|
|
297
313
|
# Analyze and plot the data:
|
|
298
314
|
sys.stdout.write("\rAnalyzing the data...")
|
|
299
|
-
plot_data(
|
|
315
|
+
plot_data(mode, cf)
|
|
300
316
|
|
|
301
317
|
# Output general information:
|
|
302
318
|
output_general_information(start_time)
|
|
303
319
|
output_scattering_information(scatter_stats)
|
|
304
|
-
|
|
320
|
+
if mode is SimulationMode.ELECTRON:
|
|
321
|
+
output_electron_information(electron_computations)
|
|
322
|
+
output_parameter_warnings(mode)
|
|
305
323
|
|
|
306
324
|
sys.stdout.write(f'\rSee the results in {Fore.GREEN}Results/{cf.output_folder_name}{Style.RESET_ALL}\n')
|
|
307
325
|
sys.stdout.write(f"\r{Fore.BLUE}Thank you for using FreePATHS{Style.RESET_ALL}\n\n")
|