• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Pablo1990 / pyVertexModel / 12350629645

16 Dec 2024 10:24AM UTC coverage: 45.933%. First build
12350629645

Pull #1

github

web-flow
Merge 4f65e0f38 into 6ab5c3541
Pull Request #1: Megha's changes

301 of 869 branches covered (34.64%)

Branch coverage included in aggregate %.

32 of 40 new or added lines in 10 files covered. (80.0%)

2675 of 5610 relevant lines covered (47.68%)

0.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/pyVertexModel/analysis/analyse_simulation.py
1
import os
×
2
import pickle
×
3

NEW
4
import cv2
×
5
import numpy as np
×
6
import pandas as pd
×
7
from matplotlib import pyplot as plt
×
8
from scipy.optimize import curve_fit
×
9

10
from src.pyVertexModel.algorithm.vertexModel import VertexModel, logger
×
11
from src.pyVertexModel.util.utils import load_state, load_variables, save_variables
×
12

13

14
def analyse_simulation(folder):
×
15
    """
16
    Analyse the simulation results
17
    :param folder:
18
    :return:
19
    """
20

21
    # Check if the pkl file exists
22
    if not os.path.exists(os.path.join(folder, 'features_per_time.pkl')):
×
23
        # return None, None, None, None
24
        vModel = VertexModel(create_output_folder=False)
×
25

26
        features_per_time = []
×
27
        features_per_time_all_cells = []
×
28

29
        # Go through all the files in the folder
30
        all_files = os.listdir(folder)
×
31

32
        # Sort files by date
33
        all_files = sorted(all_files, key=lambda x: os.path.getmtime(os.path.join(folder, x)))
×
34

35
        # if all_files has less than 20 files, return None
36
        if len(all_files) < 20:
×
37
            return None, None, None, None
×
38

39
        for file_id, file in enumerate(all_files):
×
40
            if file.endswith('.pkl') and not file.__contains__('data_step_before_remodelling') and not file.__contains__('recoil'):
×
41
                # Load the state of the model
42
                load_state(vModel, os.path.join(folder, file))
×
43

44
                # Analyse the simulation
45
                all_cells, avg_cells = vModel.analyse_vertex_model()
×
46
                features_per_time_all_cells.append(all_cells)
×
47
                features_per_time.append(avg_cells)
×
48

49
        if not features_per_time:
×
50
            return None, None, None, None
×
51

52
        # Export to xlsx
53
        features_per_time_all_cells_df = pd.DataFrame(np.concatenate(features_per_time_all_cells),
×
54
                                                      columns=features_per_time_all_cells[0].columns)
55
        features_per_time_all_cells_df.sort_values(by='time', inplace=True)
×
56
        features_per_time_all_cells_df.to_excel(os.path.join(folder, 'features_per_time_all_cells.xlsx'))
×
57

58
        features_per_time_df = pd.DataFrame(features_per_time)
×
59
        features_per_time_df.sort_values(by='time', inplace=True)
×
60
        features_per_time_df.to_excel(os.path.join(folder, 'features_per_time.xlsx'))
×
61

62
        # Save dataframes to a single pkl
63
        with open(os.path.join(folder, 'features_per_time.pkl'), 'wb') as f:
×
64
            pickle.dump(features_per_time_df, f)
×
65
            pickle.dump(None, f)
×
66
            pickle.dump(None, f)
×
67
            pickle.dump(features_per_time_all_cells_df, f)
×
68
    else:
69
        # Load dataframes from pkl
70
        with open(os.path.join(folder, 'features_per_time.pkl'), 'rb') as f:
×
71
            features_per_time_df = pickle.load(f)
×
72
            important_features = pickle.load(f)
×
73
            post_wound_features = pickle.load(f)
×
74
            features_per_time_all_cells_df = pickle.load(f)
×
75

76
        # load 'before_ablation.pkl' file
77
        vModel = VertexModel(create_output_folder=False)
×
78
        load_state(vModel, os.path.join(folder, 'before_ablation.pkl'))
×
79

80
    # Obtain wound edge cells features
81
    wound_edge_cells_top = vModel.geo.compute_cells_wound_edge('Top')
×
82
    wound_edge_cells_top_ids = [cell.ID for cell in wound_edge_cells_top]
×
83
    wound_edge_cells_features = features_per_time_all_cells_df[np.isin(features_per_time_all_cells_df['ID'],
×
84
                                                                     wound_edge_cells_top_ids)].copy()
85
    # Average wound edge cells features by time
86
    wound_edge_cells_features_avg = wound_edge_cells_features.groupby('time').mean().reset_index()
×
87

88
    # Add wound_edge_cells_features_avg columns to post_wound_features
89
    for col in wound_edge_cells_features_avg.columns:
×
90
        if col == 'time':
×
91
            continue
×
92
        features_per_time_df[col + '_wound_edge'] = wound_edge_cells_features_avg[col]
×
93

94
    # Obtain post-wound features
95
    post_wound_features = features_per_time_df[features_per_time_df['time'] >= vModel.set.TInitAblation]
×
96

97
    # Obtain pre-wound features
98
    try:
×
99
        pre_wound_features = features_per_time_df['time'][features_per_time_df['time'] < vModel.set.TInitAblation]
×
100
        pre_wound_features = features_per_time_df[features_per_time_df['time'] ==
×
101
                                                  pre_wound_features.iloc[-1]]
102
    except Exception as e:
×
103
        pre_wound_features = features_per_time_df['time'][features_per_time_df['time'] > features_per_time_df['time'][0]]
×
104
        pre_wound_features = features_per_time_df[features_per_time_df['time'] ==
×
105
                                                  pre_wound_features.iloc[0]]
106

107
    # Correlate wound edge cells features with wound area top
108
    try:
×
109
        wound_edge_cells_features_avg['wound_area_top'] = post_wound_features['wound_area_top'][1:].values
×
110
        correlation_matrix = wound_edge_cells_features_avg.corr()
×
111

112
        # Extract the correlation of 'wound_area_top' with other features
113
        wound_area_top_correlation = correlation_matrix['wound_area_top']
×
114

115
        # Sort the correlations in descending order to see which features correlate the most
116
        sorted_correlations = wound_area_top_correlation.sort_values(ascending=False)
×
117

118
        # Plot sorted correlations with a figure big enough on the x-axis to see the feature names
119
        plt.figure(figsize=(10, 5))
×
120
        plt.bar(sorted_correlations.index, sorted_correlations)
×
121
        plt.xticks(rotation=90)
×
122
        plt.ylabel('Correlation with wound area top')
×
123
        plt.tight_layout()
×
124
        plt.savefig(os.path.join(folder, 'correlation_wound_area_top.png'))
×
125

126
        # Export to xlsx
127
        wound_edge_cells_features_avg.to_excel(os.path.join(folder, 'features_per_time_only_wound_edge.xlsx'))
×
128
    except Exception as e:
×
129
        print('Error in correlating wound edge cells features with wound area top: ', e)
×
130

131

132
    if not post_wound_features.empty:
×
133
        # Reset time to ablation time.
134
        post_wound_features.loc[:, 'time'] = post_wound_features['time'] - vModel.set.TInitAblation
×
135

136
        post_wound_features = post_wound_features.dropna(axis=0)
×
137

138
        # Compare post-wound features with pre-wound features in percentage
139
        for feature in post_wound_features.columns:
×
140
            if np.any(np.isnan(pre_wound_features[feature])) or np.any(np.isnan(post_wound_features[feature])):
×
141
                continue
×
142

143
            if 'indentation' in feature:
×
144
                post_wound_features.loc[:, feature] = (post_wound_features[feature] - np.array(
×
145
                    pre_wound_features[feature])) * 100
146
                continue
×
147

148
            if 'wound_height' in feature:
×
149
                post_wound_features.loc[:, feature + '_'] = post_wound_features[feature] * 100
×
150

151
            if feature == 'time':
×
152
                continue
×
153

154
            post_wound_features.loc[:, feature] = (post_wound_features[feature] / np.array(
×
155
                pre_wound_features[feature])) * 100
156

157
        # Export to xlsx
158
        post_wound_features.to_excel(os.path.join(folder, 'post_wound_features.xlsx'))
×
159

160
        important_features, important_features_by_time = calculate_important_features(post_wound_features)
×
161
    else:
162
        important_features = {
×
163
            'max_recoiling_top': np.nan,
164
            'max_recoiling_time_top': np.nan,
165
            'min_height_change': np.nan,
166
            'min_height_change_time': np.nan,
167
        }
168
        important_features_by_time = {}
×
169

170
    # Export to xlsx
171
    df = pd.DataFrame([important_features])
×
172
    df.to_excel(os.path.join(folder, 'important_features.xlsx'))
×
173

174
    df = pd.DataFrame(important_features_by_time)
×
175
    df.to_excel(os.path.join(folder, 'important_features_by_time.xlsx'))
×
176

177
    # Plot wound area top evolution over time and save it to a file
178
    plot_feature(folder, post_wound_features, name_columns=['wound_area_top', 'wound_area_bottom'])
×
179
    plot_feature(folder, post_wound_features, name_columns='num_cells_wound_edge_top')
×
180
    plot_feature(folder, post_wound_features, name_columns='wound_height_')
×
181
    plot_feature(folder, post_wound_features, name_columns=['Volume', 'Volume_wound_edge'])
×
182
    try:
×
183
        plot_feature(folder, post_wound_features, name_columns=['wound_indentation_top', 'wound_indentation_bottom'])
×
184
    except Exception as e:
×
185
        pass
×
186
    plot_feature(folder, post_wound_features, name_columns='wound_area_bottom')
×
187

188
    return features_per_time_df, post_wound_features, important_features, features_per_time_all_cells_df
×
189

190

191
def plot_feature(folder, post_wound_features, name_columns):
×
192
    """
193
    Plot a feature and save it to a file
194
    :param folder:
195
    :param post_wound_features:
196
    :param name_columns:
197
    :return:
198
    """
199
    plt.figure()
×
200
    # Check if name_columns is an array to go through it
201
    if isinstance(name_columns, list):
×
202
        for name in name_columns:
×
203
            plt.plot(post_wound_features['time'], post_wound_features[name], label=name.replace('_', ' '))
×
204
        plt.legend()
×
205

206
        if not name_columns[0].startswith('wound_indentation_') and not name_columns[0].startswith('Volume'):
×
207
            plt.ylim([0, 250])
×
208
    else:
209
        plt.plot(post_wound_features['time'], post_wound_features[name_columns], 'k')
×
210
        plt.ylabel(name_columns)
×
211
        if name_columns == 'wound_height_':
×
212
            plt.ylim([0, 50])
×
213
        if not name_columns.startswith('wound_indentation_') and name_columns != 'wound_height_':
×
214
            plt.ylim([0, 250])
×
215

216
    plt.xlabel('Time (h)')
×
217

218
    # Change axis limits
219
    if np.max(post_wound_features['time']) > 60:
×
220
        #plt.xlim([0, np.max(post_wound_features['time'])])
221
        plt.xlim([0, 60])
×
222
    else:
223
        plt.xlim([0, 60])
×
224

225
    if isinstance(name_columns, list):
×
226
        plt.savefig(os.path.join(folder, '_'.join(name_columns) + '.png'))
×
227
    else:
228
        plt.savefig(os.path.join(folder, name_columns + '.png'))
×
229
    plt.close()
×
230

231

232
def calculate_important_features(post_wound_features):
×
233
    """
234
    Calculate important features from the post-wound features
235
    :param post_wound_features:
236
    :return:
237
    """
238
    # Obtain important features for post-wound
239
    if not post_wound_features['wound_area_top'].empty and post_wound_features['time'].iloc[-1] > 4:
×
240
        important_features = {
×
241
            'max_recoiling_top': np.max(post_wound_features['wound_area_top']),
242
            'max_recoiling_time_top': np.array(post_wound_features['time'])[
243
                np.argmax(post_wound_features['wound_area_top'])],
244
            'last_area_top': post_wound_features['wound_area_top'].iloc[-1],
245
            'last_area_time_top': post_wound_features['time'].iloc[-1],
246
        }
247

248
        # Extrapolate features to a given time
249
        times_to_extrapolate = np.arange(0, 61)
×
250
        columns_to_extrapolate = {'wound_area_top', 'wound_area_bottom', 'wound_indentation_top', 'wound_indentation_bottom', 'Volume', 'Volume_wound_edge' , 'num_cells_wound_edge_top'}  # post_wound_features.columns
×
251

252
        important_features_by_time = {}
×
253
        for feature in columns_to_extrapolate:
×
254
            for time in times_to_extrapolate:
×
255
                # Extrapolate results to a given time
256
                important_features[feature + '_extrapolated_' + str(time)] = np.interp(time,
×
257
                                                                                       post_wound_features['time'],
258
                                                                                       post_wound_features[feature])
259

260
            important_features_by_time[feature] = [important_features[feature + '_extrapolated_' + str(time)] for time in times_to_extrapolate]
×
261

262
    else:
263
        important_features = {
×
264
            'max_recoiling_top': np.nan,
265
            'max_recoiling_time_top': np.nan,
266
        }
267

268
    return important_features, important_features_by_time
×
269

270

271
def analyse_edge_recoil(file_name_v_model, type_of_ablation='recoil_edge_info_apical', n_ablations=2, location_filter=0, t_end=0.5):
×
272
    """
273
    Analyse how much an edge recoil if we ablate an edge of a cell
274
    :param type_of_ablation:
275
    :param t_end: Time to iterate after the ablation
276
    :param file_name_v_model: file nae of the Vertex model
277
    :param n_ablations: Number of ablations to perform
278
    :param location_filter: Location filter
279
    :return:
280
    """
281

282
    v_model = VertexModel(create_output_folder=False)
×
283
    load_state(v_model, file_name_v_model)
×
284

285
    # Cells to ablate
286
    # cell_to_ablate = np.random.choice(possible_cells_to_ablate, 1)
287
    cell_to_ablate = [v_model.geo.Cells[0]]
×
288

289
    #Pick the neighbouring cell to ablate
290
    neighbours = cell_to_ablate[0].compute_neighbours(location_filter)
×
291

292
    # Random order of neighbours
293
    np.random.seed(0)
×
294
    np.random.shuffle(neighbours)
×
295

296
    list_of_dicts_to_save = []
×
297
    for num_ablation in range(n_ablations):
×
298
        load_state(v_model, file_name_v_model)
×
299
        try:
×
300
            vars = load_variables(file_name_v_model.replace('before_ablation.pkl', type_of_ablation + '.pkl'))
×
301
            list_of_dicts_to_save_loaded = vars['recoiling_info_df_apical']
×
302

303
            cell_to_ablate_ID = list_of_dicts_to_save_loaded['cell_to_ablate'][num_ablation]
×
304
            neighbour_to_ablate_ID = list_of_dicts_to_save_loaded['neighbour_to_ablate'][num_ablation]
×
305
            edge_length_init = list_of_dicts_to_save_loaded['edge_length_init'][num_ablation]
×
306
            edge_length_final = list_of_dicts_to_save_loaded['edge_length_final'][num_ablation]
×
307
            if 'edge_length_final_normalized' in list_of_dicts_to_save_loaded:
×
308
                edge_length_final_normalized = list_of_dicts_to_save_loaded['edge_length_final_normalized'][
×
309
                    num_ablation]
310
            else:
311
                edge_length_final_normalized = (edge_length_final - edge_length_init) / edge_length_init
×
312

313
            initial_recoil = list_of_dicts_to_save_loaded['initial_recoil_in_s'][num_ablation]
×
314
            K = list_of_dicts_to_save_loaded['K'][num_ablation]
×
315
            scutoid_face = list_of_dicts_to_save_loaded['scutoid_face'][num_ablation]
×
316
            distance_to_centre = list_of_dicts_to_save_loaded['distance_to_centre'][num_ablation]
×
317
            if 'time_steps' in list_of_dicts_to_save_loaded:
×
318
                time_steps = list_of_dicts_to_save_loaded['time_steps'][num_ablation]
×
319
            else:
320
                time_steps = np.arange(0, len(edge_length_final)) * 6
×
321

322
            if edge_length_final[0] == 0:
×
323
                # Remove the first element
324
                edge_length_final = edge_length_final[1:]
×
325
                time_steps = time_steps[1:]
×
326
        except Exception as e:
×
327
            logger.info('Performing the analysis...' + str(e))
×
328
            # Change name_columns of folder and create it
329
            if type_of_ablation == 'recoil_info_apical':
×
330
                v_model.set.OutputFolder = v_model.set.OutputFolder + '_ablation_' + str(num_ablation)
×
331
            else:
332
                v_model.set.OutputFolder = v_model.set.OutputFolder + '_ablation_edge_' + str(num_ablation)
×
333

334
            if not os.path.exists(v_model.set.OutputFolder):
×
335
                os.mkdir(v_model.set.OutputFolder)
×
336

337
            neighbour_to_ablate = [neighbours[num_ablation]]
×
338

339
            # Calculate if the cell is neighbour on both sides
340
            scutoid_face = None
×
341
            neighbours_other_side = []
×
342
            if location_filter == 0:
×
343
                neighbours_other_side = cell_to_ablate[0].compute_neighbours(location_filter=2)
×
344
                scutoid_face = np.nan
×
345
            elif location_filter == 2:
×
346
                neighbours_other_side = cell_to_ablate[0].compute_neighbours(location_filter=0)
×
347
                scutoid_face = np.nan
×
348

349
            if scutoid_face is not None:
×
350
                if neighbour_to_ablate[0] in neighbours_other_side:
×
351
                    scutoid_face = True
×
352
                else:
353
                    scutoid_face = False
×
354

355
            # Get the centre of the tissue
356
            centre_of_tissue = v_model.geo.compute_centre_of_tissue()
×
357
            neighbour_to_ablate_cell = [cell for cell in v_model.geo.Cells if cell.ID == neighbour_to_ablate[0]][0]
×
358
            distance_to_centre = np.mean([cell_to_ablate[0].compute_distance_to_centre(centre_of_tissue),
×
359
                                          neighbour_to_ablate_cell.compute_distance_to_centre(centre_of_tissue)])
360

361
            # Pick the neighbour and put it in the list
362
            cells_to_ablate = [cell_to_ablate[0].ID, neighbour_to_ablate[0]]
×
363

364
            # Get the edge that share both cells
365
            edge_length_init = v_model.geo.get_edge_length(cells_to_ablate, location_filter)
×
366

367
            # Ablate the edge
368
            v_model.set.ablation = True
×
369
            v_model.geo.cellsToAblate = cells_to_ablate
×
370
            v_model.set.TInitAblation = v_model.t
×
371
            if type_of_ablation == 'recoil_info_apical':
×
372
                v_model.geo.ablate_cells(v_model.set, v_model.t, combine_cells=False)
×
373
                v_model.geo.y_ablated = []
×
374
            elif type_of_ablation == 'recoil_edge_info_apical':
×
375
                v_model.geo.y_ablated = v_model.geo.ablate_edge(v_model.set, v_model.t, domain=location_filter,
×
376
                                                                adjacent_surface=False)
377

378
            # Relax the system
379
            initial_time = v_model.t
×
380
            v_model.set.tend = v_model.t + t_end
×
381
            if type_of_ablation == 'recoil_info_apical':
×
382
                v_model.set.dt = 0.005
×
383
            elif type_of_ablation == 'recoil_edge_info_apical':
×
384
                v_model.set.dt = 0.005
×
385

386
            v_model.set.Remodelling = False
×
387

388
            v_model.set.dt0 = v_model.set.dt
×
389
            if type_of_ablation == 'recoil_edge_info_apical':
×
390
                v_model.set.RemodelingFrequency = 0.05
×
391
            else:
392
                v_model.set.RemodelingFrequency = 100
×
393
            v_model.set.ablation = False
×
394
            v_model.set.export_images = True
×
395
            if v_model.set.export_images and not os.path.exists(v_model.set.OutputFolder + '/images'):
×
396
                os.mkdir(v_model.set.OutputFolder + '/images')
×
397
            edge_length_final_normalized = []
×
398
            edge_length_final = []
×
399
            recoil_speed = []
×
400
            time_steps = []
×
401

402
            # if os.path.exists(v_model.set.OutputFolder):
403
            #     list_of_files = os.listdir(v_model.set.OutputFolder)
404
            #     # Get file modification times and sort files by date
405
            #     files_with_dates = [(file, os.path.getmtime(os.path.join(v_model.set.OutputFolder, file))) for file in
406
            #                         list_of_files]
407
            #     files_with_dates.sort(key=lambda x: x[1])
408
            #     for file in files_with_dates:
409
            #         load_state(v_model, os.path.join(v_model.set.OutputFolder, file[0]))
410
            #         compute_edge_length_v_model(cells_to_ablate, edge_length_final, edge_length_final_normalized,
411
            #                                     edge_length_init, initial_time, location_filter, recoil_speed,
412
            #                                     time_steps,
413
            #                                     v_model)
414

415
            while v_model.t <= v_model.set.tend and not v_model.didNotConverge:
×
416
                gr = v_model.single_iteration()
×
417

418
                compute_edge_length_v_model(cells_to_ablate, edge_length_final, edge_length_final_normalized,
×
419
                                            edge_length_init, initial_time, location_filter, recoil_speed, time_steps,
420
                                            v_model)
421

422
                if np.isnan(gr):
×
423
                    break
×
424

425
            cell_to_ablate_ID = cell_to_ablate[0].ID
×
426
            neighbour_to_ablate_ID = neighbour_to_ablate[0]
×
427

428
        K, initial_recoil, error_bars = fit_ablation_equation(edge_length_final, time_steps)
×
429

430
        # Generate a plot with the edge length final and the fit for each ablation
431
        plt.figure()
×
432
        plt.plot(time_steps, edge_length_final_normalized, 'o')
×
433
        # Plot fit line of the Kelvin-Voigt model
434
        plt.plot(time_steps, recoil_model(np.array(time_steps), initial_recoil, K), 'r')
×
435
        plt.xlabel('Time (s)')
×
436
        plt.ylabel('Edge length final')
×
437
        plt.title('Ablation fit - ' + str(cell_to_ablate_ID) + ' ' + str(neighbour_to_ablate_ID))
×
438

439
        # Save plot
440
        if type_of_ablation == 'recoil_info_apical':
×
441
            plt.savefig(
×
442
                os.path.join(file_name_v_model.replace('before_ablation.pkl', 'ablation_fit_' + str(num_ablation) + '.png'))
443
            )
444
        elif type_of_ablation == 'recoil_edge_info_apical':
×
445
            plt.savefig(
×
446
                os.path.join(file_name_v_model.replace('before_ablation.pkl', 'ablation_edge_fit_' + str(num_ablation) + '.png'))
447
            )
448
        plt.close()
×
449

450
        # Save the results
451
        dict_to_save = {
×
452
            'cell_to_ablate': cell_to_ablate_ID,
453
            'neighbour_to_ablate': neighbour_to_ablate_ID,
454
            'edge_length_init': edge_length_init,
455
            'edge_length_final': edge_length_final,
456
            'edge_length_final_normalized': edge_length_final_normalized,
457
            'initial_recoil_in_s': initial_recoil,
458
            'K': K,
459
            'scutoid_face': scutoid_face,
460
            'location_filter': location_filter,
461
            'distance_to_centre': distance_to_centre,
462
            'time_steps': time_steps,
463
        }
464
        list_of_dicts_to_save.append(dict_to_save)
×
465

466
    recoiling_info_df_apical = pd.DataFrame(list_of_dicts_to_save)
×
467
    recoiling_info_df_apical.to_excel(file_name_v_model.replace('before_ablation.pkl', type_of_ablation+'.xlsx'))
×
468
    save_variables({'recoiling_info_df_apical': recoiling_info_df_apical},
×
469
                   file_name_v_model.replace('before_ablation.pkl', type_of_ablation+'.pkl'))
470

471
    return list_of_dicts_to_save
×
472

473

474
def recoil_model(x, initial_recoil, K):
×
475
    """
476
    Model of the recoil based on a Kelvin-Voigt model
477
    :param x:
478
    :param initial_recoil:
479
    :param K:
480
    :return:   Recoil
481
    """
482
    return (initial_recoil / K) * (1 - np.exp(-K * x))
×
483

484

485
def fit_ablation_equation(edge_length_final_normalized, time_steps):
×
486
    """
487
    Fit the ablation equation. Thanks to Veronika Lachina.
488
    :param edge_length_final_normalized:
489
    :param time_steps:
490
    :return:    K, initial_recoil
491
    """
492

493
    # Normalize the edge length
494
    edge_length_init = edge_length_final_normalized[0]
×
495
    edge_length_final_normalized = (edge_length_final_normalized - edge_length_init) / edge_length_init
×
496

497
    # Fit the model to the data
498
    [params, covariance] = curve_fit(recoil_model, time_steps, edge_length_final_normalized,
×
499
                                     p0=[0.00001, 3], bounds=(0, np.inf))
500

501
    # Get the error
502
    error_bars = np.sqrt(np.diag(covariance))
×
503

504
    initial_recoil, K = params
×
505
    return K, initial_recoil, error_bars
×
506

507

508
def compute_edge_length_v_model(cells_to_ablate, edge_length_final, edge_length_final_normalized, edge_length_init,
×
509
                                initial_time, location_filter, recoil_speed, time_steps, v_model):
510
    """
511
    Compute the edge length of the edge that share the cells_to_ablate
512
    :param cells_to_ablate:
513
    :param edge_length_final:
514
    :param edge_length_final_normalized:
515
    :param edge_length_init:
516
    :param initial_time:
517
    :param location_filter:
518
    :param recoil_speed:
519
    :param time_steps:
520
    :param v_model:
521
    :return:
522
    """
523
    if v_model.t == initial_time:
×
524
        return
×
525
    # Get the edge length
526
    edge_length_final.append(v_model.geo.get_edge_length(cells_to_ablate, location_filter))
×
527
    edge_length_final_normalized.append((edge_length_final[-1] - edge_length_init) / edge_length_init)
×
528
    print('Edge length final: ', edge_length_final[-1])
×
529
    # In seconds. 1 t = 1 minute = 60 seconds
530
    time_steps.append((v_model.t - initial_time) * 60)
×
531
    # Calculate the recoil
532
    recoil_speed.append(edge_length_final_normalized[-1] / time_steps[-1])
×
533

534
def create_video(folder, name_containing_images='top_'):
×
535
    """
536
    Create a video of the images in the folder
537
    :param folder:
538
    :return:
539
    """
540

541
    # Get the images
542
    images = [img for img in os.listdir(folder) if img.endswith(".png") and name_containing_images in img]
×
543
    # Filter if the image is not a number
544
    images = [img for img in images if img.split('_')[-1].split('.')[0].isdigit()]
×
545
    images.sort(key=lambda x: int(x.split('_')[-1].split('.')[0]))
×
546

547
    # Determine the width and height from the first image
548
    image_path = os.path.join(folder, images[0])
×
549
    frame = cv2.imread(image_path)
×
550
    height, width, layers = frame.shape
×
551

552
    # Define the codec and create VideoWriter object
553
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')  # Use 'mp4v' for MP4
×
554
    video = cv2.VideoWriter(os.path.join(folder, name_containing_images + 'video.mp4'), fourcc, 7, (width, height))
×
555

556
    for image in images:
×
557
        video.write(cv2.imread(os.path.join(folder, image)))
×
558

559
    cv2.destroyAllWindows()
×
560
    video.release()
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc