Type 1 transition#

import pandas as pd
import numpy as np
import json
import matplotlib.pylab as plt
%matplotlib inline

from tyssue import Sheet, Monolayer, config
from tyssue import SheetGeometry as geom

# What we're here for
from tyssue.topology.sheet_topology import type1_transition
from tyssue.topology.base_topology import collapse_edge, remove_face
from tyssue.topology.sheet_topology import split_vert as sheet_split
from tyssue.topology.bulk_topology import split_vert as bulk_split
from tyssue.topology import condition_4i, condition_4ii

## model and solver
from tyssue.dynamics.sheet_vertex_model import SheetModel as model
from tyssue.solvers.quasistatic import QSSolver
from tyssue.generation import extrude
from tyssue.dynamics import model_factory, effectors

# 2D plotting
from tyssue.draw import sheet_view, highlight_cells

#I/O
from tyssue.io import hdf5
plt.style.use('bmh')

import logging


import numpy as np
import pandas as pd
import ipyvolume as ipv

import matplotlib.pylab as plt
%matplotlib inline
/tmp/ipykernel_3580/508352015.py:1: DeprecationWarning: 
Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),
(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)
but was not found to be installed on your system.
If this would cause problems for you,
please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466
        
  import pandas as pd
collision solver could not be imported You may need to install CGAL and re-install tyssue
CGAL-based mesh generation utilities not found, you may need to install CGAL and build from source
C++ extensions are not available for this version
This module needs ipyvolume to work.
You can install it with:
$ conda install -c conda-forge ipyvolume
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[1], line 35
     33 import numpy as np
     34 import pandas as pd
---> 35 import ipyvolume as ipv
     37 import matplotlib.pylab as plt
     38 get_ipython().run_line_magic('matplotlib', 'inline')

File ~/checkouts/readthedocs.org/user_builds/tyssue/conda/latest/lib/python3.12/site-packages/ipyvolume/__init__.py:8
      6 from ipyvolume import datasets  # noqa: F401
      7 from ipyvolume import embed  # noqa: F401
----> 8 from ipyvolume.widgets import *  # noqa: F401, F403
      9 from ipyvolume.transferfunction import *  # noqa: F401, F403
     10 from ipyvolume.pylab import *  # noqa: F401, F403

File ~/checkouts/readthedocs.org/user_builds/tyssue/conda/latest/lib/python3.12/site-packages/ipyvolume/widgets.py:23
     21 import ipyvolume._version
     22 from ipyvolume.traittypes import Image
---> 23 from ipyvolume.serialize import (
     24     array_cube_tile_serialization,
     25     array_serialization,
     26     array_sequence_serialization,
     27     color_serialization,
     28     texture_serialization,
     29 )
     30 from ipyvolume.transferfunction import TransferFunction
     31 from ipyvolume.utils import debounced, grid_slice, reduce_size

File ~/checkouts/readthedocs.org/user_builds/tyssue/conda/latest/lib/python3.12/site-packages/ipyvolume/serialize.py:17
     15 import ipywidgets
     16 import ipywebrtc
---> 17 from ipython_genutils.py3compat import string_types
     19 from ipyvolume import utils
     22 logger = logging.getLogger("ipyvolume")

ModuleNotFoundError: No module named 'ipython_genutils'

Type 1 transition

solver = QSSolver()

h5store = 'data/small_hexagonal.hf5'
datasets = hdf5.load_datasets(h5store,
                              data_names=['face', 'vert', 'edge'])
specs = config.geometry.cylindrical_sheet()
sheet = Sheet('emin', datasets, specs)


geom.update_all(sheet)

nondim_specs = config.dynamics.quasistatic_sheet_spec()
dim_model_specs = model.dimensionalize(nondim_specs)
sheet.update_specs(dim_model_specs, reset=True)

solver_settings = {'options': {'gtol':1e-4}}

sheet.get_opposite()
sheet.vert_df.is_active = 0

active_edges = (sheet.edge_df['opposite'] > -1)
active_verts = set(sheet.edge_df[active_edges]['srce'])

sheet.vert_df.loc[active_verts, 'is_active'] = 1

fig, ax = sheet_view(sheet, ['z', 'x'],
                     edge={'head_width': 0.5},
                     vert={'visible': False})
fig.set_size_inches(10, 6)
Reseting column contractility of the face dataset with new specs
Reseting column vol_elasticity of the face dataset with new specs
Reseting column prefered_height of the face dataset with new specs
Reseting column prefered_area of the face dataset with new specs
Reseting column prefered_vol of the face dataset with new specs
Reseting column radial_tension of the vert dataset with new specs
Reseting column is_active of the vert dataset with new specs
Reseting column ux of the edge dataset with new specs
Reseting column uy of the edge dataset with new specs
Reseting column uz of the edge dataset with new specs
Reseting column line_tension of the edge dataset with new specs
Reseting column is_active of the edge dataset with new specs
../_images/34fa473ab3e1167c028843710d37a0a8245bac9086aa7f6cc811ddbd6cfaac30.png
type1_transition(sheet, 82)
geom.update_all(sheet)
res = solver.find_energy_min(sheet, geom, model, **solver_settings)
fig, ax = sheet_view(sheet, mode="quick", coords=['z', 'x'])
../_images/ac8cf96aafc2ed0e947bffe186f18b6cf8a6f4293f08bd6c325dbbc5e8beefaa.png
fig, ax = sheet_view(sheet, ['z', 'x'], mode="quick")

ax.set_xlim(3, 10)
ax.set_ylim(-7.5, -2.5)

ax.set_aspect('equal')
fig.set_size_inches(8, 5)
../_images/21775ce9f40110dc6e693f702452a05af7e1eeabd57191567e70cae3056cc6cd.png
fig, mesh = sheet_view(sheet, mode='3D')
fig
res = solver.find_energy_min(sheet, geom, model)
print(res['success'])
fig, ax = sheet_view(sheet, ['z', 'x'], mode="quick")
True
../_images/399be6a3436ec0ffa868866f0236ed71ad39f3c321ca31fb7289b239ad5db6d0.png
sheet.validate()
True

Type 1 transitions can also be performed on border faces#

from tyssue.generation import three_faces_sheet

sheet = Sheet('3f', *three_faces_sheet())
geom.update_all(sheet)
fig, ax = sheet_view(sheet, edge={'head_width': 0.05})
fig.set_size_inches(8, 8)

for face, data in sheet.face_df.iterrows():
    ax.text(data.x, data.y, face)
for vert, data in sheet.vert_df.iterrows():
    ax.text(data.x, data.y+0.1, vert)
../_images/a40e56de5fb88a79bb6a475d0663df3599d5790d238644aebc72406afb9e06a5.png
type1_transition(sheet, 0, multiplier=2)
sheet.reset_index()

geom.update_all(sheet)

fig, ax = sheet_view(sheet, edge={'head_width': 0.05})
fig.set_size_inches(8, 8)
for face, data in sheet.face_df.iterrows():
    ax.text(data.x, data.y, face)

for vert, data in sheet.vert_df.iterrows():
    ax.text(data.x, data.y+0.1, vert)
../_images/815482199d4303ac56cea533f03820cc942a4bb611f2996ec6d73f69f2bb92d0.png
type1_transition(sheet, 16, multiplier=5)

geom.update_all(sheet)

fig, ax = sheet_view(sheet, edge={'head_width': 0.05})
fig.set_size_inches(8, 8)
for face, data in sheet.face_df.iterrows():
    ax.text(data.x, data.y, face)

for vert, data in sheet.vert_df.iterrows():
    ax.text(data.x, data.y+0.1, vert)
../_images/80a45dfb6aadd8e6dc61bbf0d2f778118e21bbe9b90e79a4216528824bbd5624.png
sheet.validate()
True
type1_transition(sheet, 17,  multiplier=5)

geom.update_all(sheet)
print(sheet.validate())

fig, ax = sheet_view(sheet, edge={'head_width': 0.05})
fig.set_size_inches(8, 8)
for face, data in sheet.face_df.iterrows():
    ax.text(data.x, data.y, face)

for vert, data in sheet.vert_df.iterrows():
    ax.text(data.x, data.y+0.1, vert)
True
../_images/5c6b8da93bd4c7707d01c3196f2915f617f844ce234626056ff2650592a6f20e.png

Rosette#

This part is based on the following paper:

The tricellular vertex-specific adhesion molecule Sidekick facilitates polarised cell intercalation during Drosophila axis extension Tara M Finegan, Nathan Hervieux, Alexander Nestor-Bergmann, Alexander G. Fletcher, Guy B Blanchard, Benedicte Sanson bioRxiv 704932; doi: https://doi.org/10.1101/704932

In particular, the authors expose a nice way to account for rosettes and solve topology changes in a more generic and I think robust way than T1 transition. I think, it allows vertices with more than 3 out-edges to have a finite lifetime, and avoids T1 oscillations

I explore its implementation in tyssue, in 2D (also maybe in 3D, though it’s less clear how :p).

from tyssue import PlanarGeometry as geom

Create a small patch of cells in 2D and a simple mechanical model#

sheet = Sheet.planar_sheet_2d('flat', 30, 30, 1, 1, noise=0.2)
to_cut = sheet.cut_out([(0.1, 6), (0.1, 6)])
sheet.remove(to_cut, trim_borders=True)
sheet.sanitize(trim_borders=True)
geom.center(sheet)
geom.update_all(sheet)
sheet.update_rank()
model = model_factory(
    [
        effectors.LineTension,
        effectors.FaceContractility,
        effectors.FaceAreaElasticity
    ]
)

specs = {
    "face": {
        "contractility": 5e-2,
        "prefered_area": sheet.face_df.area.mean(),
        "area_elasticity": 1.
    },
    "edge": {
        "line_tension": 1e-2,
        "is_active": 1
    },
    "vert": {
        "is_active": 1
    },
}

sheet.update_specs(specs, reset=True)
Reseting column is_active of the vert dataset with new specs

Gradient descent#

solver = QSSolver()

res = solver.find_energy_min(sheet, geom, model)

fig, ax = sheet_view(sheet, mode="quick")
../_images/65ec0827ff4cb781706d09bd66b1cf4efb309d08bb2ed93adfa5a8ba915e9393.png

Formation of rosettes#


first screencap, forming rosettes

Finegan et al. 2019

Merge vertices#

Or, said otherwise, collapse an edge

fig, ax = sheet_view(sheet, mode="quick", edge={"alpha": 0.5})
center_edge = sheet.edge_df.eval("sx**2 + sy**2").idxmin()
ax.scatter(sheet.edge_df.loc[center_edge, ["sx", "tx"]],
           sheet.edge_df.loc[center_edge, ["sy", "ty"]])

collapse_edge(sheet, center_edge)
sheet.update_rank()

geom.update_all(sheet)
fig, ax = sheet_view(sheet, mode="quick", ax=ax, edge={"alpha": 0.5})

print("Maximum vertex rank: ", sheet.vert_df['rank'].max())
Maximum vertex rank:  4
../_images/007faa86419035e7e5b5d48a09ef9b6c5898b1d37c70e5899b8ab89a6e3cb424.png
res = solver.find_energy_min(sheet, geom, model)
fig, ax = sheet_view(sheet, mode="quick", edge={"alpha": 0.5})
../_images/b38bbba51c2d6e70028401888410b71cdc52b85124cf4f380c29b514ca5434e7.png

Do it again to increase rank#

fig, ax = sheet_view(sheet, mode="quick", edge={"alpha": 0.5})

for i in range(4):
    center_edge = sheet.edge_df.eval("sx**2 + sy**2").idxmin()
    collapse_edge(sheet, center_edge)
    geom.update_all(sheet)
    res = solver.find_energy_min(sheet, geom, model)
    sheet.update_rank()

fig, ax = sheet_view(sheet, mode="quick", ax=ax, edge={"alpha": 0.5})


print("Maximum vertex rank: ", sheet.vert_df['rank'].max())
Maximum vertex rank:  4
../_images/22144d935df8f547f6b76b66ee56aa10852c6f7405fe4471005d6301dfa489cb.png

Rosettes resolution#


Second screencap, solving rosettes

Finegan et al. 2019

As far as I understand, the difference between rank 5+ and rank 4 is not topological, but geometrical (the “old” vertex is moved towards the opposite cell center) and statistical because \(p_4\) is higher than \(p_{5+}\)

Solve for a single edge#

#sheet = bck.copy()
max_rank = sheet.vert_df['rank'].idxmax()

print("Maximum vertex rank prior to rearangement: ", sheet.vert_df['rank'].max())


fig, ax = sheet_view(sheet, mode="quick", edge={"alpha": 0.5})
max_rank_vert = sheet.vert_df['rank'].idxmax()

ax.scatter(sheet.vert_df.loc[max_rank_vert, "x"],
           sheet.vert_df.loc[max_rank_vert, "y"])

sheet_split(sheet, max_rank_vert)
sheet.update_rank()

geom.update_all(sheet)
fig, ax = sheet_view(sheet, mode="quick", ax=ax, edge={"alpha": 0.5})

print("Maximum vertex rank after rearangement: ", sheet.vert_df['rank'].max())
Maximum vertex rank prior to rearangement:  4
Maximum vertex rank after rearangement:  4
../_images/6e846dd4a9c353eb1a1a1dca0d444031975cc03f41533b8054b2ca470e2f6f5b.png
res = solver.find_energy_min(sheet, geom, model)
fig, ax = sheet_view(sheet, mode="quick", edge={"alpha": 0.5})
../_images/9452bc95643f5e26ad8618bed9092bdbbd6fcdadcab1e607fd4373dc90ffb49b.png

Solve repeatedly#

#sheet = bck.copy()
max_rank = sheet.vert_df['rank'].max()
print("Maximum vertex rank prior to rearangement: ", max_rank)

while max_rank > 3:
    max_rank_vert = sheet.vert_df['rank'].idxmax()

    sheet_split(sheet, max_rank_vert)
    sheet.update_rank()

    geom.update_all(sheet)

    res = solver.find_energy_min(sheet, geom, model)
    max_rank = sheet.vert_df['rank'].max()

    print("Maximum vertex rank: ", max_rank)

fig, ax = sheet_view(sheet, mode="quick", edge={"alpha": 0.5})
Maximum vertex rank prior to rearangement:  4
Maximum vertex rank:  4
Maximum vertex rank:  4
Maximum vertex rank:  4
Maximum vertex rank:  3
../_images/de2d7a82e799857c1b8e4f6f51d16cca2c13c02f6fca3ffeac79572dd9eae3a4.png
assert sheet.validate()

Collapse a face and resolve the rosette#

remove_face(sheet, 12)
sheet.update_rank()
geom.update_all(sheet)

res = solver.find_energy_min(sheet, geom, model)
fig, ax = sheet_view(sheet, mode="quick", edge={"alpha": 0.5})
../_images/e441e2674094f4e41db9432274dff49d3fb416d59921755b8d8fbfb5490da50b.png
max_rank = sheet.vert_df['rank'].max()
while max_rank > 3:
    max_rank_vert = sheet.vert_df['rank'].idxmax()

    sheet_split(sheet, max_rank_vert)
    sheet.update_rank()

    geom.update_all(sheet)

    res = solver.find_energy_min(sheet, geom, model)
    max_rank = sheet.vert_df['rank'].max()

    print("Maximum vertex rank: ", max_rank)
Maximum vertex rank:  5
Maximum vertex rank:  4
Maximum vertex rank:  3
fig, ax = sheet_view(sheet, mode="quick", edge={"alpha": 0.5})
../_images/d9ec3c4a4f043dc0bb876a5a6c0242ae572f8f3d4e463870a50087f4f8eb389c.png

How about in 3D?#

Illustration of the split / collapse algorithm in 3D#

illustration of the rearangements

Extrude a monolayer from the previous sheet#

from tyssue import MonolayerGeometry as geom

# Promote the sheet to 2.5 D
sheet.update_specs(config.geometry.sheet_spec())


specs = config.geometry.bulk_spec()
datasets = extrude(sheet.datasets, method='translation')
eptm = Monolayer('mono', datasets, specs)

geom.center(eptm)
geom.update_all(eptm) 

## Sanity check
assert eptm.validate()

Setup a simple mechanical model and quasistatically descend it#

model = model_factory([
    effectors.CellVolumeElasticity,
    effectors.FaceContractility,
    effectors.LineTension,
])

## Initial model settings
dyn_specs = {
    "settings": {
        "threshold_length": 1e-3,
    },
    "cell": {
        "prefered_vol": eptm.cell_df.vol.mean(),
        "prefered_area": eptm.cell_df.area.mean(),
        "vol_elasticity": 14.0,
        #"area_elasticity": 1.0,
    },
    "face": {
        #"surface_tension": 1.0,
        "contractility": 1e-2
    },
    "edge": {
        "line_tension": 0.01,
    }
}

## Those settings are set homogenously in the epithelium
eptm.update_specs(dyn_specs, reset=True)

# Lower the lateral contractility
eptm.face_df.loc[eptm.lateral_faces, 'contractility'] /= 2

solver = QSSolver(with_t1=False, with_t3=False)
res = solver.find_energy_min(eptm, geom, model)
eptm.update_rank()

ipv.clear()
eptm.face_df["visible"] =True
fig, mesh = sheet_view(
    eptm,
    mode='3D',
    face={
        "visible": True,
        "color": eptm.face_df.area
    }
)

ipv.show()
Reseting column contractility of the face dataset with new specs
Reseting column line_tension of the edge dataset with new specs
center_edge = eptm.edge_df.query('segment == "apical"').eval("sx**2 + sy**2").idxmin()

fig, ax = sheet_view(eptm, coords = ['x', 'y'], mode="quick")
for cell, (x, y) in eptm.cell_df[['x', 'y']].iterrows():
    ax.text(x, y, str(cell))
_= ax.scatter(
    eptm.edge_df.loc[center_edge, ['sx', 'tx']],
    eptm.edge_df.loc[center_edge, ['sy', 'ty']])
../_images/b58e082a942849b6621d51fa7be54b2905af1bed4a07cff57b5e54ec88af793b.png
ret_code = collapse_edge(eptm, center_edge, allow_two_sided=True)
if ret_code:
    print('Collapse failed')
geom.update_all(eptm)

eptm.update_rank()
print("Maximum vertex rank: ", eptm.vert_df['rank'].max())

ipv.clear()
eptm.face_df["visible"] = eptm.face_df['segment'] == "lateral"
fig, mesh = sheet_view(
    eptm,
    mode='3D',
    face={
        "visible": True,
        "color": eptm.face_df.area
    }
)

ipv.show()

assert eptm.validate()


print("Maximum vertex rank: ", eptm.vert_df['rank'].max())
vert = eptm.vert_df['rank'].idxmax()
print("Rosette index: ", vert)
bck = eptm.copy()
Maximum vertex rank:  6
Maximum vertex rank:  6
Rosette index:  31
set(condition_4ii(eptm).ravel())
set()
res = solver.find_energy_min(eptm, geom, model)
eptm.settings['threshold_length'] = 0.1
bulk_split(eptm, vert, face=None, multiplier=2)

assert eptm.validate()
cell 18 is already closed
cell 22 is already closed
cell 17 is already closed
cell 12 is already closed
ipv.clear()
geom.update_all(eptm)
res = solver.find_energy_min(eptm, geom, model)

eptm.face_df["visible"] = False
#eptm.face_df["visible"] = eptm.face_df["segment"] == "lateral"
#eptm.face_df["visible"] = eptm.face_df.eval('(x**2 + (y+0.5)**2) < 0.5')
#eptm.face_df["visible"] = False
c4ii = np.unique(condition_4ii(eptm))
eptm.face_df.loc[c4ii, "visible"] = True
fig, mesh = sheet_view(
    eptm,
    mode='3D',
    face={
        "visible": True,
        "color": eptm.face_df.area
    }
)

ipv.show()
/home/admin-suz/miniconda3/lib/python3.8/site-packages/tyssue-0.8.1-py3.8-linux-x86_64.egg/tyssue/utils/utils.py:161: UserWarning: Sub epithelium appears to be empty
  warnings.warn("Sub epithelium appears to be empty")

Testing vertex split towards all possible faces#

all_edges = bck.edge_df[
    (bck.edge_df["trgt"] == vert)
    | (bck.edge_df["srce"] == vert)
]


eptms = {}
for face, edge in all_edges.reset_index().groupby("face").first()['edge'].items():
    eptm = bck.copy()
    cell = eptm.edge_df.loc[edge, "cell"]
    print(f"splitting vertex {vert} towards cell {cell} over face {face}")
    bulk_split(eptm, vert, face=face, multiplier=2)
    assert eptm.validate()
    c4i, c4ii = condition_4i(eptm), condition_4ii(eptm)
    if len(c4i):
        print(f"\nIssue with face {face}")
        print("Number of condition 4i faces : ", len(c4i))
    if len(c4ii):
        print(f"\nIssue with face {face}")
        print("Number of condition 4ii faces : ", len(c4ii))
    
    eptms[face] = eptm
cell 18 is already closed
cell 22 is already closed
cell 17 is already closed
cell 12 is already closed
splitting vertex 31 towards cell 12 over face 12
splitting vertex 31 towards cell 17 over face 17
cell 18 is already closed
cell 17 is already closed
cell 18 is already closed
splitting vertex 31 towards cell 18 over face 18
cell 17 is already closed
splitting vertex 31 towards cell 22 over face 22
cell 18 is already closed
cell 22 is already closed
cell 17 is already closed
cell 12 is already closed
cell 18 is already closed
cell 22 is already closed
cell 17 is already closed
cell 12 is already closed
splitting vertex 31 towards cell 18 over face 107
cell 18 is already closed
cell 22 is already closed
cell 17 is already closed
cell 12 is already closed
splitting vertex 31 towards cell 22 over face 108
cell 18 is already closed
splitting vertex 31 towards cell 18 over face 111
cell 17 is already closed
splitting vertex 31 towards cell 17 over face 112
cell 18 is already closed
cell 17 is already closed
cell 18 is already closed
cell 22 is already closed
cell 17 is already closed
cell 12 is already closed
splitting vertex 31 towards cell 17 over face 117
cell 18 is already closed
cell 22 is already closed
cell 17 is already closed
cell 12 is already closed
splitting vertex 31 towards cell 22 over face 118
cell 18 is already closed
cell 22 is already closed
cell 17 is already closed
cell 12 is already closed
splitting vertex 31 towards cell 12 over face 119
cell 18 is already closed
cell 22 is already closed
cell 17 is already closed
cell 12 is already closed
splitting vertex 31 towards cell 17 over face 120
cell 18 is already closed
cell 22 is already closed
cell 17 is already closed
cell 12 is already closed
splitting vertex 31 towards cell 12 over face 160
cell 18 is already closed
cell 22 is already closed
cell 17 is already closed
cell 12 is already closed
splitting vertex 31 towards cell 18 over face 161
ipv.clear()
geom.update_all(eptm)

eptm.face_df["visible"] = False
#eptm.face_df["visible"] = eptm.face_df["segment"] == "lateral"
#eptm.face_df["visible"] = eptm.face_df.eval('(x**2 + (y+0.5)**2) < 0.5')
#eptm.face_df["visible"] = False

eptm.face_df.loc[set(condition_4ii(eptm).ravel()[:2]), "visible"] = True
fig, mesh = sheet_view(
    eptm,
    mode='3D',
    face={
        "visible": True,
        "color": eptm.face_df.area
    }
)

ipv.show()
/home/admin-suz/miniconda3/lib/python3.8/site-packages/tyssue-0.8.1-py3.8-linux-x86_64.egg/tyssue/utils/utils.py:161: UserWarning: Sub epithelium appears to be empty
  warnings.warn("Sub epithelium appears to be empty")

Collapsing a face#

eptm.vert_df['rank'].min()
4
condition_4ii(eptm)
array([], shape=(0, 2), dtype=int64)
center_face = eptm.face_df.query("segment == 'apical'").eval("x**2 + y**2").idxmin()

remove_face(eptm, center_face)
0
ipv.clear()
geom.update_all(eptm)
res = solver.find_energy_min(eptm, geom, model)

#eptm.face_df["visible"] = False
eptm.face_df["visible"] = eptm.face_df["segment"] == "lateral"
#eptm.face_df["visible"] = eptm.face_df.eval('(x**2 + (y+0.5)**2) < 0.5')
#eptm.face_df["visible"] = False

#eptm.face_df.loc[set(condition_4ii(eptm).ravel()[:2]), "visible"] = True
fig, mesh = sheet_view(
    eptm,
    mode='3D',
    face={
        "visible": True,
        "color": eptm.face_df.area
    }
)

ipv.show()

Split the created vertex#

vert = eptm.vert_df.index[-1]

bulk_split(eptm, vert)


geom.update_all(eptm)

assert eptm.validate()
cell 2 is already closed
cell 7 is already closed
cell 12 is already closed
cell 18 is already closed
cell 11 is already closed
cell 16 is already closed
cell 17 is already closed
ipv.clear()
geom.update_all(eptm)
res = solver.find_energy_min(eptm, geom, model)

#eptm.face_df["visible"] = False
eptm.face_df["visible"] = eptm.face_df["segment"] == "lateral"
#eptm.face_df["visible"] = eptm.face_df.eval('(x**2 + (y+0.5)**2) < 0.5')
#eptm.face_df["visible"] = False

#eptm.face_df.loc[set(condition_4ii(eptm).ravel()[:2]), "visible"] = True
fig, mesh = sheet_view(
    eptm,
    mode='3D',
    face={
        "visible": True,
        "color": eptm.face_df.area
    }
)

ipv.show()
eptm.update_rank()

print("Maximum vertex rank: ", eptm.vert_df['rank'].max())

vert = eptm.vert_df['rank'].idxmax()
print("Rosette index: ", vert)
Maximum vertex rank:  14
Rosette index:  84

Solve repeatedly#

from itertools import count
for i in count():
    vert = eptm.vert_df['rank'].idxmax()
    try:
        bulk_split(eptm, vert)
    except ValueError:
        continue
    eptm.update_rank()
    max_rank = eptm.vert_df['rank'].max()
    print("Maximum vertex rank: ", max_rank)
    
    geom.update_all(eptm)
    res = solver.find_energy_min(eptm, geom, model)
    assert eptm.validate()
    if max_rank < 5:
        break
    
    elif i > 100:
        raise RecursionError
    
cell 7 is already closed
cell 18 is already closed
cell 22 is already closed
cell 16 is already closed
cell 21 is already closed
cell 17 is already closed
Maximum vertex rank:  12
cell 22 is already closed
cell 16 is already closed
cell 12 is already closed
cell 21 is already closed
cell 17 is already closed
Maximum vertex rank:  10
cell 18 is already closed
cell 22 is already closed
cell 11 is already closed
cell 16 is already closed
cell 21 is already closed
Maximum vertex rank:  8
cell 18 is already closed
cell 22 is already closed
cell 11 is already closed
cell 16 is already closed
cell 17 is already closed
Maximum vertex rank:  6
cell 18 is already closed
cell 22 is already closed
cell 17 is already closed
cell 11 is already closed
Maximum vertex rank:  4
ipv.clear()
geom.update_all(eptm)

#eptm.face_df["visible"] = False
eptm.face_df["visible"] = True #eptm.face_df["segment"] == "lateral"
#eptm.face_df["visible"] = eptm.face_df.eval('(x**2 + (y+0.5)**2) < 0.5')
#eptm.face_df["visible"] = False

#eptm.face_df.loc[set(condition_4ii(eptm).ravel()[:2]), "visible"] = True
fig, mesh = sheet_view(
    eptm,
    mode='3D',
    face={
        "visible": True,
        "color": eptm.face_df.area
    }
)

ipv.show()