Source code for tyssue.topology.sheet_topology

import logging
import numpy as np
from functools import wraps

import warnings


from .base_topology import add_vert, collapse_edge, close_face, remove_face
from .base_topology import split_vert as base_split_vert
from tyssue.utils.decorators import do_undo, validate


logger = logging.getLogger(name=__name__)
MAX_ITER = 100


[docs]def split_vert( sheet, vert, face=None, multiplier=1.5, reindex=True, recenter=False, epsilon=None ): """Splits a vertex towards the center of the face. This operation removes the face `face` from the neighborhood of the vertex. """ # Get the value for the length of the new edge if epsilon is None: epsilon = sheet.settings.get("threshold_length", 0.1) * multiplier else: warnings.warn( "The epsilon argument is deprecated and will be removed in a future version. " "The length of the new edge should be set by " "`sheet.settings['threshold_length]*multiplier` " ) if face is None: face = np.random.choice(sheet.edge_df[sheet.edge_df["srce"] == vert]["face"]) face_edges = sheet.edge_df.query(f"face == {face}") (prev_v,) = face_edges[face_edges["trgt"] == vert]["srce"] (next_v,) = face_edges[face_edges["srce"] == vert]["trgt"] connected = sheet.edge_df[ sheet.edge_df["trgt"].isin((next_v, prev_v)) | sheet.edge_df["srce"].isin((next_v, prev_v)) ] base_split_vert(sheet, vert, face, connected, epsilon, recenter) for face_ in connected["face"]: close_face(sheet, face_) if reindex: sheet.reset_index() sheet.reset_topo() return 0
[docs]def type1_transition( sheet, edge01, *, epsilon=None, remove_tri_faces=True, multiplier=1.5 ): """Performs a type 1 transition around the edge edge01 See ../../doc/illus/t1_transition.png for a sketch of the definition of the vertices and cells letterings See Finegan et al. for a description of the algotithm https://doi.org/10.1101/704932 Parameters ---------- sheet : a `Sheet` instance edge_01 : int index of the edge around which the transition takes place epsilon : float, optional, deprecated default 0.1, the initial length of the new edge, in case "threshold_length" is not in the sheet.settings remove_tri_faces : bool, optional if True (the default), will remove triangular cells after the T1 transition is performed multiplier : float, optional default 1.5, the multiplier to the threshold length, so that the length of the new edge is set to multiplier * threshold_length """ srce, trgt, face = sheet.edge_df.loc[edge01, ["srce", "trgt", "face"]].astype(int) vert = min(srce, trgt) # find the vertex that wont be reindexed ret_code = collapse_edge(sheet, edge01, reindex=True) if ret_code != 0: warnings.warn(f"Collapse of edge {edge01} failed") return ret_code split_vert( sheet, vert, face, multiplier=multiplier, reindex=True, recenter=True, epsilon=epsilon, ) if not remove_tri_faces: return 0 # Type 1 transitions might create 3 or 2 sided cells, we remove those tri_faces = sheet.face_df[sheet.face_df["num_sides"] < 4].index i = 0 while len(tri_faces): remove_face(sheet, tri_faces[0]) tri_faces = sheet.face_df[sheet.face_df["num_sides"] < 4].index i += 1 if i > MAX_ITER: raise RecursionError return 0
[docs]def cell_division(sheet, mother, geom, angle=None): """ Causes a cell to divide Parameters ---------- sheet : a 'Sheet' instance mother : face index of target dividing cell geom : a 2D geometry angle : division angle for newly formed edge Returns ------- daughter: face index of new cell Notes ----- - Function checks for perodic boundaries if there are, it checks if dividing cell rests on an edge of the periodic boundaries if so, it displaces the boundaries by a half a period and moves the target cell in the bulk of the tissue. It then performs cell division normally and reverts the periodic boundaries to the original configuration """ if sheet.settings.get("boundaries") is not None: mother_on_periodic_boundary = False if ( sheet.face_df.loc[mother]["at_x_boundary"] or sheet.face_df.loc[mother]["at_y_boundary"] ): mother_on_periodic_boundary = True saved_boundary = sheet.specs["settings"]["boundaries"].copy() for u, boundary in sheet.settings["boundaries"].items(): if sheet.face_df.loc[mother][f"at_{u}_boundary"]: period = boundary[1] - boundary[0] sheet.specs["settings"]["boundaries"][u] = [ boundary[0] + period / 2.0, boundary[1] + period / 2.0, ] geom.update_all(sheet) if not sheet.face_df.loc[mother, "is_alive"]: logger.warning("Cell %s is not alive and cannot devide", mother) return edge_a, edge_b = get_division_edges(sheet, mother, geom, angle=angle, axis="x") if edge_a is None: return vert_a, new_edge_a, new_opp_edge_a = add_vert(sheet, edge_a) vert_b, new_edge_b, new_opp_edge_b = add_vert(sheet, edge_b) sheet.vert_df.index.name = "vert" daughter = face_division(sheet, mother, vert_a, vert_b) if sheet.settings.get("boundaries") is not None and mother_on_periodic_boundary: sheet.specs["settings"]["boundaries"] = saved_boundary geom.update_all(sheet) return daughter
[docs]def get_division_edges(sheet, mother, geom, angle=None, axis="x"): if angle is None: angle = np.random.random() * np.pi m_data = sheet.edge_df[sheet.edge_df["face"] == mother] # if angle == 0: # face_pos = sheet.face_df.loc[mother, sheet.coords] # rot_pos = sheet.vert_df[sheet.coords].copy() # for c in sheet.coords: # rot_pos.loc[:, c] = rot_pos[c] - face_pos[c] # else: rot_pos = geom.face_projected_pos(sheet, mother, psi=angle) srce_pos = rot_pos.loc[m_data["srce"], axis] srce_pos.index = m_data.index trgt_pos = rot_pos.loc[m_data["trgt"], axis] trgt_pos.index = m_data.index try: edge_a = m_data[(srce_pos < 0) & (trgt_pos >= 0)].index[0] edge_b = m_data[(srce_pos >= 0) & (trgt_pos < 0)].index[0] except IndexError: print("Failed") logger.error("Division of Cell {} failed".format(mother)) return None, None return edge_a, edge_b
[docs]def face_division(sheet, mother, vert_a, vert_b): """ Divides the face associated with edges indexed by `edge_a` and `edge_b`, splitting it in the middle of those edes. """ # mother = sheet.edge_df.loc[edge_a, 'face'] face_cols = sheet.face_df.loc[mother:mother] sheet.face_df = sheet.face_df.append(face_cols, ignore_index=True) sheet.face_df.index.name = "face" daughter = int(sheet.face_df.index[-1]) edge_cols = sheet.edge_df[sheet.edge_df["face"] == mother].iloc[0:1] sheet.edge_df = sheet.edge_df.append(edge_cols, ignore_index=True) new_edge_m = sheet.edge_df.index[-1] sheet.edge_df.loc[new_edge_m, "srce"] = vert_b sheet.edge_df.loc[new_edge_m, "trgt"] = vert_a sheet.edge_df = sheet.edge_df.append(edge_cols, ignore_index=True) new_edge_d = sheet.edge_df.index[-1] sheet.edge_df.loc[new_edge_d, "srce"] = vert_a sheet.edge_df.loc[new_edge_d, "trgt"] = vert_b # ## Discover daughter edges m_data = sheet.edge_df[sheet.edge_df["face"] == mother] daughter_edges = [new_edge_d] srce, trgt = vert_a, vert_b srces, trgts = m_data[["srce", "trgt"]].values.T while trgt != vert_a: srce, trgt = trgt, trgts[srces == trgt][0] daughter_edges.append( m_data[(m_data["srce"] == srce) & (m_data["trgt"] == trgt)].index[0] ) sheet.edge_df.loc[daughter_edges, "face"] = daughter sheet.edge_df.index.name = "edge" sheet.reset_topo() return daughter
[docs]def resolve_t1s(sheet, geom, model, solver, max_iter=60): l_th = sheet.settings["threshold_length"] i = 0 while sheet.edge_df.length.min() < l_th: for edge in ( sheet.edge_df[sheet.edge_df.length < l_th].sort_values("length").index ): try: type1_transition(sheet, edge) except KeyError: continue sheet.reset_index() sheet.reset_topo() geom.update_all(sheet) solver.find_energy_min(sheet, geom, model) i += 1 if i > max_iter: break
def _cast_to_int(df_value): if len(df_value) == 1: return int(df_value) elif len(df_value) == 0: return -1 else: raise ValueError("Trying to retrieve an integer from a more than length 1 df ")