Basic Usage of the tyssue library

Easy creation of a 2D epithelial sheet

[1]:
%matplotlib inline

# Core object
from tyssue import Sheet
# Simple 2D geometry
from tyssue import PlanarGeometry as geom
# Visualisation
from tyssue.draw import sheet_view

sheet = Sheet.planar_sheet_2d('basic2D', nx=6, ny=7,
                              distx=1, disty=1)
geom.update_all(sheet)


[2]:
fig, ax = sheet_view(sheet, mode="2D")
fig.set_size_inches(8, 8)
../_images/notebooks_Basic_Usage_3_0.png

We can have a cleaner, better order sheet with the sanitize method:

[3]:
# Give the tissue a nice hear cut ;)
sheet.sanitize(trim_borders=True, order_edges=True)
geom.update_all(sheet)
[4]:
fig, ax = sheet_view(sheet, mode="2D")
fig.set_size_inches(8, 8)
../_images/notebooks_Basic_Usage_6_0.png

A remark on the half-edge data structure

As is represented in the above graph, each edge between two cells is composed of two half-edges (only one half-edge is present in the border ones). This makes it easier to compute many cell-specific quantities, as well as keeping a well oriented mesh. This is inspired by CGAL polyhedral surfaces.

Datasets and specifications

The data associated with the mesh displayed above, i.e. the points positions, the connectivity information, etc. is stored in pandas DataFrame objects, hold together in the datasets dictionnary.

Depending on the geometry, the following dataframes are populated: * datasets["edge"] or sheet.edge_df: The edge related dataframe contains - the connectivity information: source and target vertices, associated face and (for thick tissues) the associated cell body. - geometry data associated with the edge, such as its length - any suplemental data, such as a color or a dynamical parameter (an elasticity for example)

  • datasets["vert"] or sheet.vert_df: The vertices related dataframe. In the apical junction mesh above, those are the vertices at the cells junctions. It usually holds the coordinates of the points, and geometry or dynamical data.

  • datasets["face"] or sheet.face_df: The faces related dataframe. For a thin, 2D tissue, this corresponds to a cell of the epithelium, delimited by its edges. In thick, 3D models, one cell has several faces (the apical, sagittal and basal ones for a 3D monolayer, for example).

  • datasets["cell"] or sheet.cell_df: The cells related dataframe, only for 3D, thick, epithelium. Each cell have several faces.

    datastructures in ``tyssue``

[5]:
for element, data in sheet.datasets.items():
    print(element, ':', data.shape)
vert : (48, 3)
edge : (128, 20)
face : (25, 6)
[6]:
sheet.datasets['edge'].head()
[6]:
trgt nz length face srce dx dy sx sy tx ty fx fy ux uy rx ry sub_area is_valid phi
edge
0 30 0.28125 0.750000 0 28 0.0 0.75 1.5 0.625 1.5 1.375 1.125 1.250 0.0 0.0 0.375 -0.625 0.140625 True -0.643501
1 9 0.15625 0.559017 0 30 -0.5 0.25 1.5 1.375 1.0 1.625 1.125 1.250 0.0 0.0 0.375 0.125 0.078125 True 0.643501
2 5 0.21875 0.559017 0 9 -0.5 -0.25 1.0 1.625 0.5 1.375 1.125 1.250 0.0 0.0 -0.125 0.375 0.109375 True 1.570796
3 28 0.34375 1.250000 0 5 1.0 -0.75 0.5 1.375 1.5 0.625 1.125 1.250 0.0 0.0 -0.625 0.125 0.171875 True 1.570796
4 43 0.37500 0.750000 1 29 0.0 0.75 2.5 0.625 2.5 1.375 2.000 1.125 0.0 0.0 0.500 -0.500 0.187500 True -0.643501

The edge_df dataframe contains most of the information. In particular, each time the geometry is updated with the geom.update_all(sheet) function, the positions of the source and target vertices of each edge are copied to the "sx", "sy" and "tx", "ty" columns, respectively.

[7]:
sheet.face_df.head()
[7]:
y is_alive perimeter area x num_sides
face
0 1.250000 1 3.118034 0.5000 1.125000 4
1 1.125000 1 3.618034 0.8750 2.000000 5
2 1.125000 1 3.618034 0.8750 3.000000 5
3 1.125000 1 3.618034 0.8750 4.000000 5
4 1.208333 1 2.427051 0.1875 4.666667 3

We can use all the goodies from pandas DataFrames objects. For example, it is possible to compute the average edge length for each face like so:

[8]:
sheet.edge_df.groupby('face')['length'].mean().head()
[8]:
face
0    0.779508
1    0.723607
2    0.723607
3    0.723607
4    0.809017
Name: length, dtype: float64

Specifications are defined as a nested dictionnary, sheet.specs. For each element, the specification defines the columns of the corresonding DataFrame and their default values. An extra key at the root of the specification is called "settings", and can hold specific parameters, for example the arguments for an energy minimization procedure.

[9]:
sheet.specs
[9]:
{'edge': {'trgt': 0,
  'nz': 0.0,
  'length': 1.0,
  'face': 0,
  'srce': 0,
  'dx': 0.0,
  'dy': 0.0,
  'sx': 0.0,
  'sy': 0.0,
  'tx': 0.0,
  'ty': 0.0,
  'fx': 0.0,
  'fy': 0.0,
  'ux': 0.0,
  'uy': 0.0},
 'vert': {'y': 0.0, 'is_active': 1, 'x': 0.0},
 'face': {'y': 0.0,
  'is_alive': 1,
  'perimeter': 0.0,
  'area': 0.0,
  'x': 0.0,
  'num_sides': 6},
 'settings': {'geometry': 'planar'}}

It is possible to update dynamically the data structure. For example, let’s assume we want to put quasi-static model to minimize energy for the tyssue. For this we need three things. * A specification dictionnary with the required data * A model * A solver

[10]:
from tyssue.config.dynamics import quasistatic_plane_spec
from tyssue.dynamics.planar_vertex_model import PlanarModel
from tyssue.solvers import QSSolver

# Update the specs
sheet.update_specs(quasistatic_plane_spec())

# Find energy minimum
solver = QSSolver()
res = solver.find_energy_min(sheet,
                             geom,
                             PlanarModel)

print("Successfull gradient descent? ", res['success'])
fig, ax = sheet_view(sheet)
/home/docs/checkouts/readthedocs.org/user_builds/tyssue/conda/stable/lib/python3.7/site-packages/tyssue/utils/utils.py:35: FutureWarning: Support for multi-dimensional indexing (e.g. `obj[:, None]`) is deprecated and will be removed in a future version.  Convert to a numpy array before indexing instead.
  df_nd = df[:, None]#np.asarray(df).repeat(ndim).reshape((df.size, ndim))
Successfull gradient descent?  True
../_images/notebooks_Basic_Usage_18_2.png

Upcasting and downcasting data

Upcasting

Geometry or physics computations often require to access for example the cell related data on each of the cell’s edges. The Epithelium class and its derivatives defines utilities to make this, i.e copying the area of each face to each of its edges:

[11]:
print('Faces associated with the first edges:')
print(sheet.edge_df['face'].head())
print('\n')

# First edge associated face
face = sheet.edge_df.loc[0, 'face']

print('Area of cell # {}:'.format(int(face)))
print(sheet.face_df.loc[face, 'area'])

print('\n')
print('Upcasted areas over the edges:')
print(sheet.upcast_face(sheet.face_df['area']).head())
Faces associated with the first edges:
edge
0    0
1    0
2    0
3    0
4    1
Name: face, dtype: int64


Area of cell # 0:
0.43544708469079896


Upcasted areas over the edges:
edge
0    0.435447
1    0.435447
2    0.435447
3    0.435447
4    0.562091
Name: area, dtype: float64

The values have indeed be upcasted. This can also be done with the source and target vertices (sheet.upcast_srce, sheet.upcast_trgt) and cells in the 3D case (sheet.upcast_cell).

Downcasting

This is usually done by groupby operations as shown above. Syntactic sugar is available for summation, e.g. over every edges with a given source:

[12]:
sheet.sum_srce(sheet.edge_df['line_tension']).head()
[12]:
line_tension
srce
0 0.36
1 0.24
2 0.24
3 0.36
4 0.36

Input and Output

The ‘native’ format is to save the datasets to hdf5 via `pandas.HDFStore <https://pandas.pydata.org/pandas-docs/stable/cookbook.html#hdfstore>`__. The io.obj also provides functions to export the junction mesh or triangulations to the wavefront OBJ format (requires vispy), for easy import in 3D software such as Blender.

Here is the code to save the data in wavefront OBJ:

obj.save_junction_mesh('junctions.obj', sheet)

The standard data format for the datasets is HDF:

[13]:
from tyssue.io import hdf5
[14]:
hdf5.save_datasets('tmp_data.hdf5', sheet)

[15]:
dsets = hdf5.load_datasets('tmp_data.hdf5')
sheet2 = Sheet('reloaded', dsets)
[16]:
!rm tmp_data.hdf5

Specs can be saved as json files:

[17]:
import json

with open("tmp_specs.json", "w") as jh:
    json.dump(sheet.specs, jh)

And reloaded accordingly

[18]:
with open("tmp_specs.json", "r") as jh:
    specs = json.load(jh)

sheet2.update_specs(specs, reset=False)
[19]:
!rm tmp_specs.json
[ ]:

[ ]: