Skip to content

Commit c218357

Browse files
committed
Add new command: map
1 parent b525457 commit c218357

File tree

2 files changed

+162
-0
lines changed

2 files changed

+162
-0
lines changed

src/batch_processing/cmd/map.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
from pathlib import Path
2+
3+
import matplotlib.pyplot as plt
4+
import numpy as np
5+
from matplotlib.colors import ListedColormap
6+
from netCDF4 import Dataset
7+
from numpy.ma.core import MaskedArray
8+
9+
from batch_processing.cmd.base import BaseCommand
10+
from batch_processing.utils.utils import (
11+
get_batch_number,
12+
get_dimensions,
13+
write_text_file,
14+
)
15+
16+
WHITE = 0
17+
BLACK = 1
18+
RED = 2
19+
GREEN = 3
20+
GRAY = 4
21+
22+
23+
class MapCommand(BaseCommand):
24+
"""Generates a visualization of the run results, and identifies failed cells."""
25+
def __init__(self, args):
26+
super().__init__()
27+
self.base_batch_dir = Path(self.exacloud_user_dir, args.batches)
28+
29+
def execute(self):
30+
print("Pulling run_status and run-mask files...")
31+
run_status_files = [
32+
file for file in self.base_batch_dir.glob("batch_*/output/run_status.nc")
33+
]
34+
run_mask_files = [
35+
file for file in self.base_batch_dir.glob("batch_*/input/run-mask.nc")
36+
]
37+
38+
run_status_batch_numbers = [get_batch_number(file) for file in run_status_files]
39+
run_status_files.sort(key=get_batch_number)
40+
41+
run_mask_batch_numbers = [get_batch_number(file) for file in run_mask_files]
42+
run_mask_files.sort(key=get_batch_number)
43+
44+
missing_batches = list(
45+
set(run_mask_batch_numbers) - set(run_status_batch_numbers)
46+
)
47+
missing_batches.sort()
48+
49+
run_status_data = []
50+
X, Y = get_dimensions(run_mask_files[0])
51+
print("Organizing files and filling missing data...")
52+
for file in run_status_files:
53+
batch_number = get_batch_number(file)
54+
if batch_number in missing_batches:
55+
data = generate_empty_array((Y, X))
56+
else:
57+
data = get_variable(file, "run_status")
58+
59+
run_status_data.append(data[0, :])
60+
61+
run_status_matrix = np.stack(run_status_data, axis=0)
62+
63+
run_mask_data = []
64+
65+
for file in run_mask_files:
66+
batch_number = get_batch_number(file)
67+
if batch_number in missing_batches:
68+
data = generate_empty_array((Y, X))
69+
else:
70+
data = get_variable(file, "run")
71+
72+
run_mask_data.append(data[0, :])
73+
74+
run_mask_matrix = np.stack(run_mask_data, axis=0)
75+
76+
# Initialize a numeric matrix to store color codes for each coordinate
77+
numeric_color_matrix = np.zeros(run_status_matrix.shape)
78+
79+
for (i, j), elem in np.ndenumerate(run_status_matrix):
80+
# the cell is skipped
81+
if elem == 0:
82+
numeric_color_matrix[i, j] = WHITE
83+
# the cell is failed
84+
elif elem < 0:
85+
run_mask_val = run_mask_matrix[i, j]
86+
# we are not supposed to run this cell
87+
if run_mask_val == 0:
88+
numeric_color_matrix[i, j] = BLACK
89+
# we are supposed to run this cell
90+
elif run_mask_val == 1:
91+
numeric_color_matrix[i, j] = RED
92+
# unexpected value
93+
else:
94+
numeric_color_matrix[i, j] = GRAY
95+
# the cell successfully ran
96+
else:
97+
numeric_color_matrix[i, j] = GREEN
98+
99+
# Define the colormap
100+
cmap = ListedColormap(["white", "black", "red", "gray", "green"])
101+
102+
# Plot the matrix
103+
plt.figure(figsize=(10, 8))
104+
plt.imshow(numeric_color_matrix, cmap=cmap, aspect="auto")
105+
plt.colorbar(ticks=[0, 1, 2, 3, 4], label="Status")
106+
plt.clim(-0.5, 3.5) # Set the limits for color bar to align with categories
107+
108+
# Set the color bar labels
109+
plt.gca().images[-1].colorbar.set_ticks([0, 1, 2, 3, 4])
110+
plt.gca().images[-1].colorbar.set_ticklabels(
111+
["Disabled", "Should Be Disabled", "Failure", "Unknown Case", "Success"]
112+
)
113+
114+
plt.title("Run Status Visualization")
115+
plt.xlabel("Coordinate X")
116+
plt.ylabel("Coordinate Y")
117+
plt.tight_layout()
118+
119+
# Save the plot as an image file
120+
output_image_path = self.base_batch_dir / "run_status_visualization.png"
121+
plt.savefig(output_image_path)
122+
123+
print(f"Visualization saved as {output_image_path}")
124+
125+
failed_cells = []
126+
row = []
127+
temp_i = 0
128+
for (i, j), elem in np.ndenumerate(numeric_color_matrix):
129+
if elem == RED:
130+
if temp_i != i:
131+
temp_i = i
132+
if row:
133+
failed_cells.append(row)
134+
row = []
135+
else:
136+
row.append((i, j))
137+
138+
failed_cells.append(row)
139+
140+
failed_coords_file_path = self.base_batch_dir / "failed_cell_coords.txt"
141+
content = "\n\n".join([str(row) for row in failed_cells])
142+
write_text_file(failed_coords_file_path, content)
143+
144+
print(f"The failed cell coordinates are written to {failed_coords_file_path}")
145+
146+
147+
def get_variable(file_path: str, variable_name: str) -> MaskedArray:
148+
with Dataset(file_path, "r") as dataset:
149+
data = dataset.variables[variable_name][:]
150+
return data
151+
152+
153+
def generate_empty_array(shape: tuple, fill_value: int = -999) -> np.ma.MaskedArray:
154+
data = np.full(shape, fill_value)
155+
return np.ma.masked_array(data, fill_value=fill_value)

src/batch_processing/main.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
InitCommand = lazy_import.lazy_class("batch_processing.cmd.init.InitCommand")
77
InputCommand = lazy_import.lazy_class("batch_processing.cmd.input.InputCommand")
8+
MapCommand = lazy_import.lazy_class("batch_processing.cmd.map.MapCommand")
89
MonitorCommand = lazy_import.lazy_class("batch_processing.cmd.monitor.MonitorCommand")
910
RunCheckCommand = lazy_import.lazy_class(
1011
"batch_processing.cmd.run_check.RunCheckCommand"
@@ -249,6 +250,12 @@ def main():
249250
)
250251
parser_monitoring.set_defaults(func=lambda args: MonitorCommand(args).execute())
251252

253+
parser_map = subparsers.add_parser("map", help="Maps the given path's status.")
254+
255+
add_batch_path_argument(parser_map)
256+
257+
parser_map.set_defaults(func=lambda args: MapCommand(args).execute())
258+
252259
parser_init = subparsers.add_parser(
253260
"init", help="Initialize the environment for running the simulation"
254261
)

0 commit comments

Comments
 (0)