Coverage for src/cell_abm_pipeline/flows/make_simulation_movies.py: 0%
114 statements
« prev ^ index » next coverage.py v7.1.0, created at 2024-06-05 19:14 +0000
« prev ^ index » next coverage.py v7.1.0, created at 2024-06-05 19:14 +0000
1"""
2Workflow for making simulation movies.
4Working location structure:
6.. code-block:: bash
8 (name)
9 ├── data
10 │ └── data.LOCATIONS
11 │ └── (name)_(key)_(seed).LOCATIONS.tar.xz
12 ├── movies
13 │ ├── movies.CENTROIDS
14 │ │ ├── (name)_(key)_(seed).CENTROIDS.gif
15 │ │ └── (name)_(key)_(seed)
16 │ │ └── (frame).CENTROIDS.png
17 │ └── movies.SCAN
18 │ ├── (name)_(key)_(seed)_(view)_(frame).SCAN.gif
19 │ └── (name)_(key)_(seed)_(view)_(frame)
20 │ └── (index).SCAN.png
21 └── results
22 └── (name)_(key)_(seed).csv
24Different formats use inputs from **results** and **data.LOCATIONS**. Movies are
25saved to **movies**.
26"""
28from dataclasses import dataclass, field
30import numpy as np
31from arcade_collection.output import get_voxel_contours
32from io_collection.keys import check_key, make_key
33from io_collection.load import load_dataframe, load_tar
34from io_collection.save import save_figure, save_gif
35from prefect import flow, get_run_logger
37from cell_abm_pipeline.flows.plot_cell_shapes import REGION_COLORS
38from cell_abm_pipeline.tasks import make_centroids_figure, make_contour_figure
40FORMATS: list[str] = [
41 "centroids",
42 "scan",
43]
46@dataclass
47class ParametersConfigScan:
48 """Parameter configuration for make simulation movies flow - scan."""
50 seeds: list[int] = field(default_factory=lambda: [0])
51 """Simulation seeds to use for creating scan movies."""
53 frame_spec: tuple[int, int, int] = (0, 1153, 1152)
54 """Specification for simulation ticks to use for creating scan movies."""
56 index_spec: tuple[int, int, int] = (0, 1, 1)
57 """Specification for contour indices to use for creating scan movies."""
59 regions: list[str] = field(default_factory=lambda: ["DEFAULT"])
60 """List of subcellular regions."""
62 box: tuple[int, int, int] = field(default_factory=lambda: (1, 1, 1))
63 """Size of bounding box."""
65 view: str = "top"
66 """Projection view."""
68 x_bounds: tuple[int, int] = field(default_factory=lambda: (0, 1))
69 """Size of x bounds."""
71 y_bounds: tuple[int, int] = field(default_factory=lambda: (0, 1))
72 """Size of y bounds."""
74 region_colors: dict[str, str] = field(default_factory=lambda: REGION_COLORS)
75 """Colors for each cell region."""
78@dataclass
79class ParametersConfigCentroids:
80 """Parameter configuration for make simulation movies flow - centroids."""
82 seeds: list[int] = field(default_factory=lambda: [0])
83 """Simulation seeds to use for creating centroid movies."""
85 frame_spec: tuple[int, int, int] = (0, 1, 1)
86 """Specification for simulation ticks to use for creating centroid movies."""
88 x_bounds: tuple[int, int] = field(default_factory=lambda: (0, 1))
89 """Size of x bounds."""
91 y_bounds: tuple[int, int] = field(default_factory=lambda: (0, 1))
92 """Size of y bounds."""
94 dt: float = 1.0
95 """Temporal scaling in hours/tick."""
97 window: int = 0
98 """Window size for centroid tail."""
101@dataclass
102class ParametersConfig:
103 """Parameter configuration for make simulation movies flow."""
105 formats: list[str] = field(default_factory=lambda: FORMATS)
106 """List of movie formats."""
108 scan: ParametersConfigScan = ParametersConfigScan()
109 """Parameters for scan movie subflow."""
111 centroids: ParametersConfigCentroids = ParametersConfigCentroids()
112 """Parameters for centroids movie subflow."""
115@dataclass
116class ContextConfig:
117 """Context configuration for make simulation movies flow."""
119 working_location: str
120 """Location for input and output files (local path or S3 bucket)."""
123@dataclass
124class SeriesConfig:
125 """Series configuration for make simulation movies flow."""
127 name: str
128 """Name of the simulation series."""
130 conditions: list[dict]
131 """List of series condition dictionaries (must include unique condition "key")."""
134@flow(name="make-simulation-movies")
135def run_flow(context: ContextConfig, series: SeriesConfig, parameters: ParametersConfig) -> None:
136 """
137 Main make simulation movies flow.
139 Calls the following subflows, if the format is specified.
141 - :py:func:`run_flow_make_centroids_movie`
142 - :py:func:`run_flow_make_scan_movie`
143 """
145 if "centroids" in parameters.formats:
146 run_flow_make_centroids_movie(context, series, parameters.centroids)
148 if "scan" in parameters.formats:
149 run_flow_make_scan_movie(context, series, parameters.scan)
152@flow(name="make-simulation-movies_make-centroids-movie")
153def run_flow_make_centroids_movie(
154 context: ContextConfig, series: SeriesConfig, parameters: ParametersConfigCentroids
155) -> None:
156 """Make simulation movies subflow for centroids."""
158 movie_key = make_key(series.name, "movies", "movies.CENTROIDS")
159 keys = [condition["key"] for condition in series.conditions]
161 for key in keys:
162 for seed in parameters.seeds:
163 series_key = f"{series.name}_{key}_{seed:04d}"
165 results_key = make_key(series.name, "results", f"{series_key}.csv")
166 results = load_dataframe(context.working_location, results_key)
168 frame_keys = []
170 for frame in np.arange(*parameters.frame_spec):
171 frame_key = make_key(movie_key, f"{series_key}", f"{frame:06d}.CENTROIDS.png")
172 frame_keys.append(frame_key)
174 if check_key(context.working_location, frame_key):
175 continue
177 save_figure(
178 context.working_location,
179 frame_key,
180 make_centroids_figure(
181 results,
182 frame,
183 parameters.x_bounds,
184 parameters.y_bounds,
185 parameters.dt,
186 parameters.window,
187 ),
188 )
190 output_key = make_key(movie_key, f"{series_key}.CENTROIDS.gif")
191 save_gif(context.working_location, output_key, frame_keys)
194@flow(name="make-simulation-movies_make-scan-movie")
195def run_flow_make_scan_movie(
196 context: ContextConfig, series: SeriesConfig, parameters: ParametersConfigScan
197) -> None:
198 """Make simulation movies subflow for scan."""
200 data_key = make_key(series.name, "data", "data.LOCATIONS")
201 movie_key = make_key(series.name, "movies", "movies.SCAN")
202 keys = [condition["key"] for condition in series.conditions]
204 if parameters.view not in ("top", "side"):
205 logger = get_run_logger()
206 logger.error("View [ %s ] not valid for scan movie.", parameters.view)
207 return
209 indices = list(np.arange(*parameters.index_spec))
210 view = "top" if parameters.view == "top" else "side1"
212 for key in keys:
213 for seed in parameters.seeds:
214 series_key = f"{series.name}_{key}_{seed:04d}"
216 tar_key = make_key(data_key, f"{series_key}.LOCATIONS.tar.xz")
217 tar = load_tar(context.working_location, tar_key)
219 for frame in np.arange(*parameters.frame_spec):
220 frame_key = f"{series_key}_{parameters.view}_{frame:06d}"
221 contours = get_voxel_contours(
222 series_key,
223 tar,
224 frame,
225 parameters.regions,
226 parameters.box,
227 {view: indices},
228 )
230 index_keys = []
232 for index in indices:
233 index_key = make_key(movie_key, frame_key, f"{index:03d}.SCAN.png")
234 index_keys.append(index_key)
236 if check_key(context.working_location, index_key):
237 continue
239 save_figure(
240 context.working_location,
241 index_key,
242 make_contour_figure(
243 contours,
244 index,
245 view,
246 parameters.regions,
247 parameters.x_bounds,
248 parameters.y_bounds,
249 parameters.region_colors,
250 ),
251 )
253 output_key = make_key(movie_key, f"{frame_key}.SCAN.gif")
254 save_gif(context.working_location, output_key, index_keys)