freepaths 2.2.4__tar.gz → 2.3.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.
- {freepaths-2.2.4 → freepaths-2.3.1}/PKG-INFO +7 -3
- {freepaths-2.2.4 → freepaths-2.3.1}/README.md +6 -2
- freepaths-2.3.1/freepaths/__main__.py +64 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/config.py +1 -2
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/data.py +6 -4
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/default_config.py +7 -7
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/main_tracing.py +21 -10
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/maps.py +4 -3
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/output_info.py +43 -34
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/output_plots.py +35 -25
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths.egg-info/PKG-INFO +7 -3
- freepaths-2.2.4/freepaths/__main__.py +0 -42
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/animation.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/electron.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/flight.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/interface_smmm.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/main_mfp_sampling.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/materials.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/move.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/output_structure.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/particle.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/particle_types.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/phonon.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/post_computations.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/progress.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/run_particle.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/scatterers.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/scattering.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/scattering_interfaces.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/scattering_primitives.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/scattering_semicircle.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/scattering_types.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths/sources.py +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths.egg-info/SOURCES.txt +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths.egg-info/dependency_links.txt +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths.egg-info/entry_points.txt +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths.egg-info/requires.txt +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/freepaths.egg-info/top_level.txt +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/pyproject.toml +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/setup.cfg +0 -0
- {freepaths-2.2.4 → freepaths-2.3.1}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: freepaths
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.1
|
|
4
4
|
Summary: Phonon Monte Carlo simulator
|
|
5
5
|
Home-page: https://github.com/anufrievroman/freepaths
|
|
6
6
|
Author: Roman Anufriev
|
|
@@ -54,9 +54,13 @@ FreePATHS is a command line application, so it runs inside Linux, MacOS, or Wind
|
|
|
54
54
|
|
|
55
55
|
There are two modes of using the program. Main mode traces a large number of phonons through a structure and collects statistics about their paths. The MFP sampling mode measures phonon mean free paths using a small number of phonons and calculates the thermal conductivity by integrating phonon dispersion.
|
|
56
56
|
|
|
57
|
-
###
|
|
57
|
+
### Getting started
|
|
58
58
|
|
|
59
|
-
If you
|
|
59
|
+
If you run `freepaths` without any arguments, the program will print a usage summary. To run a demo simulation with default parameters, use the `--demo` flag:
|
|
60
|
+
|
|
61
|
+
`freepaths --demo`
|
|
62
|
+
|
|
63
|
+
Example input files are available in the [examples](https://github.com/anufrievroman/freepaths/tree/master/examples) folder.
|
|
60
64
|
|
|
61
65
|
### Main mode
|
|
62
66
|
|
|
@@ -18,9 +18,13 @@ FreePATHS is a command line application, so it runs inside Linux, MacOS, or Wind
|
|
|
18
18
|
|
|
19
19
|
There are two modes of using the program. Main mode traces a large number of phonons through a structure and collects statistics about their paths. The MFP sampling mode measures phonon mean free paths using a small number of phonons and calculates the thermal conductivity by integrating phonon dispersion.
|
|
20
20
|
|
|
21
|
-
###
|
|
21
|
+
### Getting started
|
|
22
22
|
|
|
23
|
-
If you
|
|
23
|
+
If you run `freepaths` without any arguments, the program will print a usage summary. To run a demo simulation with default parameters, use the `--demo` flag:
|
|
24
|
+
|
|
25
|
+
`freepaths --demo`
|
|
26
|
+
|
|
27
|
+
Example input files are available in the [examples](https://github.com/anufrievroman/freepaths/tree/master/examples) folder.
|
|
24
28
|
|
|
25
29
|
### Main mode
|
|
26
30
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""FreePATHS - Free Phonon and THermal Simulator"""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import argparse
|
|
5
|
+
import colorama
|
|
6
|
+
from colorama import Fore, Style
|
|
7
|
+
|
|
8
|
+
from freepaths.particle_types import ParticleType
|
|
9
|
+
|
|
10
|
+
__version__ = "2.3.1"
|
|
11
|
+
|
|
12
|
+
colorama.init()
|
|
13
|
+
|
|
14
|
+
# Parse user arguments:
|
|
15
|
+
parser = argparse.ArgumentParser(
|
|
16
|
+
prog = 'FreePATHS',
|
|
17
|
+
description = 'Phonon Monte Carlo simulator',
|
|
18
|
+
epilog = 'For more information, examples, and tutorials, visit: https://anufrievroman.gitbook.io/freepaths'
|
|
19
|
+
)
|
|
20
|
+
parser.add_argument('input_file', nargs='?', default=None, help='The input file')
|
|
21
|
+
parser.add_argument("-v", "--version", action="version", version=f"FreePATHS {__version__}")
|
|
22
|
+
parser.add_argument("-s", "--sampling", help="Run in phonon MFP sampling mode", action="store_true")
|
|
23
|
+
parser.add_argument("-e", "--electron", help="Run simulation for electrons", action="store_true")
|
|
24
|
+
parser.add_argument("--demo", help="Run a demo simulation with default parameters", action="store_true")
|
|
25
|
+
args = parser.parse_args()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def run():
|
|
29
|
+
"""Run the program depending on the mode"""
|
|
30
|
+
print(f"\n{Fore.BLUE}FreePATHS v{__version__}{Style.RESET_ALL}")
|
|
31
|
+
|
|
32
|
+
if not args.input_file and not args.demo:
|
|
33
|
+
print("\nUsage:")
|
|
34
|
+
print(f" freepaths <input_file> run phonon simulation")
|
|
35
|
+
print(f" freepaths <input_file> -e run electron simulation")
|
|
36
|
+
print(f" freepaths <input_file> -s run in MFP sampling mode")
|
|
37
|
+
print(f" freepaths --demo run a demo with default parameters")
|
|
38
|
+
print(f" freepaths --version show version")
|
|
39
|
+
print(f"\nExample input files:")
|
|
40
|
+
print(f" https://github.com/anufrievroman/freepaths/tree/master/examples")
|
|
41
|
+
print(f"\nDocumentation:")
|
|
42
|
+
print(f" https://anufrievroman.gitbook.io/freepaths")
|
|
43
|
+
sys.exit(0)
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
if args.sampling:
|
|
47
|
+
import freepaths.main_mfp_sampling
|
|
48
|
+
freepaths.main_mfp_sampling.main(args.input_file, ParticleType.PHONON)
|
|
49
|
+
|
|
50
|
+
elif args.electron:
|
|
51
|
+
import freepaths.main_tracing
|
|
52
|
+
freepaths.main_tracing.main(args.input_file, ParticleType.ELECTRON)
|
|
53
|
+
|
|
54
|
+
else:
|
|
55
|
+
import freepaths.main_tracing
|
|
56
|
+
freepaths.main_tracing.main(args.input_file, ParticleType.PHONON)
|
|
57
|
+
|
|
58
|
+
except KeyboardInterrupt:
|
|
59
|
+
print(f"\n{Fore.RED}Simulation interrupted by user.{Style.RESET_ALL}")
|
|
60
|
+
sys.exit(0)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
run()
|
|
@@ -26,6 +26,7 @@ parser = argparse.ArgumentParser(prog='FreePATHS', description='Monte Carlo simu
|
|
|
26
26
|
parser.add_argument('input_file', nargs='?', default=None, help='The input file')
|
|
27
27
|
parser.add_argument("-s", "--sampling", help="Run in MFP sampling mode", action="store_true")
|
|
28
28
|
parser.add_argument("-e", "--electron", help="Run with electrons", action="store_true")
|
|
29
|
+
parser.add_argument("--demo", help="Run a demo simulation with default parameters", action="store_true")
|
|
29
30
|
args = parser.parse_args()
|
|
30
31
|
|
|
31
32
|
|
|
@@ -36,8 +37,6 @@ if args.input_file:
|
|
|
36
37
|
except FileNotFoundError:
|
|
37
38
|
logging.error("Input file does not exist. Check if you are in the right folder and the file name is correct.")
|
|
38
39
|
sys.exit()
|
|
39
|
-
else:
|
|
40
|
-
logging.warning("You provided no input file, so we will run a demo simulation:")
|
|
41
40
|
|
|
42
41
|
|
|
43
42
|
class Config:
|
|
@@ -204,10 +204,12 @@ class ScatteringData(Data):
|
|
|
204
204
|
def write_into_files(self):
|
|
205
205
|
"""Write data into a file"""
|
|
206
206
|
filename = "Data/Scattering events statistics.csv"
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
self.
|
|
210
|
-
self.
|
|
207
|
+
n = cf.number_of_length_segments
|
|
208
|
+
data = np.vstack((self.wall_diffuse[:n], self.wall_specular[:n], self.top_diffuse[:n], self.top_specular[:n],
|
|
209
|
+
self.hole_diffuse[:n], self.hole_specular[:n], self.hot_side[:n], self.internal[:n],
|
|
210
|
+
self.pillar_diffuse[:n], self.pillar_specular[:n],
|
|
211
|
+
self.interfaces_diffuse[:n], self.interfaces_specular[:n],
|
|
212
|
+
self.interfaces_transmission_diffuse[:n], self.interfaces_transmission_specular[:n])).T
|
|
211
213
|
header = ("Sidewalls diffuse, Sidewalls specular, Top & bottom diffuse, Top & bottom specular, "
|
|
212
214
|
"Holes diffuse, Holes specular, Hot side, Internal, Pillars diffuse, Pillars specular, "
|
|
213
215
|
"Interfaces diffuse, Interfaces specular, Interfaces transmission diffuse, Interfaces transmission specular")
|
|
@@ -7,18 +7,18 @@ from freepaths.sources import Source
|
|
|
7
7
|
|
|
8
8
|
# General parameters:
|
|
9
9
|
OUTPUT_FOLDER_NAME = "Si nanowire at 300 K"
|
|
10
|
-
NUMBER_OF_PARTICLES =
|
|
10
|
+
NUMBER_OF_PARTICLES = 10000
|
|
11
11
|
NUMBER_OF_NODES = 400
|
|
12
12
|
T = 300
|
|
13
13
|
OUTPUT_SCATTERING_MAP = False
|
|
14
|
-
OUTPUT_TRAJECTORIES_OF_FIRST =
|
|
14
|
+
OUTPUT_TRAJECTORIES_OF_FIRST = 20
|
|
15
15
|
OUTPUT_STRUCTURE_COLOR = "#F0F0F0"
|
|
16
16
|
NUMBER_OF_LENGTH_SEGMENTS = 10
|
|
17
17
|
LOW_MEMORY_USAGE = False
|
|
18
18
|
|
|
19
19
|
# Time parameters:
|
|
20
|
-
TIMESTEP =
|
|
21
|
-
NUMBER_OF_TIMESTEPS =
|
|
20
|
+
TIMESTEP = 2e-12
|
|
21
|
+
NUMBER_OF_TIMESTEPS = 200000
|
|
22
22
|
NUMBER_OF_VIRTUAL_TIMESTEPS = NUMBER_OF_TIMESTEPS * 3
|
|
23
23
|
NUMBER_OF_TIMEFRAMES = 8
|
|
24
24
|
NUMBER_OF_STABILIZATION_TIMEFRAMES = 5
|
|
@@ -38,8 +38,8 @@ OUTPUT_PATH_ANIMATION = False
|
|
|
38
38
|
OUTPUT_ANIMATION_FPS = 24
|
|
39
39
|
|
|
40
40
|
# Map & profiles parameters:
|
|
41
|
-
NUMBER_OF_PIXELS_X =
|
|
42
|
-
NUMBER_OF_PIXELS_Y =
|
|
41
|
+
NUMBER_OF_PIXELS_X = 7
|
|
42
|
+
NUMBER_OF_PIXELS_Y = 67
|
|
43
43
|
IGNORE_FAULTY_PARTICLES = False
|
|
44
44
|
|
|
45
45
|
# Material parameters:
|
|
@@ -53,7 +53,7 @@ GRAY_APPROXIMATION_MFP = None
|
|
|
53
53
|
|
|
54
54
|
# System dimensions [m]:
|
|
55
55
|
THICKNESS = 150e-9
|
|
56
|
-
WIDTH =
|
|
56
|
+
WIDTH = 200e-9
|
|
57
57
|
LENGTH = 2000e-9
|
|
58
58
|
IS_TWO_DIMENSIONAL_MATERIAL = False
|
|
59
59
|
INCLUDE_RIGHT_SIDEWALL = True
|
|
@@ -4,6 +4,7 @@ import os
|
|
|
4
4
|
import sys
|
|
5
5
|
import time
|
|
6
6
|
import shutil
|
|
7
|
+
import signal
|
|
7
8
|
import multiprocessing
|
|
8
9
|
import traceback
|
|
9
10
|
import logging
|
|
@@ -126,6 +127,7 @@ class ParticleSimulator:
|
|
|
126
127
|
|
|
127
128
|
|
|
128
129
|
def worker_process(worker_id, particle_type: ParticleType,total_particles, shared_list, output_trajectories_of, finished_workers):
|
|
130
|
+
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
129
131
|
try:
|
|
130
132
|
# Create a particle simulator and run the simulation:
|
|
131
133
|
simulator = ParticleSimulator(worker_id, particle_type, total_particles, shared_list, output_trajectories_of)
|
|
@@ -146,13 +148,17 @@ def worker_process(worker_id, particle_type: ParticleType,total_particles, share
|
|
|
146
148
|
|
|
147
149
|
def display_workers_finished(finished_workers):
|
|
148
150
|
""" Print out the number of active workers"""
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
151
|
+
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
152
|
+
try:
|
|
153
|
+
while True:
|
|
154
|
+
text_to_display = f' Processes finished: {finished_workers.value}/{cf.num_workers}'
|
|
155
|
+
sys.stdout.write(text_to_display)
|
|
156
|
+
sys.stdout.write(f'\033[{len(text_to_display)}D') # move cursor back
|
|
157
|
+
sys.stdout.flush()
|
|
158
|
+
if finished_workers.value == cf.num_workers: break
|
|
159
|
+
time.sleep(0.3)
|
|
160
|
+
except Exception:
|
|
161
|
+
pass
|
|
156
162
|
|
|
157
163
|
|
|
158
164
|
def main(input_file, particle_type):
|
|
@@ -196,8 +202,13 @@ def main(input_file, particle_type):
|
|
|
196
202
|
|
|
197
203
|
# Wait for all processes to finish:
|
|
198
204
|
# Note that join is not called on worker_count_process because we do not want to wait for it to finish
|
|
199
|
-
|
|
200
|
-
process
|
|
205
|
+
try:
|
|
206
|
+
for process in processes:
|
|
207
|
+
process.join()
|
|
208
|
+
except KeyboardInterrupt:
|
|
209
|
+
for process in processes:
|
|
210
|
+
process.terminate()
|
|
211
|
+
raise
|
|
201
212
|
|
|
202
213
|
# Wait for the worker count to finish but continue after 3 seconds:
|
|
203
214
|
worker_count_process.join(timeout=3)
|
|
@@ -288,7 +299,7 @@ def main(input_file, particle_type):
|
|
|
288
299
|
# Output general information:
|
|
289
300
|
output_general_information(start_time)
|
|
290
301
|
output_scattering_information(scatter_stats)
|
|
291
|
-
output_parameter_warnings()
|
|
302
|
+
output_parameter_warnings(particle_type)
|
|
292
303
|
|
|
293
304
|
sys.stdout.write(f'\rSee the results in {Fore.GREEN}Results/{cf.output_folder_name}{Style.RESET_ALL}\n')
|
|
294
305
|
sys.stdout.write(f"\r{Fore.BLUE}Thank you for using FreePATHS{Style.RESET_ALL}\n\n")
|
|
@@ -208,9 +208,10 @@ class ThermalMaps(Maps):
|
|
|
208
208
|
slope, _ = np.polyfit(coordinates_y, self.temperature_profile_y[:, timeframe_number], 1)
|
|
209
209
|
grad_T = -slope
|
|
210
210
|
|
|
211
|
-
# Average heat flux
|
|
212
|
-
|
|
213
|
-
|
|
211
|
+
# Average heat flux (skip first pixel — it has a spurious spike due to
|
|
212
|
+
# rethermalization being applied before map recording in the time-step loop):
|
|
213
|
+
J_effective = np.mean(self.effective_heat_flux_profile_y[1:, timeframe_number])
|
|
214
|
+
J_material = np.mean(self.material_heat_flux_profile_y[1:, timeframe_number])
|
|
214
215
|
|
|
215
216
|
# By definition, J = -K * grad(T), so:
|
|
216
217
|
if grad_T != 0:
|
|
@@ -39,13 +39,18 @@ def output_scattering_information(scatter_stats):
|
|
|
39
39
|
"""Calculate and output general statistics on scattering events"""
|
|
40
40
|
|
|
41
41
|
# Calculate the percentage of different scattering events:
|
|
42
|
-
total = np.sum(scatter_stats.total)
|
|
43
42
|
total_wall = np.sum(scatter_stats.wall_diffuse) + np.sum(scatter_stats.wall_specular)
|
|
44
43
|
total_topbot = np.sum(scatter_stats.top_diffuse) + np.sum(scatter_stats.top_specular)
|
|
45
44
|
total_hole = np.sum(scatter_stats.hole_diffuse) + np.sum(scatter_stats.hole_specular)
|
|
46
45
|
total_pill = np.sum(scatter_stats.pillar_diffuse) + np.sum(scatter_stats.pillar_specular)
|
|
46
|
+
# total_interf counts ALL interface events (both reflection and transmission),
|
|
47
|
+
# because scattering_types.interfaces is set for both. total_transmission is
|
|
48
|
+
# a subset of total_interf, so must NOT be added separately to avoid double-counting.
|
|
47
49
|
total_interf = np.sum(scatter_stats.interfaces_diffuse) + np.sum(scatter_stats.interfaces_specular)
|
|
48
50
|
total_transmission = np.sum(scatter_stats.interfaces_transmission_specular) + np.sum(scatter_stats.interfaces_transmission_diffuse)
|
|
51
|
+
total_retherm = np.sum(scatter_stats.hot_side)
|
|
52
|
+
total_internal = np.sum(scatter_stats.internal)
|
|
53
|
+
total = total_wall + total_topbot + total_hole + total_pill + total_interf + total_retherm + total_internal
|
|
49
54
|
|
|
50
55
|
sc_on_walls = 100*(np.sum(scatter_stats.wall_diffuse) +
|
|
51
56
|
np.sum(scatter_stats.wall_specular)) / total
|
|
@@ -62,8 +67,8 @@ def output_scattering_information(scatter_stats):
|
|
|
62
67
|
sc_on_topbot_diff = 0
|
|
63
68
|
sc_on_topbot_spec = 0
|
|
64
69
|
|
|
65
|
-
retherm = 100*
|
|
66
|
-
internal = 100*
|
|
70
|
+
retherm = 100 * total_retherm / total
|
|
71
|
+
internal = 100 * total_internal / total
|
|
67
72
|
|
|
68
73
|
info = [
|
|
69
74
|
f'\n{sc_on_walls:.2f}% - scattering on side walls ',
|
|
@@ -100,45 +105,54 @@ def output_scattering_information(scatter_stats):
|
|
|
100
105
|
)
|
|
101
106
|
|
|
102
107
|
if cf.interfaces:
|
|
103
|
-
sc_on_interf = 100*
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
108
|
+
sc_on_interf = 100 * total_interf / total
|
|
109
|
+
sc_on_interf_diff = 100 * np.sum(scatter_stats.interfaces_diffuse) / total_interf
|
|
110
|
+
sc_on_interf_spec = 100 * np.sum(scatter_stats.interfaces_specular) / total_interf
|
|
111
|
+
trans_on_interf = 100 * total_transmission / total_interf
|
|
112
|
+
refl_on_interf = 100 * (total_interf - total_transmission) / total_interf
|
|
107
113
|
info.extend([
|
|
108
114
|
f'\n{sc_on_interf:.2f}% - scattering on interfaces ',
|
|
109
115
|
f'({sc_on_interf_diff:.2f}% - diffuse, ',
|
|
110
|
-
f'{sc_on_interf_spec:.2f}% - specular)'
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
trans_on_interf = 100 * (np.sum(scatter_stats.interfaces_transmission_diffuse) +
|
|
114
|
-
np.sum(scatter_stats.interfaces_transmission_specular)) / total_interf
|
|
115
|
-
trans_on_interf_diff = 100*np.sum(scatter_stats.interfaces_transmission_diffuse)/ total_transmission
|
|
116
|
-
trans_on_interf_spec = 100*np.sum(scatter_stats.interfaces_transmission_specular)/ total_transmission
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
info.extend([
|
|
120
|
-
f'\n{trans_on_interf:.2f}% - transmission through interfaces ',
|
|
121
|
-
f'({trans_on_interf_diff:.2f}% - diffuse, ',
|
|
122
|
-
f'{trans_on_interf_spec:.2f}% - specular)'
|
|
116
|
+
f'{sc_on_interf_spec:.2f}% - specular), ',
|
|
117
|
+
f'of which {trans_on_interf:.2f}% - transmission, ',
|
|
118
|
+
f'{refl_on_interf:.2f}% - reflection',
|
|
123
119
|
])
|
|
124
120
|
|
|
125
121
|
# Write the file:
|
|
126
122
|
with open("Information.txt", "a", encoding="utf-8") as file:
|
|
127
123
|
file.writelines(info)
|
|
128
124
|
|
|
125
|
+
# Warn if multiple scattering events co-occur in a single timestep:
|
|
126
|
+
total_timesteps_with_scattering = np.sum(scatter_stats.total)
|
|
127
|
+
if total_timesteps_with_scattering > 0:
|
|
128
|
+
multi_event_percentage = 100 * (total - total_timesteps_with_scattering) / total_timesteps_with_scattering
|
|
129
|
+
if multi_event_percentage > 5:
|
|
130
|
+
logging.warning(f"In {multi_event_percentage:.1f}% of scattering timesteps, more than one scattering "
|
|
131
|
+
f"mechanism fired simultaneously. Consider reducing TIMESTEP.")
|
|
129
132
|
|
|
130
|
-
|
|
133
|
+
|
|
134
|
+
def output_parameter_warnings(particle_type):
|
|
131
135
|
"""Check if parameters used for this simulation made sense considering the simulation results"""
|
|
132
136
|
|
|
133
|
-
|
|
137
|
+
from freepaths.particle_types import ParticleType
|
|
138
|
+
|
|
139
|
+
# These checks are only relevant for phonon simulations:
|
|
140
|
+
if particle_type is not ParticleType.ELECTRON:
|
|
141
|
+
travel_times = np.loadtxt("Data/All travel times.csv", encoding='utf-8')
|
|
142
|
+
|
|
143
|
+
total_time = cf.timestep * cf.number_of_timesteps
|
|
144
|
+
time_of_stabilization = cf.number_of_stabilization_timeframes * total_time / cf.number_of_timeframes
|
|
145
|
+
long_travel_times = travel_times[travel_times > time_of_stabilization]
|
|
146
|
+
percentage = (len(long_travel_times) / len(travel_times)) * 100
|
|
147
|
+
if percentage > 10:
|
|
148
|
+
logging.warning(f"Travel time of {percentage}% of particles was longer than the stabilization period.\n" +
|
|
149
|
+
"Increase stabilization period as the thermal conductivity might be incorrect.")
|
|
150
|
+
|
|
151
|
+
# Check how many particles reached the cold side during simulation:
|
|
134
152
|
travel_times = np.loadtxt("Data/All travel times.csv", encoding='utf-8')
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
percentage = (len(long_travel_times) / len(travel_times)) * 100
|
|
139
|
-
if percentage > 10:
|
|
140
|
-
logging.warning(f"Travel time of {percentage}% of particles was longer than the stabilization period.\n" +
|
|
141
|
-
"Increase stabilization period as the thermal conductivity might be incorrect.")
|
|
153
|
+
percentage = int(100 * np.count_nonzero(travel_times) / cf.number_of_particles)
|
|
154
|
+
if percentage < 95:
|
|
155
|
+
logging.warning(f"Only {percentage}% of particles reached the cold side. Increase the number of timesteps.")
|
|
142
156
|
|
|
143
157
|
# Check if pixel size is too small:
|
|
144
158
|
speeds = np.loadtxt("Data/All group velocities.csv", encoding='utf-8')
|
|
@@ -146,8 +160,3 @@ def output_parameter_warnings():
|
|
|
146
160
|
logging.warning("Pixels in y direction are smaller than length of one step")
|
|
147
161
|
if max(speeds) * cf.timestep > cf.width / cf.number_of_pixels_x:
|
|
148
162
|
logging.warning("Pixels in x direction are smaller than length of one step")
|
|
149
|
-
|
|
150
|
-
# Check how many particles reached the cold side during simulation:
|
|
151
|
-
percentage = int(100 * np.count_nonzero(travel_times) / cf.number_of_particles)
|
|
152
|
-
if percentage < 95:
|
|
153
|
-
logging.warning(f"Only {percentage}% of particles reached the cold side. Increase the number of timesteps.")
|
|
@@ -271,19 +271,6 @@ def plot_interfaces_angles_distribution():
|
|
|
271
271
|
np.savetxt('Data/Distribution of interfaces angles.csv', angle_distributions, fmt='%1.3e', delimiter=",")
|
|
272
272
|
|
|
273
273
|
|
|
274
|
-
import subprocess
|
|
275
|
-
import os
|
|
276
|
-
|
|
277
|
-
def open_pdf_with_mupdf(pdf_path):
|
|
278
|
-
try:
|
|
279
|
-
# path of mu pdf
|
|
280
|
-
mupdf_path = r"C:\Users\victo\Downloads\mupdf-1.26.2-windows\mupdf-1.26.2-windows\mupdf-gl.exe"
|
|
281
|
-
|
|
282
|
-
# Lancer mupdf avec ton fichier
|
|
283
|
-
subprocess.Popen([mupdf_path, os.path.abspath(pdf_path)])
|
|
284
|
-
except Exception as e:
|
|
285
|
-
print(f"[ERROR] Impossible d'ouvrir le PDF avec MuPDF : {e}")
|
|
286
|
-
|
|
287
274
|
def plot_transmission_vs_angle():
|
|
288
275
|
try:
|
|
289
276
|
angles = np.loadtxt("Data/All interfaces angles.csv", dtype='float', encoding='utf-8')
|
|
@@ -808,8 +795,13 @@ def plot_temperature_profile():
|
|
|
808
795
|
"""Plot profile of temperature for each time segment"""
|
|
809
796
|
fig, ax = plt.subplots()
|
|
810
797
|
data = np.genfromtxt("Data/Temperature profiles y.csv", unpack=True, delimiter=',', skip_header=1, encoding='utf-8')
|
|
811
|
-
|
|
812
|
-
|
|
798
|
+
coords = data[0]
|
|
799
|
+
n_timeframes = len(data) - 1
|
|
800
|
+
for timeframe_num in range(n_timeframes):
|
|
801
|
+
ax.plot(coords[1:], data[timeframe_num + 1][1:], linewidth=1, label=f'Time frame {timeframe_num+1}')
|
|
802
|
+
# Linear fit of the last timeframe (same algorithm as thermal conductivity calculation)
|
|
803
|
+
slope, intercept = np.polyfit(coords, data[n_timeframes], 1)
|
|
804
|
+
ax.plot(coords[1:], slope * coords[1:] + intercept, '--', color='gray', linewidth=1, label='Linear fit (last frame)')
|
|
813
805
|
ax.set_xlabel('Y (μm)')
|
|
814
806
|
ax.set_ylabel('Temperature (K)')
|
|
815
807
|
ax.legend()
|
|
@@ -819,11 +811,16 @@ def plot_temperature_profile():
|
|
|
819
811
|
def plot_heat_flux_profile():
|
|
820
812
|
"""Plot profile of heat flux for each time segment"""
|
|
821
813
|
|
|
814
|
+
data = np.genfromtxt("Data/Heat flux profiles y.csv", unpack=True, delimiter=',', skip_header=1, encoding='utf-8')
|
|
815
|
+
n_timeframes = (len(data) - 1) // 2
|
|
816
|
+
|
|
822
817
|
# Effective heat flux:
|
|
823
818
|
fig, ax = plt.subplots()
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
819
|
+
for timeframe_num in range(n_timeframes):
|
|
820
|
+
ax.plot(data[0][1:], data[timeframe_num + 1][1:], linewidth=1, label=f'Time frame {timeframe_num+1}')
|
|
821
|
+
# Mean of the last timeframe, skipping first pixel (same as thermal conductivity calculation)
|
|
822
|
+
mean_flux = np.mean(data[n_timeframes][1:])
|
|
823
|
+
ax.axhline(mean_flux, color='gray', linestyle='--', linewidth=1, label='Mean (last frame)')
|
|
827
824
|
ax.set_xlabel('Y (μm)')
|
|
828
825
|
ax.set_ylabel('Heat flux (W/m²)')
|
|
829
826
|
ax.legend()
|
|
@@ -832,8 +829,10 @@ def plot_heat_flux_profile():
|
|
|
832
829
|
|
|
833
830
|
# Material heat flux:
|
|
834
831
|
fig, ax = plt.subplots()
|
|
835
|
-
for timeframe_num in range(
|
|
836
|
-
ax.plot(data[0], data[2*timeframe_num + 1], linewidth=1, label=f'Time frame {timeframe_num+1}')
|
|
832
|
+
for timeframe_num in range(n_timeframes):
|
|
833
|
+
ax.plot(data[0][1:], data[2*timeframe_num + 1][1:], linewidth=1, label=f'Time frame {timeframe_num+1}')
|
|
834
|
+
mean_flux_mat = np.mean(data[2*n_timeframes - 1][1:])
|
|
835
|
+
ax.axhline(mean_flux_mat, color='gray', linestyle='--', linewidth=1, label='Mean (last frame)')
|
|
837
836
|
ax.set_xlabel('Y (μm)')
|
|
838
837
|
ax.set_ylabel('Heat flux (W/m²)')
|
|
839
838
|
ax.legend()
|
|
@@ -872,7 +871,7 @@ def plot_heat_flux_map(file, label, units="a.u."):
|
|
|
872
871
|
"""Plot heat flux map as color map"""
|
|
873
872
|
fig = plt.figure()
|
|
874
873
|
heat_flux_map = np.genfromtxt(file, unpack=False, delimiter=',', skip_header=0, encoding='utf-8')
|
|
875
|
-
heat_flux_map = np.flipud(heat_flux_map)
|
|
874
|
+
heat_flux_map = np.abs(np.flipud(heat_flux_map))
|
|
876
875
|
minimum_of_colorbar = 1e5
|
|
877
876
|
boundaries = [(-cf.width / 2) * 1e6, (cf.width / 2) * 1e6, 0, cf.length * 1e6]
|
|
878
877
|
plt.imshow(heat_flux_map, cmap='jet', interpolation='none', extent=boundaries,
|
|
@@ -970,7 +969,8 @@ def plot_scattering_statistics():
|
|
|
970
969
|
if scattering_type == 6: # Skip hot side scattering
|
|
971
970
|
continue
|
|
972
971
|
# Calculate and plot scattering events per second:
|
|
973
|
-
scattering_rate =
|
|
972
|
+
scattering_rate = np.divide(scattering_data[scattering_type, :], time_spent,
|
|
973
|
+
out=np.zeros_like(time_spent), where=time_spent != 0)
|
|
974
974
|
all_scattering_rates.append(scattering_rate)
|
|
975
975
|
ax.plot(segments, scattering_rate, '-o', markersize=2)
|
|
976
976
|
|
|
@@ -995,6 +995,15 @@ def plot_scattering_statistics():
|
|
|
995
995
|
data = np.vstack((segments, all_scattering_rates)).T
|
|
996
996
|
np.savetxt(filename, data, fmt='%1.2e', delimiter=",", header=header)
|
|
997
997
|
|
|
998
|
+
# Save average scattering rates (one value per type, averaged over all segments):
|
|
999
|
+
avg_header = ("Sidewalls diffuse [1/ns], Sidewalls specular [1/ns], Top & bottom diffuse [1/ns], "
|
|
1000
|
+
"Top & bottom specular [1/ns], Holes diffuse [1/ns], Holes specular [1/ns], "
|
|
1001
|
+
"Internal [1/ns], Pillars diffuse [1/ns], Pillars specular [1/ns], "
|
|
1002
|
+
"Interfaces diffuse [1/ns], Interfaces specular [1/ns], "
|
|
1003
|
+
"Interfaces transmission diffuse [1/ns], Interfaces transmission specular [1/ns]")
|
|
1004
|
+
avg_data = np.array([np.mean(rate) for rate in all_scattering_rates])
|
|
1005
|
+
np.savetxt("Data/Scattering rates averaged.csv", avg_data.reshape(1, -1), fmt='%1.2e', delimiter=",", header=avg_header)
|
|
1006
|
+
|
|
998
1007
|
def plot_scattering_rate_vs_energy():
|
|
999
1008
|
fig, ax = plt.subplots()
|
|
1000
1009
|
energies, scattering_rate = np.genfromtxt("Data/Scattering rate vs energy.csv", unpack=True, delimiter=',', usecols=(0,1), skip_header=1)
|
|
@@ -1099,11 +1108,8 @@ def plot_data(particle_type: ParticleType, cf, mfp_sampling=False):
|
|
|
1099
1108
|
plot_trajectories,
|
|
1100
1109
|
plot_angle_distribution,
|
|
1101
1110
|
plot_free_path_distribution,
|
|
1102
|
-
plot_frequency_distribution,
|
|
1103
|
-
plot_wavelength_distribution,
|
|
1104
1111
|
plot_travel_time_distribution,
|
|
1105
1112
|
plot_mean_free_path_distribution,
|
|
1106
|
-
plot_velocity_distribution,
|
|
1107
1113
|
plot_energy_distribution,
|
|
1108
1114
|
plot_travel_time_vs_energy,
|
|
1109
1115
|
plot_transport_function,
|
|
@@ -1134,6 +1140,10 @@ def plot_data(particle_type: ParticleType, cf, mfp_sampling=False):
|
|
|
1134
1140
|
else:
|
|
1135
1141
|
function_list = electron_function_list
|
|
1136
1142
|
|
|
1143
|
+
# These plots require data files that are not saved in low memory mode:
|
|
1144
|
+
if cf.low_memory_usage:
|
|
1145
|
+
function_list = [f for f in function_list if f not in (plot_trajectories, plot_free_path_distribution)]
|
|
1146
|
+
|
|
1137
1147
|
# Run main functions and handle exceptions:
|
|
1138
1148
|
for func in function_list:
|
|
1139
1149
|
try:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: freepaths
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.1
|
|
4
4
|
Summary: Phonon Monte Carlo simulator
|
|
5
5
|
Home-page: https://github.com/anufrievroman/freepaths
|
|
6
6
|
Author: Roman Anufriev
|
|
@@ -54,9 +54,13 @@ FreePATHS is a command line application, so it runs inside Linux, MacOS, or Wind
|
|
|
54
54
|
|
|
55
55
|
There are two modes of using the program. Main mode traces a large number of phonons through a structure and collects statistics about their paths. The MFP sampling mode measures phonon mean free paths using a small number of phonons and calculates the thermal conductivity by integrating phonon dispersion.
|
|
56
56
|
|
|
57
|
-
###
|
|
57
|
+
### Getting started
|
|
58
58
|
|
|
59
|
-
If you
|
|
59
|
+
If you run `freepaths` without any arguments, the program will print a usage summary. To run a demo simulation with default parameters, use the `--demo` flag:
|
|
60
|
+
|
|
61
|
+
`freepaths --demo`
|
|
62
|
+
|
|
63
|
+
Example input files are available in the [examples](https://github.com/anufrievroman/freepaths/tree/master/examples) folder.
|
|
60
64
|
|
|
61
65
|
### Main mode
|
|
62
66
|
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
"""FreePATHS - Free Phonon and THermal Simulator"""
|
|
2
|
-
|
|
3
|
-
import argparse
|
|
4
|
-
import colorama
|
|
5
|
-
from colorama import Fore, Style
|
|
6
|
-
|
|
7
|
-
from freepaths.particle_types import ParticleType
|
|
8
|
-
|
|
9
|
-
__version__ = "2.2.4"
|
|
10
|
-
|
|
11
|
-
colorama.init()
|
|
12
|
-
|
|
13
|
-
# Parse user arguments:
|
|
14
|
-
parser = argparse.ArgumentParser(
|
|
15
|
-
prog = 'FreePATHS',
|
|
16
|
-
description = 'Phonon Monte Carlo simulator',
|
|
17
|
-
epilog = 'For more information, examples, and tutorials, visit: https://anufrievroman.gitbook.io/freepaths'
|
|
18
|
-
)
|
|
19
|
-
parser.add_argument('input_file', nargs='?', default=None, help='The input file')
|
|
20
|
-
parser.add_argument("-s", "--sampling", help="Run in phonon MFP sampling mode", action="store_true")
|
|
21
|
-
parser.add_argument("-e", "--electron", help="Run simulation for electrons", action="store_true")
|
|
22
|
-
args = parser.parse_args()
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def run():
|
|
26
|
-
"""Run the program depending on the mode"""
|
|
27
|
-
print(f"\n{Fore.BLUE}FreePATHS v{__version__}{Style.RESET_ALL}")
|
|
28
|
-
if args.sampling:
|
|
29
|
-
import freepaths.main_mfp_sampling
|
|
30
|
-
freepaths.main_mfp_sampling.main(args.input_file, ParticleType.PHONON)
|
|
31
|
-
|
|
32
|
-
elif args.electron:
|
|
33
|
-
import freepaths.main_tracing
|
|
34
|
-
freepaths.main_tracing.main(args.input_file, ParticleType.ELECTRON)
|
|
35
|
-
|
|
36
|
-
else:
|
|
37
|
-
import freepaths.main_tracing
|
|
38
|
-
freepaths.main_tracing.main(args.input_file, ParticleType.PHONON)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if __name__ == "__main__":
|
|
42
|
-
run()
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|