Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 53 additions & 20 deletions src/pyVertexModel/algorithm/vertexModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,17 @@ class VertexModel:

def __init__(self, set_option='wing_disc', c_set=None, create_output_folder=True, update_derived_parameters=True):
"""
Vertex Model class.
:param c_set:
Initialize a VertexModel instance and configure simulation settings and outputs.

Parameters:
set_option (str): Name of a preset configuration to apply when no Set instance is provided.
c_set (Set | None): Preconstructed Set object to use for configuration. If None, a new Set is created and the preset named by `set_option` is applied.
create_output_folder (bool): If True and the Set defines an OutputFolder, redirect stdout/stderr to that folder.
update_derived_parameters (bool): If True and a new Set is created, call its `update_derived_parameters` method to compute dependent parameters.

Notes:
- If `set_option` does not correspond to a preset on a newly created Set, ValueError is raised.
- If the Set has `ablation` enabled, its `wound_default()` method is invoked during initialization.
"""
self.remodelled_cells = None
self.colormap_lim = None
Expand Down Expand Up @@ -254,9 +263,15 @@ def __init__(self, set_option='wing_disc', c_set=None, create_output_folder=True

def initialize(self, img_input=None):
"""
Initialize the geometry and the topology of the model.
:param img_input: Optional. Either a filename (str) or a numpy array containing the image.
If None, uses the filename from settings.
Initialize or load the model geometry and topology from settings, a file, or an image input.

If a saved state matching current settings exists it is loaded; otherwise the model is built from the provided image or filename, the tissue is resized, substrates and periodic boundary conditions are set up, reference values and degrees of freedom are initialized, copies for convergence tracking are prepared, scutoid percentage is adjusted, and an initial screenshot and state file are saved.

Parameters:
img_input: Optional image source for initialization; either a filename (str) or a numpy array. If None, the filename specified in the model settings is used.

Raises:
FileNotFoundError: If the configured initial filename does not exist and img_input is None.
"""
filename = os.path.join(PROJECT_DIRECTORY, self.set.initial_filename_state)

Expand Down Expand Up @@ -432,9 +447,12 @@ def brownian_motion(self, scale):

def iterate_over_time(self):
"""
Iterate the model over time. This includes updating the degrees of freedom, applying boundary conditions,
updating measures, and checking for convergence.
:return:
Advance the vertex model through time until the configured end time or until the solver fails to converge, performing per-step updates and saving model state.

Prepares degrees of freedom and backup state, then repeatedly performs simulation iterations via single_iteration. Saves model state (and image files when OutputFolder is set) before starting and after finishing the time loop.

Returns:
bool: `True` if the simulation did not converge before completion, `False` otherwise.
"""
if self.set.OutputFolder is not None:
temp_dir = os.path.join(self.set.OutputFolder, 'images')
Expand Down Expand Up @@ -619,9 +637,12 @@ def iteration_converged(self):

def save_v_model_state(self, file_name=None):
"""
Save the state of the vertex model.
:param file_name:
:return:
Persist current model output files (VTK exports, a screenshot, and a saved state) into the configured output folder.

If no output folder is configured on the model (`self.set.OutputFolder` is None) this function does nothing. When an output folder is present, VTK files for edges and cells are exported, a screenshot is written to an "images" subdirectory, and the model state is saved as a `.pkl` file.

Parameters:
file_name (str | None): Optional base name (without extension) for the saved state file. If omitted, the state is saved as `data_step_{numStep}.pkl`.
"""
if self.set.OutputFolder is not None:
# Create VTK files for the current state
Expand All @@ -637,8 +658,11 @@ def save_v_model_state(self, file_name=None):

def reset_noisy_parameters(self):
"""
Reset noisy parameters.
:return:
Reinitialize per-cell stochastic multipliers for mechanical and adhesion parameters.

For every cell in the current geometry, set the following *_perc attributes to 1 plus noise generated by add_noise_to_parameter using self.set.noise_random:
- lambda_s1_perc, lambda_s2_perc, lambda_s3_perc, lambda_v_perc, lambda_r_perc,
- c_line_tension_perc, k_substrate_perc, lambda_b_perc.
"""
for cell in self.geo.Cells:
cell.lambda_s1_perc = add_noise_to_parameter(1, self.set.noise_random)
Expand Down Expand Up @@ -1250,9 +1274,13 @@ def required_purse_string_strength_for_timepoint(self, directory, timepoint) ->

def find_lambda_s1_s2_equal_target_gr(self, target_energy=0.01299466280896831):
"""
Find the lmin0 value that makes the average geometric ratio equal to target_gr for all aspect ratios.
:param target_energy:
:return:
Finds values for lambdaS1 and lambdaS2 that make the model's surface adhesion energy equal to the given target.

Parameters:
target_energy (float): Target adhesion energy value to match.

Returns:
tuple(float, float): The optimized (lambdaS1, lambdaS2) values that minimize the squared deviation from target_energy.
"""

def objective(lambdas):
Expand All @@ -1272,8 +1300,12 @@ def objective(lambdas):

def create_temporary_folder(self):
"""
Create temporary folder where store every require file during the simulation.
:return:
Create a temporary output folder under PROJECT_DIRECTORY/Temp and assign it to self.set.OutputFolder.

If an output folder already exists on the model, the existing path is returned unchanged. Otherwise a new temporary directory is created inside PROJECT_DIRECTORY/Temp, assigned to self.set.OutputFolder, and its path is returned.

Returns:
str: Filesystem path of the temporary output directory (existing or newly created).
"""
if self.set.OutputFolder is not None:
logger.warning('Output folder already exists, using the existing one.')
Expand All @@ -1293,8 +1325,9 @@ def create_temporary_folder(self):

def clean_temporary_folder(self):
"""
Clean temporary folder after the simulation.
:return:
Remove the model's temporary OutputFolder and clear its reference.

If self.set.OutputFolder is set and its path contains "Temp", delete that directory and set self.set.OutputFolder to None.
"""
if self.set.OutputFolder is not None and 'Temp' in self.set.OutputFolder:
shutil.rmtree(self.set.OutputFolder)
Expand Down
17 changes: 7 additions & 10 deletions src/pyVertexModel/geometry/geo.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,15 +346,12 @@ def build_cells(self, c_set, X, twg):

def init_reference_cell_values(self, c_set):
"""
Initializes the average cell properties. This method calculates the average area of all triangles (tris) in the
geometry (Geo) structure, and sets the upper and lower area thresholds based on the standard deviation of the areas.
It also calculates the minimum edge length and the minimum area of all tris, and sets the initial values for
BarrierTri0 and lmin0 based on these calculations. The method also calculates the average edge lengths for tris
located at the top, bottom, and lateral sides of the cells. Finally, it initializes an empty list for storing
removed debris cells.

:param c_set: The settings of the simulation
:return: None
Initialize reference geometric and mechanical baseline values for the tissue.

This configures per-tissue reference data used by mechanics and topology updates: it sets AssembleNodes and non_dead_cells, initializes per-cell reference volumes/areas and optional parameter noise, updates the reference minimum edge length (lmin0) and triangle barrier (BarrierTri0), computes average edge lengths by face type, resets RemovedDebrisCells, and determines substrate/ceiling heights.

Parameters:
c_set: Simulation settings object providing parameters and state (e.g., ref_V0/ref_A0, contributionOldYs, and initial_filename_state) used during initialization.
"""
logger.info('Initializing reference cell values')

Expand Down Expand Up @@ -2273,4 +2270,4 @@ def resize_tissue(self, average_volume=0.0003168604676977124):
self.update_measures()

volumes_after_deformation = np.array([cell.Vol for cell in self.Cells if cell.AliveStatus is not None])
logger.info(f'Volume difference: {np.mean(volumes_after_deformation) - average_volume}')
logger.info(f'Volume difference: {np.mean(volumes_after_deformation) - average_volume}')
37 changes: 32 additions & 5 deletions src/pyVertexModel/parameters/set.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@

class Set:
def __init__(self, mat_file=None):
"""
Initialize simulation configuration attributes with sensible defaults.

When mat_file is None, populate a comprehensive set of simulation parameters (topology, geometry, mechanics, time stepping, substrate, viscosity, remodeling, solver, boundary/loading, postprocessing, and ablation defaults). When mat_file is provided, load parameter values from the given MATLAB-like structure via read_mat_file(mat_file).

Parameters:
mat_file (optional): A MATLAB-style struct or object containing saved Set parameters; when provided, values are read and assigned to this instance.
"""
self.min_3d_neighbours = None
self.periodic_boundaries = True
self.frozen_face_centres = False
Expand Down Expand Up @@ -158,8 +166,19 @@ def __init__(self, mat_file=None):

def check_for_non_used_parameters(self):
"""
Check for non-used parameters and put the alternative to zero
:return:
Adjust configuration attributes based on feature toggles, disabling unused options and setting related defaults.

For each boolean toggle on the Set instance, updates dependent parameters as follows:
- If EnergyBarrierA is False, sets `lambdaB` to 0.
- If EnergyBarrierAR is False, sets `lambdaR` to 0.
- If Bending is False, sets `lambdaBend` to 0.
- If Contractility_external is False, sets `cLineTension_external` to 0.
- If Contractility is False, sets `cLineTension` to 0.
- If brownian_motion is False, sets `brownian_motion_scale` to 0.
- If implicit_method is False, sets `tol` to `nu` and `tol0` to `nu/20`.
- If Remodelling is True, sets `RemodelStiffness` to 0.9 and `Remodel_stiffness_wound` to 0.7; otherwise sets both to 2.

This method mutates the instance's attributes and does not return a value.
"""
if not self.EnergyBarrierA:
self.lambdaB = 0
Expand Down Expand Up @@ -192,8 +211,9 @@ def check_for_non_used_parameters(self):

def redirect_output(self):
"""
Redirect the output to a log file in the output folder
:return:
Configure logging to write to a file under the instance's OutputFolder and ensure the required directories exist.

If self.OutputFolder is not None, this creates the OutputFolder and an images subdirectory, attaches a FileHandler writing to 'log.out' with DEBUG level and a timestamped formatter, and enables logger propagation. If OutputFolder is None, no logging configuration or filesystem changes are made.
"""
if self.OutputFolder is not None:
os.makedirs(self.OutputFolder, exist_ok=True)
Expand All @@ -206,6 +226,13 @@ def redirect_output(self):
logger.propagate = True

def read_mat_file(self, mat_file):
"""
Populate this object's attributes from a MATLAB-like structured array by mapping each top-level field to an attribute of the same name.

Parameters:
mat_file: array-like
A MATLAB-style structured array (as returned by scipy.io.loadmat for a struct). For each field in `mat_file`, the corresponding attribute on `self` is set to `None` if the field is empty, otherwise to the field's first nested value.
"""
for param in mat_file.dtype.fields:
if len(mat_file[param][0][0]) == 0:
setattr(self, param, None)
Expand Down Expand Up @@ -457,4 +484,4 @@ def copy(self):

copy_non_mutable_attributes(self, '', set_copy)

return set_copy
return set_copy