<?xml version='1.0' encoding='UTF-8'?>
<MorpheusModel version="4">
    <Description>
        <Details>Full title:		Collective cell migration due to guidance-by-followers is robust to multiple stimuli
Authors:		R. Müller, D. Jahn, J. Starruß, A. Boutillon, N. B. David, L. Brusch
Contributors:	D. Jahn
Date:		10.02.2023
Software:       	Morpheus (open-source). Download from https://morpheus.gitlab.io
Model ID:		https://identifiers.org/morpheus/M0008
Units: 		[time] = min, [space] = μm
Reference:		This model is the original used in the publication, up to technical updates:
		R. Müller, A. Boutillon, D. Jahn, J. Starruß, N. B. David, L. Brusch: Collective cell migration due to guidance-by-followers is robust to multiple stimuli. Front. Appl. Math. Stat. 9: 2297-4687, 2023.
		https://doi.org/10.3389/fams.2023.1163583
Comment:		Extension of the guidance-by-followers mechanism (https://identifiers.org/morpheus/M0006) comparing robustness of various integrations of multiple simultaneous cell-cell contact stimuli.</Details>
        <Title>Robust Guidance by Followers</Title>
    </Description>
    <Space>
        <Lattice class="hexagonal">
            <Neighborhood>
                <Order>1</Order>
            </Neighborhood>
            <Size symbol="size" value="500, 2400, 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="620"/>
        <TimeSymbol symbol="time"/>
        <RandomSeed value="0"/>
    </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>
        <Variable symbol="axial_cell_front_y" name="Y-coordinate of axial cell front" value="-1"/>
        <Variable symbol="polster_cell_front_y" name="Y-coordinate of polster cell front" value="-1"/>
        <Variable symbol="axial_cell_front_y_init" name="Y-coordinate of axial cell front right after initialization at t = init_time" value="-1"/>
        <Variable symbol="polster_cell_front_y_init" name="Y-coordinate of polster cell front right after initialization at t = init_time" value="-1"/>
        <Event trigger="on-change" time-step="1.0" name="Save Y-coordinate of initial axial and polster cell fronts">
            <Condition>time == init_time</Condition>
            <Rule symbol-ref="axial_cell_front_y_init">
                <Expression>axial_cell_front_y</Expression>
            </Rule>
            <Rule symbol-ref="polster_cell_front_y_init">
                <Expression>polster_cell_front_y</Expression>
            </Rule>
        </Event>
        <Function symbol="axial_cell_front_speed" name="Speed of axial cell front">
            <Expression>(axial_cell_front_y - axial_cell_front_y_init) / (time - init_time)</Expression>
        </Function>
        <Function symbol="polster_cell_front_speed" name="Speed of polster cell front">
            <Expression>(polster_cell_front_y - polster_cell_front_y_init) / (time - init_time)</Expression>
        </Function>
        <Variable symbol="polster_cells_total" name="Total number of polster cells" value="-1"/>
        <Variable symbol="polster_cells_orientated_total" name="Total number of polster cells orientated ±45° " value="-1">
            <Annotation>Total number of polster cells orientated ±45°</Annotation>
        </Variable>
        <Function symbol="polster_cells_orientated_ratio" name="Fraction of polster cells orientated ±45°">
            <Expression>polster_cells_orientated_total/polster_cells_total</Expression>
        </Function>
        <Constant symbol="pushing_mode" name="Mode selector for evaluation of multi-neighbor collisions (default: 1)" tags="py" value="1">
            <Annotation>0 = none, 1 = contact-weighted mean, 2 = largest contact takes all, 3 = min angle takes all</Annotation>
        </Constant>
        <Function symbol="cell_count" name="Parameter for pushing mode 2" tags="py">
            <Expression>celltype.cell.size</Expression>
        </Function>
    </Global>
    <CellTypes>
        <CellType class="biological" name="cell">
            <Function symbol="color" name="Set cell colors">
                <Annotation>Cell colors:

- white: uninitialized cells (time >= init_time)
- yellow: axial cells (RandT_or_Mech_motion_strength == 0)
- red: RaT polster cells not orientated ±45° (mech_induced_dir.abs == 0 and !polster_cell_orientated)
- orange: guided polster cells not orientated ±45°(mech_induced_dir.abs > 0 and !polster_cell_orientated)
- bright green: guided polster cells orientated ±45°(mech_induced_dir.abs > 0 and polster_cell_orientated)
- dark green: RaT polster cells randomly orientated ±45° (mech_induced_dir.abs == 0 and polster_cell_orientated)</Annotation>
                <Expression>if(time >= init_time, if(RandT_or_Mech_motion_strength > 0, if(mech_induced_dir.abs > 0, if(polster_cell_orientated, 5, 3), if(polster_cell_orientated, 6, 2)), 4), 1)</Expression>
            </Function>
            <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 time-step="1.0" 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>
            </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 time-step="1.0" 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>
            </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="pushing_mode"/>
                <Input symbol-ref="cell_count"/>
                <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
#print(df.to_string())

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

#aggregate information per cell and apply summary statistics as selected by pushing_mode
if pushing_mode == 0:
#    print("Pushing mode 0: nothing to be done.")
    pass

elif pushing_mode == 1:
#    print("Pushing mode 1: 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()

elif pushing_mode == 2:
#    print("Pushing mode 2: winner takes all, longest contact wins.")
    df2 = df.groupby('Cell')['induced_dir_x'].apply(lambda x: list(x.mode()))
    df2.loc[df2.apply(len) == 0] = 0.0
    df3 = df.groupby('Cell')['induced_dir_y'].apply(lambda x: list(x.mode()))
    for i in range(1,int(cell_count)+1):
        if(df2[i] == 0.0):
            py_induced_dir_x[i] = 0.0
            py_induced_dir_y[i] = 0.0
        else:
            py_induced_dir_x[i] = df2[i][0]
            py_induced_dir_y[i] = df3[i][0]

elif pushing_mode == 3:
#    print("Pushing mode 3: winner takes all, the angle closest to zero wins.")
    df2 = df.reset_index()
    py_induced_dir_x = df2.loc[df2.groupby('Cell')['angle_abs'].idxmin()]['induced_dir_x']
    py_induced_dir_y = df2.loc[df2.groupby('Cell')['angle_abs'].idxmin()]['induced_dir_y']
else:
    print("This mode ",pushing_mode," does not match 0, 1, 2, 3 and still needs to be defined.")

#print(py_induced_dir_x.to_string())
#print(py_induced_dir_y.to_string())</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" name="Direction of polster cells" 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>
            <Function symbol="axial_cell_y" name="Y-coordinate of axial cells">
                <Expression>cell.center.y * (RandT_or_Mech_motion_strength == 0)</Expression>
            </Function>
            <Function symbol="polster_cell_y" name="Y-coordinate of polster cells">
                <Expression>cell.center.y * (RandT_or_Mech_motion_strength > 0)</Expression>
            </Function>
            <Mapper time-step="1.0" name="Y-coordinate of axial cell front">
                <Annotation>The axial cell furthest ahead determines the axial cell front</Annotation>
                <Input value="axial_cell_y"/>
                <Output symbol-ref="axial_cell_front_y" mapping="maximum"/>
            </Mapper>
            <Mapper time-step="1.0" name="Y-coordinate of polster cell front">
                <Annotation>The polster cell furthest ahead determines the polster cell front</Annotation>
                <Input value="polster_cell_y"/>
                <Output symbol-ref="polster_cell_front_y" mapping="maximum"/>
            </Mapper>
            <Function symbol="polster_cell" name="Flag for polster cells">
                <Annotation>Returns:
    - '1' if cell is a polster cell
    - '0' if cell is a polster cell</Annotation>
                <Expression>RandT_or_Mech_motion_strength > 0 and time >= init_time</Expression>
            </Function>
            <Mapper time-step="orientation_logger_time_step" name="Sum of polster cells">
                <Input value="polster_cell"/>
                <Output symbol-ref="polster_cells_total" mapping="sum"/>
            </Mapper>
            <Function symbol="polster_cell_orientated" name="Flag for polster cells orientated ±45°">
                <Annotation>Returns:
    - '1' for polster cells moving in y direction ±45°
    - '0' for polster cells moving in other directions and all axial cells</Annotation>
                <Expression>RandT_or_Mech_motion_strength > 0 and velocity.phi >= 1/4 * pi and velocity.phi &lt;= 3/4 * pi</Expression>
            </Function>
            <Mapper name="Sum of polster cells orientated ±45°">
                <Annotation>Sums up polster cells oriented ±45°</Annotation>
                <Input value="polster_cell_orientated"/>
                <Output symbol-ref="polster_cells_orientated_total" mapping="sum"/>
            </Mapper>
            <Function symbol="velocity.angle_rotated" name="Transform angle using 'arctan2'">
                <Annotation>Transform the angle of the cell from Morpheus' 0° in x-axis direction to 0° when the cell moves in y-axis direction. The direction of movement of the cell is then given as an absolute difference in degrees from the ideal movement in the y-axis direction.</Annotation>
                <Expression>atan2(-cos(velocity.phi), sin(velocity.phi)) / pi * 180</Expression>
            </Function>
            <!--    <Disabled>
        <Function symbol="velocity.angle" name="Convert angle to dregrees">
            <Annotation>Convert the angle from radians to degrees</Annotation>
            <Expression>velocity.phi / pi * 180</Expression>
        </Function>
    </Disabled>
-->
            <!--    <Disabled>
        <Function symbol="velocity.angle_rotated_if" name="Transform angles using 'if'">
            <Annotation>Transform the angle using if-conditions and modulo operator</Annotation>
            <Expression>if(mod(velocity.angle + 270, 360) > 180, mod(velocity.angle + 270, 360) - 360, mod(velocity.angle + 270, 360))</Expression>
        </Function>
    </Disabled>
-->
            <!--    <Disabled>
        <Function symbol="velocity.angle_rotated_mod" name="Transform angle using 'mod'">
            <Annotation>Transform the angle using only the modulo operator</Annotation>
            <Expression>mod(mod(velocity.angle + 270, 360) + 180, 360) - 180</Expression>
        </Function>
    </Disabled>
-->
        </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, 2400.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, 2400.0*0.866, 0.0"/>
                </Arrangement>
            </InitCellObjects>
        </Population>
    </CellPopulations>
    <Analysis>
        <ModelGraph format="png" reduced="false" include-tags="#untagged,RunandTumble,guidance_by_followers,py"/>
        <Gnuplotter time-step="10">
            <Plot title=" ">
                <Cells min="0" max="6" value="color">
                    <ColorMap>
                        <Color value="0" color="gray90"/>
                        <Color value="1" color="white"/>
                        <Color value="2" color="light-red"/>
                        <Color value="3" color="orange"/>
                        <Color value="4" color="yellow"/>
                        <Color value="5" color="green"/>
                        <Color value="6" color="forest-green"/>
                    </ColorMap>
                </Cells>
                <CellArrows orientation="dir * 0"/>
            </Plot>
            <Terminal name="png" size="1440, 1440, 0"/>
        </Gnuplotter>
        <Logger time-step="620" name="Orientation summary statistics">
            <Annotation>For plot 'Velocity Angle ±45°'</Annotation>
            <Input>
                <Symbol symbol-ref="directed_motion_strength_global"/>
                <Symbol symbol-ref="axial_cell_front_y_init"/>
                <Symbol symbol-ref="axial_cell_front_y"/>
                <Symbol symbol-ref="axial_cell_front_speed"/>
                <Symbol symbol-ref="polster_cells_orientated_ratio"/>
                <Symbol symbol-ref="polster_cell_front_y_init"/>
                <Symbol symbol-ref="polster_cell_front_y"/>
                <Symbol symbol-ref="polster_cell_front_speed"/>
            </Input>
            <Output>
                <TextOutput separator="semicolon" file-name="stats_orientation_summary" file-format="csv"/>
            </Output>
        </Logger>
        <Logger time-step="620" name="Orientation per cell">
            <Annotation>For plot 'Velocity Angle Distribution'</Annotation>
            <Restriction condition="RandT_or_Mech_motion_strength > 0">
                <Celltype celltype="cell"/>
            </Restriction>
            <Input>
                <Symbol symbol-ref="velocity.phi"/>
                <Symbol symbol-ref="velocity.angle_rotated"/>
            </Input>
            <Output>
                <TextOutput separator="semicolon" file-name="stats_orientation_cells" file-format="csv"/>
            </Output>
        </Logger>
        <Logger time-step="1.0" name="Helper logger; do not turn off">
            <Annotation>Do not turn off. Helper logger ensuring that the VectorEquations for 'mech_induced_dir' and 'dir' are periodically evaluated by the TimeScheduler.</Annotation>
            <Restriction>
                <Celltype celltype="cell"/>
            </Restriction>
            <Input>
                <Symbol symbol-ref="dir.z"/>
            </Input>
            <Output>
                <TextOutput file-name="helper_logger" file-format="matrix"/>
            </Output>
        </Logger>
    </Analysis>
</MorpheusModel>
