Coverage for src/cell_abm_pipeline/flows/initialize_physicell_simulations.py: 0%

62 statements  

« prev     ^ index     » next       coverage.py v7.1.0, created at 2024-06-05 19:14 +0000

1""" 

2Workflow for initializing PhysiCell simulations. 

3 

4Working location structure: 

5 

6.. code-block:: bash 

7 

8 (name) 

9 ├── inits 

10 │ └── inits.PHYSICELL 

11 │ └── (name)_(key)_(resolution).csv 

12 └── plots 

13 └── plots.COORDINATES 

14 └── (name)_(key)_(resolution).COORDINATES.png 

15 

16Initialization consist of a single cell of the specified height and volume, 

17sampled at the given spatial resolution. Coordinates are saved to 

18**inits.PHYSICELL**. 

19""" 

20 

21from dataclasses import dataclass, field 

22from math import ceil, pi, sqrt 

23 

24import pandas as pd 

25from abm_initialization_collection.coordinate import filter_coordinate_bounds, make_grid_coordinates 

26from abm_initialization_collection.image import plot_contact_sheet 

27from io_collection.keys import make_key 

28from io_collection.save import save_dataframe, save_figure 

29from prefect import flow 

30 

31# Default average cell height in um. 

32AVERAGE_CELL_HEIGHT = 9.0 

33 

34# Default average cell volume in um^3. 

35AVERAGE_CELL_VOLUME = 1300.0 

36 

37# Default cell id. 

38DEFAULT_CELL_ID = 1 

39 

40# Substrate id. 

41SUBSTRATE_ID = -1 

42 

43 

44@dataclass 

45class ParametersConfig: 

46 """Parameter configuration for initialize PhysiCell simulations flow.""" 

47 

48 grid: str = "rect" 

49 """Type of sampling grid (rect = rectangular, hex = hexagonal).""" 

50 

51 ds: list[float] = field(default_factory=lambda: [1.0]) 

52 """Spatial scaling in um/voxel.""" 

53 

54 bounding_box: tuple[int, int] = (100, 100) 

55 """Size of bounding box in um.""" 

56 

57 cell_height: float = AVERAGE_CELL_HEIGHT 

58 """Average cell height in um.""" 

59 

60 cell_volume: float = AVERAGE_CELL_VOLUME 

61 """Average cell volume in um^3.""" 

62 

63 substrate: bool = True 

64 """True to include substrate in initialization, False otherwise.""" 

65 

66 contact_sheet: bool = True 

67 """True to save contact sheet of initialization, False otherwise.""" 

68 

69 

70@dataclass 

71class ContextConfig: 

72 """Context configuration for initialize PhysiCell simulations flow.""" 

73 

74 working_location: str 

75 """Location for input and output files (local path or S3 bucket).""" 

76 

77 

78@dataclass 

79class SeriesConfig: 

80 """Series configuration for initialize PhysiCell simulations flow.""" 

81 

82 name: str 

83 """Name of the simulation series.""" 

84 

85 

86@flow(name="initialize-physicell-simulations") 

87def run_flow(context: ContextConfig, series: SeriesConfig, parameters: ParametersConfig) -> None: 

88 """Main initialize PhysiCell simulations flow.""" 

89 

90 for ds in parameters.ds: 

91 # Calculate cell radius and height. 

92 cell_radius = sqrt(parameters.cell_volume / parameters.cell_height / pi) 

93 cell_height = parameters.cell_height 

94 

95 # Adjust size of bounding box. 

96 x_bound = ceil(parameters.bounding_box[0] / ds) * ds 

97 y_bound = ceil(parameters.bounding_box[1] / ds) * ds 

98 z_bound = ceil(cell_height) 

99 

100 # Generate full coordinates list. 

101 grid_bounds = (ceil(x_bound + ds), ceil(y_bound + ds), ceil(z_bound + ds)) 

102 coords_list = make_grid_coordinates(parameters.grid, grid_bounds, ds, ds) 

103 

104 # Filter coordinates list for cell coordinates. 

105 cell_coords_list = [ 

106 (x, y, z) 

107 for x, y, z in coords_list 

108 if x_bound / 2 - cell_radius <= x <= x_bound / 2 + cell_radius 

109 and y_bound / 2 - cell_radius <= y <= y_bound / 2 + cell_radius 

110 and z > 0 

111 ] 

112 cell_coords = filter_coordinate_bounds(cell_coords_list, cell_radius, center=False) 

113 cell_coords["id"] = DEFAULT_CELL_ID 

114 

115 # If substrate is included, add the z = 0 coordinates. If not, adjust 

116 # all the z positions of the cell coordinates. 

117 if parameters.substrate: 

118 substrate_coords_list = [(x, y, z) for x, y, z in coords_list if z == 0] 

119 substrate_coords = pd.DataFrame(substrate_coords_list, columns=["x", "y", "z"]) 

120 substrate_coords["id"] = SUBSTRATE_ID 

121 else: 

122 cell_coords["z"] = cell_coords["z"] - ds 

123 substrate_coords = pd.DataFrame() 

124 

125 # Save final list of coordinates. 

126 coords = pd.concat([substrate_coords, cell_coords]) 

127 init_key = make_key(series.name, "inits", "inits.PHYSICELL", f"{series.name}_{ds}.csv") 

128 save_dataframe(context.working_location, init_key, coords, index=False, header=False) 

129 

130 # Plot contact sheet of coordinates. 

131 if parameters.contact_sheet: 

132 contact_sheet = plot_contact_sheet(coords) 

133 plot_key = make_key( 

134 series.name, "plots", "plots.COORDINATES", f"{series.name}_{ds}.COORDINATES.png" 

135 ) 

136 save_figure(context.working_location, plot_key, contact_sheet)