forked from michelebucelli/nmpde-projects
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplot_performance.py
More file actions
executable file
·182 lines (157 loc) · 7.68 KB
/
Copy pathplot_performance.py
File metadata and controls
executable file
·182 lines (157 loc) · 7.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/usr/bin/env python3
import sys
import os
import re
def parse_results(results_file):
data = []
pattern = re.compile(r'^(\S+)\s+-\s+(.*?):\s+([0-9.]+)\s+seconds')
with open(results_file, 'r') as f:
for line in f:
match = pattern.match(line.strip())
if match:
job_id = match.group(1)
config = match.group(2).strip()
# Clean up multiple spaces
config = re.sub(r'\s+', ' ', config)
time_s = float(match.group(3))
data.append((job_id, config, time_s))
return data
def parse_iterations(out_file):
iterations = []
pattern = re.compile(r'Timestep\s+\d+.*:\s*(\d+)\s+\w+\s+iterations')
if not os.path.isfile(out_file):
return 0.0
with open(out_file, 'r') as f:
for line in f:
match = pattern.search(line)
if match:
iterations.append(int(match.group(1)))
return sum(iterations) / len(iterations) if iterations else 0.0
def text_plot(data):
if not data:
print("No data to display.")
return
print("\n=== Text-Based Performance Plot ===")
max_len = max(len(d[0]) for d in data)
# Times scale
max_time = max(d[1] for d in data)
scale_time = 25.0 / max_time if max_time > 0 else 1.0
# Iters scale
max_iter = max(d[3] for d in data)
scale_iter = 25.0 / max_iter if max_iter > 0 else 1.0
for config, mean_time, std_time, mean_iter, std_iter in data:
bar_time = '#' * int(mean_time * scale_time)
bar_iter = '*' * int(mean_iter * scale_iter)
time_str = f"{mean_time:.2f} ± {std_time:.2f}m" if std_time > 0 else f"{mean_time:.2f}m"
iter_str = f"{mean_iter:.1f} ± {std_iter:.1f}" if std_iter > 0 else f"{mean_iter:.1f}"
print(f"{config:<{max_len}} | Time: {bar_time:<25} ({time_str}) | Iters: {bar_iter:<25} ({iter_str})")
print("===================================\n")
def main():
if len(sys.argv) < 2:
print("Usage: plot_performance.py <results_directory>")
sys.exit(1)
directory = sys.argv[1]
results_file = os.path.join(directory, "solver_performance.txt")
if not os.path.isfile(results_file):
print(f"Error: Results file '{results_file}' not found.")
sys.exit(1)
raw_data = parse_results(results_file)
if not raw_data:
print(f"Error: No valid result data found in '{results_file}'.")
sys.exit(1)
from collections import defaultdict
import math
# Group times and iterations by configuration
config_times = defaultdict(list)
config_iters = defaultdict(list)
for job_id, config, time_s in raw_data:
out_file = os.path.join(directory, f"{job_id}.out")
avg_iter = parse_iterations(out_file)
config_times[config].append(time_s / 60.0)
config_iters[config].append(avg_iter)
def get_stats(vals):
if not vals:
return 0.0, 0.0
m = sum(vals) / len(vals)
if len(vals) > 1:
var = sum((x - m) ** 2 for x in vals) / (len(vals) - 1)
sd = math.sqrt(var)
else:
sd = 0.0
return m, sd
data = []
for config in config_times:
mean_time, std_time = get_stats(config_times[config])
mean_iter, std_iter = get_stats(config_iters[config])
data.append((config, mean_time, std_time, mean_iter, std_iter))
# Attempt to plot with matplotlib
try:
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
configs = [d[0] for d in data]
mean_times = [d[1] for d in data]
std_times = [d[2] for d in data]
mean_iters = [d[3] for d in data]
std_iters = [d[4] for d in data]
# Modern styling
plt.style.use('seaborn-v0_8-whitegrid' if 'seaborn-v0_8-whitegrid' in plt.style.available else 'default')
# Slate/teal palette
color_palette = ['#2b5c8f', '#4682b4', '#5f9ea0', '#66c2a5', '#3288bd', '#5e4fa2']
colors = [color_palette[i % len(color_palette)] for i in range(len(data))]
# Plot 1: Execution Time (Bar Chart with Std Dev)
fig1, ax1 = plt.subplots(figsize=(10, max(5, len(data) * 0.8)))
bars1 = ax1.barh(configs, mean_times, xerr=std_times, color=colors, edgecolor='none', height=0.6,
error_kw={'ecolor': '#7f8c8d', 'capsize': 4, 'elinewidth': 1.5})
max_limit = max(m + s for m, s in zip(mean_times, std_times)) if mean_times else 1.0
ax1.set_xlim(0, max_limit * 1.18)
for bar, std in zip(bars1, std_times):
width = bar.get_width()
label_text = f'{width:.2f} ± {std:.2f}m' if std > 0 else f'{width:.2f}m'
ax1.text(width + std + (max_limit * 0.015), bar.get_y() + bar.get_height()/2,
label_text,
va='center', ha='left', fontsize=10, fontweight='bold', color='#2c3e50')
ax1.set_xlabel('Execution Time (minutes)', fontsize=12, fontweight='bold', labelpad=10)
ax1.set_ylabel('Configuration', fontsize=12, fontweight='bold', labelpad=10)
ax1.set_title('Solver & Preconditioner Sweep Execution Times', fontsize=14, fontweight='bold', pad=15)
ax1.grid(True, linestyle='--', alpha=0.5, axis='x')
ax1.spines['top'].set_visible(False)
ax1.spines['right'].set_visible(False)
ax1.spines['left'].set_color('#bdc3c7')
ax1.spines['bottom'].set_color('#bdc3c7')
plt.tight_layout()
output_image1 = os.path.join(directory, "execution_times.png")
plt.savefig(output_image1, dpi=300)
plt.close(fig1)
print(f"Successfully generated execution times plot: {output_image1}")
# Plot 2: Average Iterations (Bar Chart with Std Dev)
fig2, ax2 = plt.subplots(figsize=(10, max(5, len(data) * 0.8)))
bars2 = ax2.barh(configs, mean_iters, xerr=std_iters, color=colors, edgecolor='none', height=0.6,
error_kw={'ecolor': '#7f8c8d', 'capsize': 4, 'elinewidth': 1.5})
max_limit_iter = max(m + s for m, s in zip(mean_iters, std_iters)) if mean_iters else 1.0
ax2.set_xlim(0, max_limit_iter * 1.18)
for bar, std in zip(bars2, std_iters):
width = bar.get_width()
label_text = f'{width:.1f} ± {std:.1f}' if std > 0 else f'{width:.1f}'
ax2.text(width + std + (max_limit_iter * 0.015), bar.get_y() + bar.get_height()/2,
label_text,
va='center', ha='left', fontsize=10, fontweight='bold', color='#2c3e50')
ax2.set_xlabel('Average Iterations per Timestep', fontsize=12, fontweight='bold', labelpad=10)
ax2.set_ylabel('Configuration', fontsize=12, fontweight='bold', labelpad=10)
ax2.set_title('Solver & Preconditioner Sweep Average Iterations', fontsize=14, fontweight='bold', pad=15)
ax2.grid(True, linestyle='--', alpha=0.5, axis='x')
ax2.spines['top'].set_visible(False)
ax2.spines['right'].set_visible(False)
ax2.spines['left'].set_color('#bdc3c7')
ax2.spines['bottom'].set_color('#bdc3c7')
plt.tight_layout()
output_image2 = os.path.join(directory, "average_iterations.png")
plt.savefig(output_image2, dpi=300)
plt.close(fig2)
print(f"Successfully generated average iterations plot: {output_image2}")
except ImportError:
print("Matplotlib is not installed. Generating text-based ASCII plot instead:")
text_plot(data)
print("Tip: Install matplotlib (`pip install matplotlib`) to generate high-quality PNG plots.")
if __name__ == '__main__':
main()