<?xml version='1.0' encoding='UTF-8'?>
<MorpheusModel version="4">
    <Description>
        <Details>Full title:		Guidance by followers ensures long-range coordination of cell migration through α-catenin mechanoperception
Date:		20.06.2022
Authors:		A. Boutillon, S. Escot, A. Elouin, D. Jahn, S. González-Tirado, J. Starruß, L. Brusch, N. B. David
Contributors:	D. Jahn
Software:       	Morpheus (open-source). Download from https://morpheus.gitlab.io
Time unit:		min
Model ID:		https://identifiers.org/morpheus/M0006
Comment:		The migration of zebrafish polster cells is oriented by the migration of follower cells, allowing robust long-range coordination of different cell populations.
                    		This model is the original used in the publication, up to technical updates.
Reference:		A. Boutillon, S. Escot, A. Elouin, D. Jahn, S. González-Tirado, J. Starruß, L. Brusch, N. B. David: Guidance by followers ensures long-range coordination of cell migration through α-catenin mechanoperception. Dev. Cell 57 (12): 1529-1544.e5, 2022.
                         	https://doi.org/10.1371/journal.pcbi.1009231

</Details>
        <Title>Guidance by Followers</Title>
    </Description>
    <Space>
        <Lattice class="hexagonal">
            <Neighborhood>
                <Order>1</Order>
            </Neighborhood>
            <Size symbol="size" value="500, 1500, 0"/>
            <BoundaryConditions>
                <Condition type="periodic" boundary="x"/>
                <Condition type="periodic" boundary="y"/>
            </BoundaryConditions>
        </Lattice>
        <SpaceSymbol symbol="space"/>
        <MembraneLattice>
            <Resolution symbol="membrane_size" value="50"/>
            <SpaceSymbol symbol="membrane_pos"/>
        </MembraneLattice>
    </Space>
    <Time>
        <StartTime value="0"/>
        <StopTime value="100"/>
        <TimeSymbol symbol="time"/>
    </Time>
    <Global>
        <Constant symbol="init_time" name="Time for cell initialization" value="20"/>
        <Constant symbol="directed_motion_strength_global" name="Speed of axial cells" value="0.5">
            <Annotation>Strength of the DirectedMotion plugin</Annotation>
        </Constant>
        <Constant symbol="run_duration_adjustment" name="Mean duration of run phases" tags="RunandTumble" value="0.76"/>
        <Constant symbol="RandT_or_Mech_motion_strength_global" name="Speed of polster cells" value="0.5">
            <Annotation>Strength of the DirectedMotion plugin</Annotation>
        </Constant>
        <Constant symbol="max_angle" name="Max. angle of influence" tags="guidance_by_followers" value="pi/6">
            <Annotation>Angle between cell movement and direction to the neighbor. Below max_angle, the cell is considered as moving towards the neighbouring cell and will influence its direction</Annotation>
        </Constant>
        <Constant symbol="min_push_velocity" name="Min. velocity of influence" tags="guidance_by_followers" value="0.1">
            <Annotation>Minimal velocity for a cell to influence its neighbors</Annotation>
        </Constant>
        <VariableVector symbol="velocity" value="0.0, 0.0, 0.0">
            <Annotation>Needed for NeighborhoodReporters neighbor_velocity_x and neighbor_velocity_y</Annotation>
        </VariableVector>
    </Global>
    <CellTypes>
        <CellType class="biological" name="cell">
            <Property symbol="color" name="Set cell colors" value="1">
                <Annotation>Cell colors:

- white: uninitialized cells (time >= init_time)
- yellow: axial cells (RandT_or_Mech_motion_strength == 0)
- green: polster cells (RandT_or_Mech_motion_strength > 0)</Annotation>
            </Property>
            <SurfaceConstraint target="1" strength="1" mode="aspherity"/>
            <Property symbol="target_volume" value="326.0"/>
            <VolumeConstraint target="target_volume" strength="1"/>
            <Property symbol="directed_motion_strength" name="Strength of the directed motion" value="0.0"/>
            <Event name="Axial cell initialization">
                <Annotation>After init_time, set the properties of axial cells</Annotation>
                <Condition>time == init_time and cell.center.y &lt;= 360</Condition>
                <Rule symbol-ref="directed_motion_strength">
                    <Expression>directed_motion_strength_global</Expression>
                </Rule>
                <Rule symbol-ref="RandT_or_Mech_motion_strength">
                    <Expression>0</Expression>
                </Rule>
                <Rule symbol-ref="color">
                    <Expression>2</Expression>
                </Rule>
            </Event>
            <PropertyVector symbol="directed_motion_dir" name="Direction of the directed motion (axial cells)." value="0.0, 1.0, 0.0"/>
            <DirectedMotion direction="directed_motion_dir" name="Directed movement of axial cells" strength="directed_motion_strength">
                <Annotation>Set to strength = 0 for polster cells</Annotation>
            </DirectedMotion>
            <Property symbol="RandT_or_Mech_motion_strength" name="Strength of Run and Tumble or mechanically induced motion." tags="guidance_by_followers" value="0"/>
            <Event name="Polster cell initialization">
                <Annotation>After init_time, set the properties of polster cells</Annotation>
                <Condition>time == init_time and cell.center.y > 360</Condition>
                <Rule symbol-ref="RandT_or_Mech_motion_strength">
                    <Expression>RandT_or_Mech_motion_strength_global</Expression>
                </Rule>
                <Rule symbol-ref="directed_motion_strength">
                    <Expression>0</Expression>
                </Rule>
                <Rule symbol-ref="color">
                    <Expression>3</Expression>
                </Rule>
            </Event>
            <Property symbol="tumble.run_duration" name="run duration" tags="RunandTumble" value="0.0"/>
            <Property symbol="tumble.last" name="last tumble event" tags="RunandTumble" value="0"/>
            <PropertyVector symbol="RaT_dir" tags="RunandTumble" value="0.0, 0.0, 0.0"/>
            <Event trigger="when-true" time-step="5" name="Run and Tumble" tags="RunandTumble">
                <Condition>(time >= tumble.last + tumble.run_duration)</Condition>
                <Rule symbol-ref="tumble.last">
                    <Expression>time</Expression>
                </Rule>
                <Rule symbol-ref="tumble.run_duration" name="New update time">
                    <Expression>run_duration_adjustment * rand_gamma(0.5, 5)</Expression>
                </Rule>
                <Intermediate symbol="angle" value="rand_uni(0, 2 * pi)"/>
                <VectorRule symbol-ref="RaT_dir" notation="φ,θ,r">
                    <Expression>angle, 0 , 1</Expression>
                </VectorRule>
            </Event>
            <MembraneProperty symbol="cell_center_x" tags="py" value="0.0">
                <Diffusion rate="0"/>
            </MembraneProperty>
            <MembraneProperty symbol="cell_center_y" tags="py" value="0.0">
                <Diffusion rate="0"/>
            </MembraneProperty>
            <Mapper time-step="1.0" name="cell_center_x" tags="py">
                <Input value="cell.center.x"/>
                <Output symbol-ref="cell_center_x"/>
            </Mapper>
            <Mapper time-step="1.0" name="cell_center_y" tags="py">
                <Input value="cell.center.y"/>
                <Output symbol-ref="cell_center_y"/>
            </Mapper>
            <MembraneProperty symbol="neighbor_center_x" tags="py" value="0.0">
                <Diffusion rate="0"/>
            </MembraneProperty>
            <MembraneProperty symbol="neighbor_center_y" tags="py" value="0.0">
                <Diffusion rate="0"/>
            </MembraneProperty>
            <NeighborhoodReporter time-step="1.0" name="neighbor_center_x" tags="py">
                <Input scaling="length" value="cell.center.x"/>
                <Output symbol-ref="neighbor_center_x" mapping="discrete"/>
            </NeighborhoodReporter>
            <NeighborhoodReporter time-step="1.0" name="neighbor_center_y" tags="py">
                <Input scaling="length" value="cell.center.y"/>
                <Output symbol-ref="neighbor_center_y" mapping="discrete"/>
            </NeighborhoodReporter>
            <PropertyVector symbol="velocity" value="0.0, 0.0, 0.0"/>
            <MotilityReporter time-step="1" name="Get velocity for computing mechanics">
                <Annotation>Needs time-step = 1</Annotation>
                <Velocity symbol-ref="velocity"/>
            </MotilityReporter>
            <MembraneProperty symbol="neighbor_velocity_x" tags="py" value="0.0">
                <Diffusion rate="0"/>
            </MembraneProperty>
            <MembraneProperty symbol="neighbor_velocity_y" tags="py" value="0.0">
                <Diffusion rate="0"/>
            </MembraneProperty>
            <NeighborhoodReporter time-step="1.0" name="neighbor_velocity_x" tags="py">
                <Input scaling="length" value="velocity.x"/>
                <Output symbol-ref="neighbor_velocity_x" mapping="discrete"/>
            </NeighborhoodReporter>
            <NeighborhoodReporter time-step="1.0" name="neighbor_velocity_y" tags="py">
                <Input scaling="length" value="velocity.y"/>
                <Output symbol-ref="neighbor_velocity_y" mapping="discrete"/>
            </NeighborhoodReporter>
            <Property symbol="py_angle" tags="py" value="0.0"/>
            <Property symbol="py_induced_dir_x" tags="py" value="0.0"/>
            <Property symbol="py_induced_dir_y" tags="py" value="0.0"/>
            <PyMapper time-step="1" name="Code for multi-neighbor collisions" tags="py">
                <Annotation>Needs time-step = 1</Annotation>
                <Input symbol-ref="cell_center_x"/>
                <Input symbol-ref="cell_center_y"/>
                <Input symbol-ref="neighbor_center_x"/>
                <Input symbol-ref="neighbor_center_y"/>
                <Input symbol-ref="neighbor_velocity_x"/>
                <Input symbol-ref="neighbor_velocity_y"/>
                <Input symbol-ref="max_angle"/>
                <Input symbol-ref="min_push_velocity"/>
                <Script>import numpy as np

# merge all inputs into one big data frame df
# with two multiindex-columns "MemX"=membrane-position and "Cell"=cell.id
# and many rows for all membrane-positions and one cell after the other

df = pandas.concat([cell_center_x, cell_center_y, neighbor_center_x, neighbor_center_y, neighbor_velocity_x, neighbor_velocity_y], axis=1, keys=['cell_center_x', 'cell_center_y', 'neighbor_center_x', 'neighbor_center_y', 'neighbor_velocity_x', 'neighbor_velocity_y'])

# relative_position_to_neighbor = if(neighbor_center.abs>0, cell.center - neighbor_center, 0)
df['neighbor_center_abs'] = (df['neighbor_center_x']**2+df['neighbor_center_y']**2)**0.5

df['relative_position_to_neighbor_x'] = df['cell_center_x']-df['neighbor_center_x']
df['relative_position_to_neighbor_y'] = df['cell_center_y']-df['neighbor_center_y']

df.loc[df['neighbor_center_abs'] == 0, 'relative_position_to_neighbor_x'] = np.NaN
df.loc[df['neighbor_center_abs'] == 0, 'relative_position_to_neighbor_y'] = np.NaN

# angle = if(neighbor_center.abs>0, neighbor_velocity.phi - relative_position_to_neighbor.phi, pi)
df['angle'] = np.arctan2(df['neighbor_velocity_y'],df['neighbor_velocity_x'])-np.arctan2(df['relative_position_to_neighbor_y'],df['relative_position_to_neighbor_x'])
df.loc[df['neighbor_center_abs'] == 0, 'angle'] = np.pi
df['angle_abs'] = abs((df['angle'] + np.pi) % (2 * np.pi) - np.pi)

# induced_dir = if(cos(angle)>cos(max_angle) and neighbor_velocity.abs>min_push_velocity, neighbor_velocity, 0)
df['induced_dir_x'] = df['neighbor_velocity_x']
df['induced_dir_y'] = df['neighbor_velocity_y']
df['induced_dir_abs'] = (df['neighbor_velocity_x']**2+df['neighbor_velocity_y']**2)**0.5
df.loc[(np.cos(df['angle'])&lt;np.cos(max_angle)) | (df['induced_dir_abs']&lt;min_push_velocity), 'induced_dir_x'] = np.NaN
df.loc[(np.cos(df['angle'])&lt;np.cos(max_angle)) | (df['induced_dir_abs']&lt;min_push_velocity), 'induced_dir_y'] = np.NaN

# to monitor, also return per-cell aggregated intermediate quantities
py_angle = df.groupby('Cell')['angle'].mean()

# aggregate information per cell and apply summary statistics
# contact-length weighted mean of direction vectors
py_induced_dir_x = df.groupby('Cell')['induced_dir_x'].mean()
py_induced_dir_y = df.groupby('Cell')['induced_dir_y'].mean()
</Script>
                <Output symbol-ref="py_angle"/>
                <Output symbol-ref="py_induced_dir_x"/>
                <Output symbol-ref="py_induced_dir_y"/>
            </PyMapper>
            <PropertyVector symbol="mech_induced_dir" name="Mechanically induced direction (computed in the PyMapper)." notation="x,y,z" tags="py" value="0.0, 0.0, 0.0"/>
            <VectorEquation symbol-ref="mech_induced_dir" name="Set induced direction of polster cells" notation="x,y,z" tags="py">
                <Expression>py_induced_dir_x, py_induced_dir_y, 0.0</Expression>
            </VectorEquation>
            <PropertyVector symbol="dir" value="0.0, 0.0, 0.0"/>
            <VectorEquation symbol-ref="dir" name="Set direction of polster cells">
                <Expression>if(mech_induced_dir.abs > 0, mech_induced_dir, RaT_dir)</Expression>
            </VectorEquation>
            <DirectedMotion direction="dir" name="Directed movement polster cells" strength="RandT_or_Mech_motion_strength" tags="guidance_by_followers">
                <Annotation>RandT or mechanically induced movement. Set to strength = 0 for axial cells</Annotation>
            </DirectedMotion>
        </CellType>
        <CellType class="biological" name="confinement">
            <FreezeMotion>
                <Condition>1</Condition>
            </FreezeMotion>
            <Property symbol="color" value="0"/>
        </CellType>
    </CellTypes>
    <CPM>
        <Interaction>
            <Contact type1="cell" type2="confinement" value="20.0"/>
        </Interaction>
        <ShapeSurface scaling="norm">
            <Neighborhood>
                <Order>3</Order>
            </Neighborhood>
        </ShapeSurface>
        <MonteCarloSampler stepper="edgelist">
            <MCSDuration value="0.1"/>
            <MetropolisKinetics temperature="1"/>
            <Neighborhood>
                <Order>1</Order>
            </Neighborhood>
        </MonteCarloSampler>
    </CPM>
    <CellPopulations>
        <Population type="cell" name="Axial and polster cells" size="1">
            <InitRectangle random-offset="5" number-of-cells="400" mode="regular">
                <Dimensions origin="160,15.0, 0.0" size="180.0, 650.0, 1.0"/>
            </InitRectangle>
        </Population>
        <Population type="confinement" name="Lateral confinement" size="1">
            <InitCellObjects mode="order">
                <Arrangement displacements="1, 1, 1" repetitions="1, 1, 1">
                    <Box origin="0.0, 0.0, 0.0" size="150.0, 1500.0*0.866, 0.0"/>
                </Arrangement>
            </InitCellObjects>
            <InitCellObjects mode="distance">
                <Arrangement displacements="1, 1, 1" repetitions="1, 1, 1">
                    <Box origin="size.x-150, 0.0, 0.0" size="150.0, 1500.0*0.866, 0.0"/>
                </Arrangement>
            </InitCellObjects>
        </Population>
    </CellPopulations>
    <Analysis>
        <Gnuplotter time-step="1">
            <Plot title=" ">
                <Cells min="0" max="3" value="color">
                    <ColorMap>
                        <Color value="0" color="gray90"/>
                        <Color value="1" color="white"/>
                        <Color value="2" color="yellow"/>
                        <Color value="3" color="green"/>
                    </ColorMap>
                </Cells>
                <CellArrows orientation="dir * 0"/>
            </Plot>
            <Terminal name="png"/>
        </Gnuplotter>
        <ModelGraph format="png" reduced="false" include-tags="#untagged,RunandTumble,guidance_by_followers,py"/>
    </Analysis>
</MorpheusModel>
