Robust Guidance by Followers

Persistent Identifier

Use this permanent link to cite or share this Morpheus model:

Reference

This model is used in the publication by Müller et al., currently under review.

Model

Get this model via:

  • Morpheus-Link or
  •  Download: M0008_robust-guidance-by-followers_model.xml
  • XML Preview

    <?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
    Date:		10.02.2023
    Authors:		R. Müller, D. Jahn, J. Starruß, A. Boutillon, N. B. David, L. Brusch
    Contributors:	D. Jahn, R. Müller
    Software:       	Morpheus (open-source). Download from https://morpheus.gitlab.io
    Units: 		[time] = min, [space] = μm
    Model ID:		https://identifiers.org/morpheus/M0008
    Comment:		Extension of the guidance-by-followers mechanism 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>
    
    

    This model requires a special Morpheus version that features the PyMapper capability. For this, please see the installation instructions below.
    Model Graph
    Model Graph

    Installation Instructions

    The model can be run with a prebuilt Morpheus PyMapper version, which requires Docker installed on a Linux system.

    Install the Docker Engine with the following commands:

    sudo apt update
    sudo apt install docker.io
    

    Now enter the directory containing the model M0008_robust-guidance-by-followers_model.xml ( | ) and run it by typing:

    sudo docker run --rm -it -v${PWD}:/morpheus registry.gitlab.com/morpheus.lab/morpheus/morpheus-python M0008_robust-guidance-by-followers_model.xml
    

    When you call the model for the first time, a Docker image with the Morpheus PyMapper version is automatically downloaded before launching the simulation. You should now be able to see the Morpheus output in the console and observe how the simulation results are written to the same folder where the XML file is located.

    Downloads

    Files associated with this model:

    Next