# -*- coding: utf-8 -*-
"""
Provide rules and states for a lattice-grain CTS model.

Created on Tue Feb  2 07:37:59 2016

@author: gtucker
"""

from landlab.ca.celllab_cts import Transition


def lattice_grain_node_states():
    """
    Create and return dict of states for lattice-grain model.
    """
    ns_dict = {
        0: "empty",
        1: "moving up",
        2: "moving right and up",
        3: "moving right and down",
        4: "moving down",
        5: "moving left and down",
        6: "moving left and up",
        7: "rest",
        8: "wall",
    }
    return ns_dict


def lattice_grain_transition_list(g=0.0, f=0.0, motion=1.0):
    """
    Creates and returns a list of Transition() objects to represent state
    transitions for simple granular mechanics model.

    Parameters
    ----------
    g : float (optional)
        Gravitational parameter. 1/g is the time taken for an initially
        stationary particle to fall one cell width. (Dimensions of 1/time)
    f : float (optional)
        Frictional parameter. Dimensions of 1/time. Probability per unit time
        of a frictional collision, in which one or both colliding particles
        come to a halt.
    motion : float (optional)
        Rate of motion (cells per time unit)

    Returns
    -------
    xn_list : list of Transition objects
        List of objects that encode information about the link-state transitions.

    Notes
    -----
    The transitions for this version of lattice gas have 11 pair-transition
    rules. The shorthand for the states is as follows:

        AR = air/empty
        IN = incoming particle (moving toward its neighbor)
        OU = outgoing particle (moving away from its neighbor)
        IO = incoming at an oblique angle
        OO = outgoing at an oblique angle
        RE = rest particle
        WA = wall particle
        op = oblique pair moving in opposite perpendicular direction
        sm = oblique pair moving in same perpendicular direction

    The 11 pairs with transitions are:

        1. AR-IN => IN-AR (move to empty particle)
        2. IN-IN => OO-OO-op (1/3 each dir), OU-OU (1/3) (head-on collision)
        3. IN-IO => OO-OU (oblique collision)
        4. IN-OO => IO-OU (oblique collision from behind)
        5. IN-OU => IO-OO (1/4 each of 2 directions) (collision from behind)
        6. IN-RE => RE-OU (1/3) RE-OO (1/3 each dir) (collision with rest)
        7. IN-WA => OU-WA (1/3) OO-WA (1/3 each dir) (wall collision)
        8. IO-IO-op => OO-OO-op (1/2 each dir) (glacing collision)
        9. IO-IO-sm => OO-OO-sm (30-degree collision)
        10. IO-RE => RE-OU (oblique collision with rest particle)
        11. IO-WA => OO-WA (oblique collision with wall)
    """
    xn_list = []

    # Set elastic-response rate, and scale frictional-response rate to motion
    # rate
    p_elast = motion * (1.0 - f)  # rate of elastic (non-dissipative) collision
    f *= motion

    # Rule 1: Transitions for particle movement into an empty cell
    xn_list.append(Transition((1, 0, 0), (0, 1, 0), motion, "motion"))
    xn_list.append(Transition((2, 0, 1), (0, 2, 1), motion, "motion"))
    xn_list.append(Transition((3, 0, 2), (0, 3, 2), motion, "motion"))
    xn_list.append(Transition((0, 4, 0), (4, 0, 0), motion, "motion"))
    xn_list.append(Transition((0, 5, 1), (5, 0, 1), motion, "motion"))
    xn_list.append(Transition((0, 6, 2), (6, 0, 2), motion, "motion"))

    # Rule 2: Transitions for head-on collision: elastic
    if p_elast > 0.0:
        xn_list.append(
            Transition((1, 4, 0), (4, 1, 0), p_elast / 3, "head-on collision")
        )
        xn_list.append(
            Transition((1, 4, 0), (3, 6, 0), p_elast / 3, "head-on collision")
        )
        xn_list.append(
            Transition((1, 4, 0), (5, 2, 0), p_elast / 3, "head-on collision")
        )
        xn_list.append(
            Transition((2, 5, 1), (5, 2, 1), p_elast / 3, "head-on collision")
        )
        xn_list.append(
            Transition((2, 5, 1), (4, 1, 1), p_elast / 3, "head-on collision")
        )
        xn_list.append(
            Transition((2, 5, 1), (6, 3, 1), p_elast / 3, "head-on collision")
        )
        xn_list.append(
            Transition((3, 6, 2), (6, 3, 2), p_elast / 3, "head-on collision")
        )
        xn_list.append(
            Transition((3, 6, 2), (1, 4, 2), p_elast / 3, "head-on collision")
        )
        xn_list.append(
            Transition((3, 6, 2), (5, 2, 2), p_elast / 3, "head-on collision")
        )

    # Rule 2: Transitions for head-on collision: frictional dissipation
    if f > 0.0:
        xn_list.append(Transition((1, 4, 0), (7, 7, 0), f, "head-on collision"))
        xn_list.append(Transition((2, 5, 1), (7, 7, 1), f, "head-on collision"))
        xn_list.append(Transition((3, 6, 2), (7, 7, 2), f, "head-on collision"))

    # Rule 3: Transitions for oblique collision: elastic
    if p_elast > 0.0:
        xn_list.append(Transition((1, 3, 0), (3, 1, 0), p_elast, "oblique collision"))
        xn_list.append(Transition((1, 5, 0), (5, 1, 0), p_elast, "oblique collision"))
        xn_list.append(Transition((2, 4, 0), (4, 2, 0), p_elast, "oblique collision"))
        xn_list.append(Transition((6, 4, 0), (4, 6, 0), p_elast, "oblique collision"))
        xn_list.append(Transition((2, 4, 1), (4, 2, 1), p_elast, "oblique collision"))
        xn_list.append(Transition((2, 6, 1), (6, 2, 1), p_elast, "oblique collision"))
        xn_list.append(Transition((1, 5, 1), (5, 1, 1), p_elast, "oblique collision"))
        xn_list.append(Transition((3, 5, 1), (5, 3, 1), p_elast, "oblique collision"))
        xn_list.append(Transition((3, 1, 2), (1, 3, 2), p_elast, "oblique collision"))
        xn_list.append(Transition((3, 5, 2), (5, 3, 2), p_elast, "oblique collision"))
        xn_list.append(Transition((2, 6, 2), (6, 2, 2), p_elast, "oblique collision"))
        xn_list.append(Transition((4, 6, 2), (6, 4, 2), p_elast, "oblique collision"))

    # Rule 3 frictional
    if f > 0.0:
        xn_list.append(Transition((1, 3, 0), (7, 7, 0), f, "oblique collision"))
        xn_list.append(Transition((1, 5, 0), (7, 7, 0), f, "oblique collision"))
        xn_list.append(Transition((2, 4, 0), (7, 7, 0), f, "oblique collision"))
        xn_list.append(Transition((6, 4, 0), (7, 7, 0), f, "oblique collision"))
        xn_list.append(Transition((2, 4, 1), (7, 7, 1), f, "oblique collision"))
        xn_list.append(Transition((2, 6, 1), (7, 7, 1), f, "oblique collision"))
        xn_list.append(Transition((1, 5, 1), (7, 7, 1), f, "oblique collision"))
        xn_list.append(Transition((3, 5, 1), (7, 7, 1), f, "oblique collision"))
        xn_list.append(Transition((3, 1, 2), (7, 7, 2), f, "oblique collision"))
        xn_list.append(Transition((3, 5, 2), (7, 7, 2), f, "oblique collision"))
        xn_list.append(Transition((2, 6, 2), (7, 7, 2), f, "oblique collision"))
        xn_list.append(Transition((4, 6, 2), (7, 7, 2), f, "oblique collision"))

    # Rule 4: Transitions for oblique-from-behind collisions
    if p_elast > 0.0:
        xn_list.append(Transition((1, 2, 0), (2, 1, 0), p_elast, "oblique"))
        xn_list.append(Transition((1, 6, 0), (6, 1, 0), p_elast, "oblique"))
        xn_list.append(Transition((3, 4, 0), (4, 3, 0), p_elast, "oblique"))
        xn_list.append(Transition((5, 4, 0), (4, 5, 0), p_elast, "oblique"))
        xn_list.append(Transition((2, 1, 1), (1, 2, 1), p_elast, "oblique"))
        xn_list.append(Transition((2, 3, 1), (3, 2, 1), p_elast, "oblique"))
        xn_list.append(Transition((4, 5, 1), (5, 4, 1), p_elast, "oblique"))
        xn_list.append(Transition((6, 5, 1), (5, 6, 1), p_elast, "oblique"))
        xn_list.append(Transition((3, 2, 2), (2, 3, 2), p_elast, "oblique"))
        xn_list.append(Transition((3, 4, 2), (4, 3, 2), p_elast, "oblique"))
        xn_list.append(Transition((1, 6, 2), (6, 1, 2), p_elast, "oblique"))
        xn_list.append(Transition((5, 6, 2), (6, 5, 2), p_elast, "oblique"))

    # Rule 4 frictional
    if f > 0.0:
        xn_list.append(Transition((1, 2, 0), (7, 1, 0), f, "oblique"))
        xn_list.append(Transition((1, 6, 0), (7, 1, 0), f, "oblique"))
        xn_list.append(Transition((3, 4, 0), (4, 7, 0), f, "oblique"))
        xn_list.append(Transition((5, 4, 0), (4, 7, 0), f, "oblique"))
        xn_list.append(Transition((2, 1, 1), (7, 2, 1), f, "oblique"))
        xn_list.append(Transition((2, 3, 1), (7, 2, 1), f, "oblique"))
        xn_list.append(Transition((4, 5, 1), (5, 7, 1), f, "oblique"))
        xn_list.append(Transition((6, 5, 1), (5, 7, 1), f, "oblique"))
        xn_list.append(Transition((3, 2, 2), (7, 3, 2), f, "oblique"))
        xn_list.append(Transition((3, 4, 2), (7, 3, 2), f, "oblique"))
        xn_list.append(Transition((1, 6, 2), (6, 7, 2), f, "oblique"))
        xn_list.append(Transition((5, 6, 2), (6, 7, 2), f, "oblique"))

    # Rule 5: Transitions for direct-from-behind collisions
    if p_elast > 0.0:
        xn_list.append(Transition((1, 1, 0), (2, 6, 0), p_elast / 4, "behind"))
        xn_list.append(Transition((1, 1, 0), (6, 2, 0), p_elast / 4, "behind"))
        xn_list.append(Transition((4, 4, 0), (3, 5, 0), p_elast / 4, "behind"))
        xn_list.append(Transition((4, 4, 0), (5, 3, 0), p_elast / 4, "behind"))
        xn_list.append(Transition((2, 2, 1), (1, 3, 1), p_elast / 4, "behind"))
        xn_list.append(Transition((2, 2, 1), (3, 1, 1), p_elast / 4, "behind"))
        xn_list.append(Transition((5, 5, 1), (4, 6, 1), p_elast / 4, "behind"))
        xn_list.append(Transition((5, 5, 1), (6, 4, 1), p_elast / 4, "behind"))
        xn_list.append(Transition((3, 3, 2), (2, 4, 2), p_elast / 4, "behind"))
        xn_list.append(Transition((3, 3, 2), (4, 2, 2), p_elast / 4, "behind"))
        xn_list.append(Transition((6, 6, 2), (1, 5, 2), p_elast / 4, "behind"))
        xn_list.append(Transition((6, 6, 2), (5, 1, 2), p_elast / 4, "behind"))

    # Rule 5 frictional
    if f > 0.0:
        xn_list.append(Transition((1, 1, 0), (7, 1, 0), f / 4, "behind"))
        xn_list.append(Transition((4, 4, 0), (4, 7, 0), f / 4, "behind"))
        xn_list.append(Transition((2, 2, 1), (7, 2, 1), f / 4, "behind"))
        xn_list.append(Transition((5, 5, 1), (5, 7, 1), f / 4, "behind"))
        xn_list.append(Transition((3, 3, 2), (7, 3, 2), f / 4, "behind"))
        xn_list.append(Transition((6, 6, 2), (6, 7, 2), f / 4, "behind"))

    # Rule 6: Transitions for direct collision with stationary (resting) particle
    if p_elast > 0.0:
        xn_list.append(Transition((1, 7, 0), (7, 1, 0), p_elast / 3., "rest"))
        xn_list.append(Transition((1, 7, 0), (7, 2, 0), p_elast / 3., "rest"))
        xn_list.append(Transition((1, 7, 0), (7, 6, 0), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 4, 0), (4, 7, 0), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 4, 0), (3, 7, 0), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 4, 0), (5, 7, 0), p_elast / 3., "rest"))
        xn_list.append(Transition((2, 7, 1), (7, 2, 1), p_elast / 3., "rest"))
        xn_list.append(Transition((2, 7, 1), (7, 1, 1), p_elast / 3., "rest"))
        xn_list.append(Transition((2, 7, 1), (7, 3, 1), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 5, 1), (5, 7, 1), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 5, 1), (4, 7, 1), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 5, 1), (6, 7, 1), p_elast / 3., "rest"))
        xn_list.append(Transition((3, 7, 2), (7, 3, 2), p_elast / 3., "rest"))
        xn_list.append(Transition((3, 7, 2), (7, 2, 2), p_elast / 3., "rest"))
        xn_list.append(Transition((3, 7, 2), (7, 4, 2), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 6, 2), (6, 7, 2), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 6, 2), (1, 7, 2), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 6, 2), (5, 7, 2), p_elast / 3., "rest"))

    # Rule 6 frictionl
    if f > 0.0:
        xn_list.append(Transition((1, 7, 0), (7, 7, 0), f, "rest"))
        xn_list.append(Transition((7, 4, 0), (7, 7, 0), f, "rest"))
        xn_list.append(Transition((2, 7, 1), (7, 7, 1), f, "rest"))
        xn_list.append(Transition((7, 5, 1), (7, 7, 1), f, "rest"))
        xn_list.append(Transition((3, 7, 2), (7, 7, 2), f, "rest"))
        xn_list.append(Transition((7, 6, 2), (7, 7, 2), f, "rest"))

    # Rule 7: Transitions for wall impact
    if p_elast > 0.0:
        xn_list.append(Transition((1, 8, 0), (4, 8, 0), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((1, 8, 0), (3, 8, 0), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((1, 8, 0), (5, 8, 0), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((2, 8, 1), (5, 8, 1), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((2, 8, 1), (4, 8, 1), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((2, 8, 1), (6, 8, 1), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((3, 8, 2), (6, 8, 2), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((3, 8, 2), (5, 8, 2), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((3, 8, 2), (1, 8, 2), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 4, 0), (8, 1, 0), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 4, 0), (8, 6, 0), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 4, 0), (8, 2, 0), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 5, 1), (8, 1, 1), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 5, 1), (8, 2, 1), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 5, 1), (8, 3, 1), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 6, 2), (8, 2, 2), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 6, 2), (8, 3, 2), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 6, 2), (8, 4, 2), p_elast / 3, "wall rebound"))

    # Rule 7 frictional
    if f > 0.0:
        xn_list.append(Transition((1, 8, 0), (7, 8, 0), f, "wall rebound"))
        xn_list.append(Transition((2, 8, 1), (7, 8, 1), f, "wall rebound"))
        xn_list.append(Transition((3, 8, 2), (7, 8, 2), f, "wall rebound"))
        xn_list.append(Transition((8, 4, 0), (8, 7, 0), f, "wall rebound"))
        xn_list.append(Transition((8, 5, 1), (8, 7, 1), f, "wall rebound"))
        xn_list.append(Transition((8, 6, 2), (8, 7, 2), f, "wall rebound"))

    # Rule 8: Transitions for glancing oblique collision
    if p_elast > 0.0:
        xn_list.append(Transition((2, 5, 0), (3, 6, 0), p_elast, "glancing"))
        xn_list.append(Transition((6, 3, 0), (5, 2, 0), p_elast, "glancing"))
        xn_list.append(Transition((3, 6, 1), (4, 1, 1), p_elast, "glancing"))
        xn_list.append(Transition((1, 4, 1), (6, 3, 1), p_elast, "glancing"))
        xn_list.append(Transition((4, 1, 2), (5, 2, 2), p_elast, "glancing"))
        xn_list.append(Transition((2, 5, 2), (1, 4, 2), p_elast, "glancing"))

    # Rule 8 frictional
    if f > 0.0:
        xn_list.append(Transition((2, 5, 0), (7, 7, 0), f, "glancing"))
        xn_list.append(Transition((6, 3, 0), (7, 7, 0), f, "glancing"))
        xn_list.append(Transition((3, 6, 1), (7, 7, 1), f, "glancing"))
        xn_list.append(Transition((1, 4, 1), (7, 7, 1), f, "glancing"))
        xn_list.append(Transition((4, 1, 2), (7, 7, 2), f, "glancing"))
        xn_list.append(Transition((2, 5, 2), (7, 7, 2), f, "glancing"))

    # Rule 9: Transitions for "near-on" collisions
    if p_elast > 0.0:
        xn_list.append(Transition((6, 5, 0), (5, 6, 0), p_elast, "near-on"))
        xn_list.append(Transition((2, 3, 0), (3, 2, 0), p_elast, "near-on"))
        xn_list.append(Transition((1, 6, 1), (6, 1, 1), p_elast, "near-on"))
        xn_list.append(Transition((3, 4, 1), (4, 3, 1), p_elast, "near-on"))
        xn_list.append(Transition((2, 1, 2), (1, 2, 2), p_elast, "near-on"))
        xn_list.append(Transition((4, 5, 2), (5, 4, 2), p_elast, "near-on"))

    # Rule 9 frictional
    if f > 0.0:
        xn_list.append(Transition((6, 5, 0), (7, 6, 0), f / 2, "near-on"))
        xn_list.append(Transition((6, 5, 0), (5, 7, 0), f / 2, "near-on"))
        xn_list.append(Transition((2, 3, 0), (7, 2, 0), f / 2, "near-on"))
        xn_list.append(Transition((2, 3, 0), (3, 7, 0), f / 2, "near-on"))
        xn_list.append(Transition((1, 6, 1), (7, 1, 1), f / 2, "near-on"))
        xn_list.append(Transition((1, 6, 1), (6, 7, 1), f / 2, "near-on"))
        xn_list.append(Transition((3, 4, 1), (7, 3, 1), f / 2, "near-on"))
        xn_list.append(Transition((3, 4, 1), (4, 7, 1), f / 2, "near-on"))
        xn_list.append(Transition((2, 1, 2), (7, 2, 2), f / 2, "near-on"))
        xn_list.append(Transition((2, 1, 2), (1, 7, 2), f / 2, "near-on"))
        xn_list.append(Transition((4, 5, 2), (7, 4, 2), f / 2, "near-on"))
        xn_list.append(Transition((4, 5, 2), (5, 7, 2), f / 2, "near-on"))

    # Rule 10: Transitions for oblique collision with rest particle
    if p_elast > 0.0:
        xn_list.append(Transition((2, 7, 0), (7, 1, 0), p_elast, "oblique with rest"))
        xn_list.append(Transition((6, 7, 0), (7, 1, 0), p_elast, "oblique with rest"))
        xn_list.append(Transition((7, 3, 0), (4, 7, 0), p_elast, "oblique with rest"))
        xn_list.append(Transition((7, 5, 0), (4, 7, 0), p_elast, "oblique with rest"))
        xn_list.append(Transition((3, 7, 1), (7, 2, 1), p_elast, "oblique with rest"))
        xn_list.append(Transition((1, 7, 1), (7, 2, 1), p_elast, "oblique with rest"))
        xn_list.append(Transition((7, 6, 1), (5, 7, 1), p_elast, "oblique with rest"))
        xn_list.append(Transition((7, 4, 1), (5, 7, 1), p_elast, "oblique with rest"))
        xn_list.append(Transition((4, 7, 2), (7, 3, 2), p_elast, "oblique with rest"))
        xn_list.append(Transition((2, 7, 2), (7, 3, 2), p_elast, "oblique with rest"))
        xn_list.append(Transition((7, 5, 2), (6, 7, 2), p_elast, "oblique with rest"))
        xn_list.append(Transition((7, 1, 2), (6, 7, 2), p_elast, "oblique with rest"))

    # Rule 10 frictional
    if f > 0.0:
        xn_list.append(Transition((2, 7, 0), (7, 7, 0), f, "oblique with rest"))
        xn_list.append(Transition((6, 7, 0), (7, 7, 0), f, "oblique with rest"))
        xn_list.append(Transition((7, 3, 0), (7, 7, 0), f, "oblique with rest"))
        xn_list.append(Transition((7, 5, 0), (7, 7, 0), f, "oblique with rest"))
        xn_list.append(Transition((3, 7, 1), (7, 7, 1), f, "oblique with rest"))
        xn_list.append(Transition((1, 7, 1), (7, 7, 1), f, "oblique with rest"))
        xn_list.append(Transition((7, 6, 1), (7, 7, 1), f, "oblique with rest"))
        xn_list.append(Transition((7, 4, 1), (7, 7, 1), f, "oblique with rest"))
        xn_list.append(Transition((4, 7, 2), (7, 7, 2), f, "oblique with rest"))
        xn_list.append(Transition((2, 7, 2), (7, 7, 2), f, "oblique with rest"))
        xn_list.append(Transition((7, 5, 2), (7, 7, 2), f, "oblique with rest"))
        xn_list.append(Transition((7, 1, 2), (7, 7, 2), f, "oblique with rest"))

    # Rule 11: Transitions for oblique collision with wall particle
    if p_elast > 0.0:
        xn_list.append(Transition((2, 8, 0), (3, 8, 0), p_elast, "oblique with wall"))
        xn_list.append(Transition((6, 8, 0), (5, 8, 0), p_elast, "oblique with wall"))
        xn_list.append(Transition((8, 3, 0), (8, 2, 0), p_elast, "oblique with wall"))
        xn_list.append(Transition((8, 5, 0), (8, 6, 0), p_elast, "oblique with wall"))
        xn_list.append(Transition((1, 8, 1), (6, 8, 1), p_elast, "oblique with wall"))
        xn_list.append(Transition((3, 8, 1), (4, 8, 1), p_elast, "oblique with wall"))
        xn_list.append(Transition((8, 4, 1), (8, 3, 1), p_elast, "oblique with wall"))
        xn_list.append(Transition((8, 6, 1), (8, 1, 1), p_elast, "oblique with wall"))
        xn_list.append(Transition((4, 8, 2), (5, 8, 2), p_elast, "oblique with wall"))
        xn_list.append(Transition((2, 8, 2), (1, 8, 2), p_elast, "oblique with wall"))
        xn_list.append(Transition((8, 1, 2), (8, 2, 2), p_elast, "oblique with wall"))
        xn_list.append(Transition((8, 5, 2), (8, 4, 2), p_elast, "oblique with wall"))

    # Rule 11 frictional
    if f > 0.0:
        xn_list.append(Transition((2, 8, 0), (7, 8, 0), f, "oblique with wall"))
        xn_list.append(Transition((6, 8, 0), (7, 8, 0), f, "oblique with wall"))
        xn_list.append(Transition((8, 3, 0), (8, 7, 0), f, "oblique with wall"))
        xn_list.append(Transition((8, 5, 0), (8, 7, 0), f, "oblique with wall"))
        xn_list.append(Transition((1, 8, 1), (7, 8, 1), f, "oblique with wall"))
        xn_list.append(Transition((3, 8, 1), (7, 8, 1), f, "oblique with wall"))
        xn_list.append(Transition((8, 4, 1), (8, 7, 1), f, "oblique with wall"))
        xn_list.append(Transition((8, 6, 1), (8, 7, 1), f, "oblique with wall"))
        xn_list.append(Transition((4, 8, 2), (7, 8, 2), f, "oblique with wall"))
        xn_list.append(Transition((2, 8, 2), (7, 8, 2), f, "oblique with wall"))
        xn_list.append(Transition((8, 1, 2), (8, 7, 2), f, "oblique with wall"))
        xn_list.append(Transition((8, 5, 2), (8, 7, 2), f, "oblique with wall"))

    # Gravity rule 1: rising particles become rest particles
    if g > 0.0:
        xn_list.append(Transition((0, 1, 0), (0, 7, 0), g, "gravity 1"))
        xn_list.append(Transition((1, 1, 0), (1, 7, 0), g, "gravity 1"))
        xn_list.append(Transition((2, 1, 0), (2, 7, 0), g, "gravity 1"))
        xn_list.append(Transition((3, 1, 0), (3, 7, 0), g, "gravity 1"))
        xn_list.append(Transition((4, 1, 0), (4, 7, 0), g, "gravity 1"))
        xn_list.append(Transition((5, 1, 0), (5, 7, 0), g, "gravity 1"))
        xn_list.append(Transition((6, 1, 0), (6, 7, 0), g, "gravity 1"))
        xn_list.append(Transition((7, 1, 0), (7, 7, 0), g, "gravity 1"))
        xn_list.append(Transition((8, 1, 0), (8, 7, 0), g, "gravity 1"))

    # Gravity rule 2: resting particles become falling particles (if not above
    # rest or wall?)
    if g > 0.0:
        xn_list.append(Transition((0, 7, 0), (0, 4, 0), g, "gravity 2"))
    #        xn_list.append( Transition((1,7,0), (1,4,0), g, 'gravity 2') )
    #        xn_list.append( Transition((2,7,0), (2,4,0), g, 'gravity 2') )
    #        xn_list.append( Transition((3,7,0), (3,4,0), g, 'gravity 2') )
    #        xn_list.append( Transition((4,7,0), (4,4,0), g, 'gravity 2') )
    #        xn_list.append( Transition((5,7,0), (5,4,0), g, 'gravity 2') )
    #        xn_list.append( Transition((6,7,0), (6,4,0), g, 'gravity 2') )

    # Gravity rule 3: up/sideways particles become down/sideways particles
    if g > 0.0:
        xn_list.append(Transition((0, 2, 0), (0, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((1, 2, 0), (1, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((2, 2, 0), (2, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((3, 2, 0), (3, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((4, 2, 0), (4, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((5, 2, 0), (5, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((6, 2, 0), (6, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((7, 2, 0), (7, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((8, 2, 0), (8, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((0, 6, 0), (0, 5, 0), g, "gravity 3"))
        xn_list.append(Transition((1, 6, 0), (1, 5, 0), g, "gravity 3"))
        xn_list.append(Transition((2, 6, 0), (2, 5, 0), g, "gravity 3"))
        xn_list.append(Transition((3, 6, 0), (3, 5, 0), g, "gravity 3"))
        xn_list.append(Transition((4, 6, 0), (4, 5, 0), g, "gravity 3"))
        xn_list.append(Transition((5, 6, 0), (5, 5, 0), g, "gravity 3"))
        xn_list.append(Transition((6, 6, 0), (6, 5, 0), g, "gravity 3"))
        xn_list.append(Transition((7, 6, 0), (7, 5, 0), g, "gravity 3"))
        xn_list.append(Transition((8, 6, 0), (8, 5, 0), g, "gravity 3"))

    # Gravity rule 4: down/side to straight down
    if g > 0.0:
        xn_list.append(Transition((0, 3, 0), (0, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((1, 3, 0), (1, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((2, 3, 0), (2, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((3, 3, 0), (3, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((4, 3, 0), (4, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((5, 3, 0), (5, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((6, 3, 0), (6, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((7, 3, 0), (7, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((8, 3, 0), (8, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((0, 5, 0), (0, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((1, 5, 0), (1, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((2, 5, 0), (2, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((3, 5, 0), (3, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((4, 5, 0), (4, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((5, 5, 0), (5, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((6, 5, 0), (6, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((7, 5, 0), (7, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((8, 5, 0), (8, 4, 0), g, "gravity 4"))

    # Gravity rule for lateral destabilization (represents grain
    # motion above angle of repose on sloping surface)
    if g > 0.0:
        xn_list.append(Transition((7, 0, 2), (3, 0, 2), g / 2.0, "gravity"))
        xn_list.append(Transition((0, 7, 1), (0, 5, 1), g / 2.0, "gravity"))

    return xn_list

# -*- coding: utf-8 -*-
"""
Provide rules and states for a lattice-grain CTS model.

Created on Tue Feb  2 07:37:59 2016

@author: gtucker
"""

from landlab.ca.celllab_cts import Transition


def lattice_grain_node_states():
    """
    Create and return dict of states for lattice-grain model.
    """
    ns_dict = {
        0: "empty",
        1: "moving up",
        2: "moving right and up",
        3: "moving right and down",
        4: "moving down",
        5: "moving left and down",
        6: "moving left and up",
        7: "rest",
        8: "wall",
    }
    return ns_dict


def lattice_grain_transition_list(g=0.0, f=0.0, motion=1.0):
    """
    Creates and returns a list of Transition() objects to represent state
    transitions for simple granular mechanics model.

    Parameters
    ----------
    g : float (optional)
        Gravitational parameter. 1/g is the time taken for an initially
        stationary particle to fall one cell width. (Dimensions of 1/time)
    f : float (optional)
        Frictional parameter. Dimensions of 1/time. Probability per unit time
        of a frictional collision, in which one or both colliding particles
        come to a halt.
    motion : float (optional)
        Rate of motion (cells per time unit)

    Returns
    -------
    xn_list : list of Transition objects
        List of objects that encode information about the link-state transitions.

    Notes
    -----
    The transitions for this version of lattice gas have 11 pair-transition
    rules. The shorthand for the states is as follows:

        AR = air/empty
        IN = incoming particle (moving toward its neighbor)
        OU = outgoing particle (moving away from its neighbor)
        IO = incoming at an oblique angle
        OO = outgoing at an oblique angle
        RE = rest particle
        WA = wall particle
        op = oblique pair moving in opposite perpendicular direction
        sm = oblique pair moving in same perpendicular direction

    The 11 pairs with transitions are:

        1. AR-IN => IN-AR (move to empty particle)
        2. IN-IN => OO-OO-op (1/3 each dir), OU-OU (1/3) (head-on collision)
        3. IN-IO => OO-OU (oblique collision)
        4. IN-OO => IO-OU (oblique collision from behind)
        5. IN-OU => IO-OO (1/4 each of 2 directions) (collision from behind)
        6. IN-RE => RE-OU (1/3) RE-OO (1/3 each dir) (collision with rest)
        7. IN-WA => OU-WA (1/3) OO-WA (1/3 each dir) (wall collision)
        8. IO-IO-op => OO-OO-op (1/2 each dir) (glacing collision)
        9. IO-IO-sm => OO-OO-sm (30-degree collision)
        10. IO-RE => RE-OU (oblique collision with rest particle)
        11. IO-WA => OO-WA (oblique collision with wall)
    """
    xn_list = []

    # Set elastic-response rate, and scale frictional-response rate to motion
    # rate
    p_elast = motion * (1.0 - f)  # rate of elastic (non-dissipative) collision
    f *= motion

    # Rule 1: Transitions for particle movement into an empty cell
    xn_list.append(Transition((1, 0, 0), (0, 1, 0), motion, "motion"))
    xn_list.append(Transition((2, 0, 1), (0, 2, 1), motion, "motion"))
    xn_list.append(Transition((3, 0, 2), (0, 3, 2), motion, "motion"))
    xn_list.append(Transition((0, 4, 0), (4, 0, 0), motion, "motion"))
    xn_list.append(Transition((0, 5, 1), (5, 0, 1), motion, "motion"))
    xn_list.append(Transition((0, 6, 2), (6, 0, 2), motion, "motion"))

    # Rule 2: Transitions for head-on collision: elastic
    if p_elast > 0.0:
        xn_list.append(
            Transition((1, 4, 0), (4, 1, 0), p_elast / 3, "head-on collision")
        )
        xn_list.append(
            Transition((1, 4, 0), (3, 6, 0), p_elast / 3, "head-on collision")
        )
        xn_list.append(
            Transition((1, 4, 0), (5, 2, 0), p_elast / 3, "head-on collision")
        )
        xn_list.append(
            Transition((2, 5, 1), (5, 2, 1), p_elast / 3, "head-on collision")
        )
        xn_list.append(
            Transition((2, 5, 1), (4, 1, 1), p_elast / 3, "head-on collision")
        )
        xn_list.append(
            Transition((2, 5, 1), (6, 3, 1), p_elast / 3, "head-on collision")
        )
        xn_list.append(
            Transition((3, 6, 2), (6, 3, 2), p_elast / 3, "head-on collision")
        )
        xn_list.append(
            Transition((3, 6, 2), (1, 4, 2), p_elast / 3, "head-on collision")
        )
        xn_list.append(
            Transition((3, 6, 2), (5, 2, 2), p_elast / 3, "head-on collision")
        )

    # Rule 2: Transitions for head-on collision: frictional dissipation
    if f > 0.0:
        xn_list.append(Transition((1, 4, 0), (7, 7, 0), f, "head-on collision"))
        xn_list.append(Transition((2, 5, 1), (7, 7, 1), f, "head-on collision"))
        xn_list.append(Transition((3, 6, 2), (7, 7, 2), f, "head-on collision"))

    # Rule 3: Transitions for oblique collision: elastic
    if p_elast > 0.0:
        xn_list.append(Transition((1, 3, 0), (3, 1, 0), p_elast, "oblique collision"))
        xn_list.append(Transition((1, 5, 0), (5, 1, 0), p_elast, "oblique collision"))
        xn_list.append(Transition((2, 4, 0), (4, 2, 0), p_elast, "oblique collision"))
        xn_list.append(Transition((6, 4, 0), (4, 6, 0), p_elast, "oblique collision"))
        xn_list.append(Transition((2, 4, 1), (4, 2, 1), p_elast, "oblique collision"))
        xn_list.append(Transition((2, 6, 1), (6, 2, 1), p_elast, "oblique collision"))
        xn_list.append(Transition((1, 5, 1), (5, 1, 1), p_elast, "oblique collision"))
        xn_list.append(Transition((3, 5, 1), (5, 3, 1), p_elast, "oblique collision"))
        xn_list.append(Transition((3, 1, 2), (1, 3, 2), p_elast, "oblique collision"))
        xn_list.append(Transition((3, 5, 2), (5, 3, 2), p_elast, "oblique collision"))
        xn_list.append(Transition((2, 6, 2), (6, 2, 2), p_elast, "oblique collision"))
        xn_list.append(Transition((4, 6, 2), (6, 4, 2), p_elast, "oblique collision"))

    # Rule 3 frictional
    if f > 0.0:
        xn_list.append(Transition((1, 3, 0), (7, 7, 0), f, "oblique collision"))
        xn_list.append(Transition((1, 5, 0), (7, 7, 0), f, "oblique collision"))
        xn_list.append(Transition((2, 4, 0), (7, 7, 0), f, "oblique collision"))
        xn_list.append(Transition((6, 4, 0), (7, 7, 0), f, "oblique collision"))
        xn_list.append(Transition((2, 4, 1), (7, 7, 1), f, "oblique collision"))
        xn_list.append(Transition((2, 6, 1), (7, 7, 1), f, "oblique collision"))
        xn_list.append(Transition((1, 5, 1), (7, 7, 1), f, "oblique collision"))
        xn_list.append(Transition((3, 5, 1), (7, 7, 1), f, "oblique collision"))
        xn_list.append(Transition((3, 1, 2), (7, 7, 2), f, "oblique collision"))
        xn_list.append(Transition((3, 5, 2), (7, 7, 2), f, "oblique collision"))
        xn_list.append(Transition((2, 6, 2), (7, 7, 2), f, "oblique collision"))
        xn_list.append(Transition((4, 6, 2), (7, 7, 2), f, "oblique collision"))

    # Rule 4: Transitions for oblique-from-behind collisions
    if p_elast > 0.0:
        xn_list.append(Transition((1, 2, 0), (2, 1, 0), p_elast, "oblique"))
        xn_list.append(Transition((1, 6, 0), (6, 1, 0), p_elast, "oblique"))
        xn_list.append(Transition((3, 4, 0), (4, 3, 0), p_elast, "oblique"))
        xn_list.append(Transition((5, 4, 0), (4, 5, 0), p_elast, "oblique"))
        xn_list.append(Transition((2, 1, 1), (1, 2, 1), p_elast, "oblique"))
        xn_list.append(Transition((2, 3, 1), (3, 2, 1), p_elast, "oblique"))
        xn_list.append(Transition((4, 5, 1), (5, 4, 1), p_elast, "oblique"))
        xn_list.append(Transition((6, 5, 1), (5, 6, 1), p_elast, "oblique"))
        xn_list.append(Transition((3, 2, 2), (2, 3, 2), p_elast, "oblique"))
        xn_list.append(Transition((3, 4, 2), (4, 3, 2), p_elast, "oblique"))
        xn_list.append(Transition((1, 6, 2), (6, 1, 2), p_elast, "oblique"))
        xn_list.append(Transition((5, 6, 2), (6, 5, 2), p_elast, "oblique"))

    # Rule 4 frictional
    if f > 0.0:
        xn_list.append(Transition((1, 2, 0), (7, 1, 0), f, "oblique"))
        xn_list.append(Transition((1, 6, 0), (7, 1, 0), f, "oblique"))
        xn_list.append(Transition((3, 4, 0), (4, 7, 0), f, "oblique"))
        xn_list.append(Transition((5, 4, 0), (4, 7, 0), f, "oblique"))
        xn_list.append(Transition((2, 1, 1), (7, 2, 1), f, "oblique"))
        xn_list.append(Transition((2, 3, 1), (7, 2, 1), f, "oblique"))
        xn_list.append(Transition((4, 5, 1), (5, 7, 1), f, "oblique"))
        xn_list.append(Transition((6, 5, 1), (5, 7, 1), f, "oblique"))
        xn_list.append(Transition((3, 2, 2), (7, 3, 2), f, "oblique"))
        xn_list.append(Transition((3, 4, 2), (7, 3, 2), f, "oblique"))
        xn_list.append(Transition((1, 6, 2), (6, 7, 2), f, "oblique"))
        xn_list.append(Transition((5, 6, 2), (6, 7, 2), f, "oblique"))

    # Rule 5: Transitions for direct-from-behind collisions
    if p_elast > 0.0:
        xn_list.append(Transition((1, 1, 0), (2, 6, 0), p_elast / 4, "behind"))
        xn_list.append(Transition((1, 1, 0), (6, 2, 0), p_elast / 4, "behind"))
        xn_list.append(Transition((4, 4, 0), (3, 5, 0), p_elast / 4, "behind"))
        xn_list.append(Transition((4, 4, 0), (5, 3, 0), p_elast / 4, "behind"))
        xn_list.append(Transition((2, 2, 1), (1, 3, 1), p_elast / 4, "behind"))
        xn_list.append(Transition((2, 2, 1), (3, 1, 1), p_elast / 4, "behind"))
        xn_list.append(Transition((5, 5, 1), (4, 6, 1), p_elast / 4, "behind"))
        xn_list.append(Transition((5, 5, 1), (6, 4, 1), p_elast / 4, "behind"))
        xn_list.append(Transition((3, 3, 2), (2, 4, 2), p_elast / 4, "behind"))
        xn_list.append(Transition((3, 3, 2), (4, 2, 2), p_elast / 4, "behind"))
        xn_list.append(Transition((6, 6, 2), (1, 5, 2), p_elast / 4, "behind"))
        xn_list.append(Transition((6, 6, 2), (5, 1, 2), p_elast / 4, "behind"))

    # Rule 5 frictional
    if f > 0.0:
        xn_list.append(Transition((1, 1, 0), (7, 1, 0), f / 4, "behind"))
        xn_list.append(Transition((4, 4, 0), (4, 7, 0), f / 4, "behind"))
        xn_list.append(Transition((2, 2, 1), (7, 2, 1), f / 4, "behind"))
        xn_list.append(Transition((5, 5, 1), (5, 7, 1), f / 4, "behind"))
        xn_list.append(Transition((3, 3, 2), (7, 3, 2), f / 4, "behind"))
        xn_list.append(Transition((6, 6, 2), (6, 7, 2), f / 4, "behind"))

    # Rule 6: Transitions for direct collision with stationary (resting) particle
    if p_elast > 0.0:
        xn_list.append(Transition((1, 7, 0), (7, 1, 0), p_elast / 3., "rest"))
        xn_list.append(Transition((1, 7, 0), (7, 2, 0), p_elast / 3., "rest"))
        xn_list.append(Transition((1, 7, 0), (7, 6, 0), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 4, 0), (4, 7, 0), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 4, 0), (3, 7, 0), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 4, 0), (5, 7, 0), p_elast / 3., "rest"))
        xn_list.append(Transition((2, 7, 1), (7, 2, 1), p_elast / 3., "rest"))
        xn_list.append(Transition((2, 7, 1), (7, 1, 1), p_elast / 3., "rest"))
        xn_list.append(Transition((2, 7, 1), (7, 3, 1), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 5, 1), (5, 7, 1), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 5, 1), (4, 7, 1), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 5, 1), (6, 7, 1), p_elast / 3., "rest"))
        xn_list.append(Transition((3, 7, 2), (7, 3, 2), p_elast / 3., "rest"))
        xn_list.append(Transition((3, 7, 2), (7, 2, 2), p_elast / 3., "rest"))
        xn_list.append(Transition((3, 7, 2), (7, 4, 2), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 6, 2), (6, 7, 2), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 6, 2), (1, 7, 2), p_elast / 3., "rest"))
        xn_list.append(Transition((7, 6, 2), (5, 7, 2), p_elast / 3., "rest"))

    # Rule 6 frictionl
    if f > 0.0:
        xn_list.append(Transition((1, 7, 0), (7, 7, 0), f, "rest"))
        xn_list.append(Transition((7, 4, 0), (7, 7, 0), f, "rest"))
        xn_list.append(Transition((2, 7, 1), (7, 7, 1), f, "rest"))
        xn_list.append(Transition((7, 5, 1), (7, 7, 1), f, "rest"))
        xn_list.append(Transition((3, 7, 2), (7, 7, 2), f, "rest"))
        xn_list.append(Transition((7, 6, 2), (7, 7, 2), f, "rest"))

    # Rule 7: Transitions for wall impact
    if p_elast > 0.0:
        xn_list.append(Transition((1, 8, 0), (4, 8, 0), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((1, 8, 0), (3, 8, 0), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((1, 8, 0), (5, 8, 0), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((2, 8, 1), (5, 8, 1), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((2, 8, 1), (4, 8, 1), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((2, 8, 1), (6, 8, 1), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((3, 8, 2), (6, 8, 2), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((3, 8, 2), (5, 8, 2), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((3, 8, 2), (1, 8, 2), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 4, 0), (8, 1, 0), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 4, 0), (8, 6, 0), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 4, 0), (8, 2, 0), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 5, 1), (8, 1, 1), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 5, 1), (8, 2, 1), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 5, 1), (8, 3, 1), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 6, 2), (8, 2, 2), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 6, 2), (8, 3, 2), p_elast / 3, "wall rebound"))
        xn_list.append(Transition((8, 6, 2), (8, 4, 2), p_elast / 3, "wall rebound"))

    # Rule 7 frictional
    if f > 0.0:
        xn_list.append(Transition((1, 8, 0), (7, 8, 0), f, "wall rebound"))
        xn_list.append(Transition((2, 8, 1), (7, 8, 1), f, "wall rebound"))
        xn_list.append(Transition((3, 8, 2), (7, 8, 2), f, "wall rebound"))
        xn_list.append(Transition((8, 4, 0), (8, 7, 0), f, "wall rebound"))
        xn_list.append(Transition((8, 5, 1), (8, 7, 1), f, "wall rebound"))
        xn_list.append(Transition((8, 6, 2), (8, 7, 2), f, "wall rebound"))

    # Rule 8: Transitions for glancing oblique collision
    if p_elast > 0.0:
        xn_list.append(Transition((2, 5, 0), (3, 6, 0), p_elast, "glancing"))
        xn_list.append(Transition((6, 3, 0), (5, 2, 0), p_elast, "glancing"))
        xn_list.append(Transition((3, 6, 1), (4, 1, 1), p_elast, "glancing"))
        xn_list.append(Transition((1, 4, 1), (6, 3, 1), p_elast, "glancing"))
        xn_list.append(Transition((4, 1, 2), (5, 2, 2), p_elast, "glancing"))
        xn_list.append(Transition((2, 5, 2), (1, 4, 2), p_elast, "glancing"))

    # Rule 8 frictional
    if f > 0.0:
        xn_list.append(Transition((2, 5, 0), (7, 7, 0), f, "glancing"))
        xn_list.append(Transition((6, 3, 0), (7, 7, 0), f, "glancing"))
        xn_list.append(Transition((3, 6, 1), (7, 7, 1), f, "glancing"))
        xn_list.append(Transition((1, 4, 1), (7, 7, 1), f, "glancing"))
        xn_list.append(Transition((4, 1, 2), (7, 7, 2), f, "glancing"))
        xn_list.append(Transition((2, 5, 2), (7, 7, 2), f, "glancing"))

    # Rule 9: Transitions for "near-on" collisions
    if p_elast > 0.0:
        xn_list.append(Transition((6, 5, 0), (5, 6, 0), p_elast, "near-on"))
        xn_list.append(Transition((2, 3, 0), (3, 2, 0), p_elast, "near-on"))
        xn_list.append(Transition((1, 6, 1), (6, 1, 1), p_elast, "near-on"))
        xn_list.append(Transition((3, 4, 1), (4, 3, 1), p_elast, "near-on"))
        xn_list.append(Transition((2, 1, 2), (1, 2, 2), p_elast, "near-on"))
        xn_list.append(Transition((4, 5, 2), (5, 4, 2), p_elast, "near-on"))

    # Rule 9 frictional
    if f > 0.0:
        xn_list.append(Transition((6, 5, 0), (7, 6, 0), f / 2, "near-on"))
        xn_list.append(Transition((6, 5, 0), (5, 7, 0), f / 2, "near-on"))
        xn_list.append(Transition((2, 3, 0), (7, 2, 0), f / 2, "near-on"))
        xn_list.append(Transition((2, 3, 0), (3, 7, 0), f / 2, "near-on"))
        xn_list.append(Transition((1, 6, 1), (7, 1, 1), f / 2, "near-on"))
        xn_list.append(Transition((1, 6, 1), (6, 7, 1), f / 2, "near-on"))
        xn_list.append(Transition((3, 4, 1), (7, 3, 1), f / 2, "near-on"))
        xn_list.append(Transition((3, 4, 1), (4, 7, 1), f / 2, "near-on"))
        xn_list.append(Transition((2, 1, 2), (7, 2, 2), f / 2, "near-on"))
        xn_list.append(Transition((2, 1, 2), (1, 7, 2), f / 2, "near-on"))
        xn_list.append(Transition((4, 5, 2), (7, 4, 2), f / 2, "near-on"))
        xn_list.append(Transition((4, 5, 2), (5, 7, 2), f / 2, "near-on"))

    # Rule 10: Transitions for oblique collision with rest particle
    if p_elast > 0.0:
        xn_list.append(Transition((2, 7, 0), (7, 1, 0), p_elast, "oblique with rest"))
        xn_list.append(Transition((6, 7, 0), (7, 1, 0), p_elast, "oblique with rest"))
        xn_list.append(Transition((7, 3, 0), (4, 7, 0), p_elast, "oblique with rest"))
        xn_list.append(Transition((7, 5, 0), (4, 7, 0), p_elast, "oblique with rest"))
        xn_list.append(Transition((3, 7, 1), (7, 2, 1), p_elast, "oblique with rest"))
        xn_list.append(Transition((1, 7, 1), (7, 2, 1), p_elast, "oblique with rest"))
        xn_list.append(Transition((7, 6, 1), (5, 7, 1), p_elast, "oblique with rest"))
        xn_list.append(Transition((7, 4, 1), (5, 7, 1), p_elast, "oblique with rest"))
        xn_list.append(Transition((4, 7, 2), (7, 3, 2), p_elast, "oblique with rest"))
        xn_list.append(Transition((2, 7, 2), (7, 3, 2), p_elast, "oblique with rest"))
        xn_list.append(Transition((7, 5, 2), (6, 7, 2), p_elast, "oblique with rest"))
        xn_list.append(Transition((7, 1, 2), (6, 7, 2), p_elast, "oblique with rest"))

    # Rule 10 frictional
    if f > 0.0:
        xn_list.append(Transition((2, 7, 0), (7, 7, 0), f, "oblique with rest"))
        xn_list.append(Transition((6, 7, 0), (7, 7, 0), f, "oblique with rest"))
        xn_list.append(Transition((7, 3, 0), (7, 7, 0), f, "oblique with rest"))
        xn_list.append(Transition((7, 5, 0), (7, 7, 0), f, "oblique with rest"))
        xn_list.append(Transition((3, 7, 1), (7, 7, 1), f, "oblique with rest"))
        xn_list.append(Transition((1, 7, 1), (7, 7, 1), f, "oblique with rest"))
        xn_list.append(Transition((7, 6, 1), (7, 7, 1), f, "oblique with rest"))
        xn_list.append(Transition((7, 4, 1), (7, 7, 1), f, "oblique with rest"))
        xn_list.append(Transition((4, 7, 2), (7, 7, 2), f, "oblique with rest"))
        xn_list.append(Transition((2, 7, 2), (7, 7, 2), f, "oblique with rest"))
        xn_list.append(Transition((7, 5, 2), (7, 7, 2), f, "oblique with rest"))
        xn_list.append(Transition((7, 1, 2), (7, 7, 2), f, "oblique with rest"))

    # Rule 11: Transitions for oblique collision with wall particle
    if p_elast > 0.0:
        xn_list.append(Transition((2, 8, 0), (3, 8, 0), p_elast, "oblique with wall"))
        xn_list.append(Transition((6, 8, 0), (5, 8, 0), p_elast, "oblique with wall"))
        xn_list.append(Transition((8, 3, 0), (8, 2, 0), p_elast, "oblique with wall"))
        xn_list.append(Transition((8, 5, 0), (8, 6, 0), p_elast, "oblique with wall"))
        xn_list.append(Transition((1, 8, 1), (6, 8, 1), p_elast, "oblique with wall"))
        xn_list.append(Transition((3, 8, 1), (4, 8, 1), p_elast, "oblique with wall"))
        xn_list.append(Transition((8, 4, 1), (8, 3, 1), p_elast, "oblique with wall"))
        xn_list.append(Transition((8, 6, 1), (8, 1, 1), p_elast, "oblique with wall"))
        xn_list.append(Transition((4, 8, 2), (5, 8, 2), p_elast, "oblique with wall"))
        xn_list.append(Transition((2, 8, 2), (1, 8, 2), p_elast, "oblique with wall"))
        xn_list.append(Transition((8, 1, 2), (8, 2, 2), p_elast, "oblique with wall"))
        xn_list.append(Transition((8, 5, 2), (8, 4, 2), p_elast, "oblique with wall"))

    # Rule 11 frictional
    if f > 0.0:
        xn_list.append(Transition((2, 8, 0), (7, 8, 0), f, "oblique with wall"))
        xn_list.append(Transition((6, 8, 0), (7, 8, 0), f, "oblique with wall"))
        xn_list.append(Transition((8, 3, 0), (8, 7, 0), f, "oblique with wall"))
        xn_list.append(Transition((8, 5, 0), (8, 7, 0), f, "oblique with wall"))
        xn_list.append(Transition((1, 8, 1), (7, 8, 1), f, "oblique with wall"))
        xn_list.append(Transition((3, 8, 1), (7, 8, 1), f, "oblique with wall"))
        xn_list.append(Transition((8, 4, 1), (8, 7, 1), f, "oblique with wall"))
        xn_list.append(Transition((8, 6, 1), (8, 7, 1), f, "oblique with wall"))
        xn_list.append(Transition((4, 8, 2), (7, 8, 2), f, "oblique with wall"))
        xn_list.append(Transition((2, 8, 2), (7, 8, 2), f, "oblique with wall"))
        xn_list.append(Transition((8, 1, 2), (8, 7, 2), f, "oblique with wall"))
        xn_list.append(Transition((8, 5, 2), (8, 7, 2), f, "oblique with wall"))

    # Gravity rule 1: rising particles become rest particles
    if g > 0.0:
        xn_list.append(Transition((0, 1, 0), (0, 7, 0), g, "gravity 1"))
        xn_list.append(Transition((1, 1, 0), (1, 7, 0), g, "gravity 1"))
        xn_list.append(Transition((2, 1, 0), (2, 7, 0), g, "gravity 1"))
        xn_list.append(Transition((3, 1, 0), (3, 7, 0), g, "gravity 1"))
        xn_list.append(Transition((4, 1, 0), (4, 7, 0), g, "gravity 1"))
        xn_list.append(Transition((5, 1, 0), (5, 7, 0), g, "gravity 1"))
        xn_list.append(Transition((6, 1, 0), (6, 7, 0), g, "gravity 1"))
        xn_list.append(Transition((7, 1, 0), (7, 7, 0), g, "gravity 1"))
        xn_list.append(Transition((8, 1, 0), (8, 7, 0), g, "gravity 1"))

    # Gravity rule 2: resting particles become falling particles (if not above
    # rest or wall?)
    if g > 0.0:
        xn_list.append(Transition((0, 7, 0), (0, 4, 0), g, "gravity 2"))
    #        xn_list.append( Transition((1,7,0), (1,4,0), g, 'gravity 2') )
    #        xn_list.append( Transition((2,7,0), (2,4,0), g, 'gravity 2') )
    #        xn_list.append( Transition((3,7,0), (3,4,0), g, 'gravity 2') )
    #        xn_list.append( Transition((4,7,0), (4,4,0), g, 'gravity 2') )
    #        xn_list.append( Transition((5,7,0), (5,4,0), g, 'gravity 2') )
    #        xn_list.append( Transition((6,7,0), (6,4,0), g, 'gravity 2') )

    # Gravity rule 3: up/sideways particles become down/sideways particles
    if g > 0.0:
        xn_list.append(Transition((0, 2, 0), (0, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((1, 2, 0), (1, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((2, 2, 0), (2, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((3, 2, 0), (3, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((4, 2, 0), (4, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((5, 2, 0), (5, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((6, 2, 0), (6, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((7, 2, 0), (7, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((8, 2, 0), (8, 3, 0), g, "gravity 3"))
        xn_list.append(Transition((0, 6, 0), (0, 5, 0), g, "gravity 3"))
        xn_list.append(Transition((1, 6, 0), (1, 5, 0), g, "gravity 3"))
        xn_list.append(Transition((2, 6, 0), (2, 5, 0), g, "gravity 3"))
        xn_list.append(Transition((3, 6, 0), (3, 5, 0), g, "gravity 3"))
        xn_list.append(Transition((4, 6, 0), (4, 5, 0), g, "gravity 3"))
        xn_list.append(Transition((5, 6, 0), (5, 5, 0), g, "gravity 3"))
        xn_list.append(Transition((6, 6, 0), (6, 5, 0), g, "gravity 3"))
        xn_list.append(Transition((7, 6, 0), (7, 5, 0), g, "gravity 3"))
        xn_list.append(Transition((8, 6, 0), (8, 5, 0), g, "gravity 3"))

    # Gravity rule 4: down/side to straight down
    if g > 0.0:
        xn_list.append(Transition((0, 3, 0), (0, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((1, 3, 0), (1, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((2, 3, 0), (2, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((3, 3, 0), (3, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((4, 3, 0), (4, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((5, 3, 0), (5, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((6, 3, 0), (6, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((7, 3, 0), (7, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((8, 3, 0), (8, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((0, 5, 0), (0, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((1, 5, 0), (1, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((2, 5, 0), (2, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((3, 5, 0), (3, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((4, 5, 0), (4, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((5, 5, 0), (5, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((6, 5, 0), (6, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((7, 5, 0), (7, 4, 0), g, "gravity 4"))
        xn_list.append(Transition((8, 5, 0), (8, 4, 0), g, "gravity 4"))

    # Gravity rule for lateral destabilization (represents grain
    # motion above angle of repose on sloping surface)
    if g > 0.0:
        xn_list.append(Transition((7, 0, 2), (3, 0, 2), g / 2.0, "gravity"))
        xn_list.append(Transition((0, 7, 1), (0, 5, 1), g / 2.0, "gravity"))

    return xn_list

# -*- coding: utf-8 -*- Provide rules and states for a lattice-grain CTS model.  Created on Tue Feb  2 07:37:59 2016  @author: gtucker Create and return dict of states for lattice-grain model. Creates and returns a list of Transition() objects to represent state     transitions for simple granular mechanics model.      Parameters     ----------     g : float (optional)         Gravitational parameter. 1/g is the time taken for an initially         stationary particle to fall one cell width. (Dimensions of 1/time)     f : float (optional)         Frictional parameter. Dimensions of 1/time. Probability per unit time         of a frictional collision, in which one or both colliding particles         come to a halt.     motion : float (optional)         Rate of motion (cells per time unit)      Returns     -------     xn_list : list of Transition objects         List of objects that encode information about the link-state transitions.      Notes     -----     The transitions for this version of lattice gas have 11 pair-transition     rules. The shorthand for the states is as follows:          AR = air/empty         IN = incoming particle (moving toward its neighbor)         OU = outgoing particle (moving away from its neighbor)         IO = incoming at an oblique angle         OO = outgoing at an oblique angle         RE = rest particle         WA = wall particle         op = oblique pair moving in opposite perpendicular direction         sm = oblique pair moving in same perpendicular direction      The 11 pairs with transitions are:          1. AR-IN => IN-AR (move to empty particle)         2. IN-IN => OO-OO-op (1/3 each dir), OU-OU (1/3) (head-on collision)         3. IN-IO => OO-OU (oblique collision)         4. IN-OO => IO-OU (oblique collision from behind)         5. IN-OU => IO-OO (1/4 each of 2 directions) (collision from behind)         6. IN-RE => RE-OU (1/3) RE-OO (1/3 each dir) (collision with rest)         7. IN-WA => OU-WA (1/3) OO-WA (1/3 each dir) (wall collision)         8. IO-IO-op => OO-OO-op (1/2 each dir) (glacing collision)         9. IO-IO-sm => OO-OO-sm (30-degree collision)         10. IO-RE => RE-OU (oblique collision with rest particle)         11. IO-WA => OO-WA (oblique collision with wall) # Set elastic-response rate, and scale frictional-response rate to motion # rate # rate of elastic (non-dissipative) collision # Rule 1: Transitions for particle movement into an empty cell # Rule 2: Transitions for head-on collision: elastic # Rule 2: Transitions for head-on collision: frictional dissipation # Rule 3: Transitions for oblique collision: elastic # Rule 3 frictional # Rule 4: Transitions for oblique-from-behind collisions # Rule 4 frictional # Rule 5: Transitions for direct-from-behind collisions # Rule 5 frictional # Rule 6: Transitions for direct collision with stationary (resting) particle # Rule 6 frictionl # Rule 7: Transitions for wall impact # Rule 7 frictional # Rule 8: Transitions for glancing oblique collision # Rule 8 frictional # Rule 9: Transitions for "near-on" collisions # Rule 9 frictional # Rule 10: Transitions for oblique collision with rest particle # Rule 10 frictional # Rule 11: Transitions for oblique collision with wall particle # Rule 11 frictional # Gravity rule 1: rising particles become rest particles # Gravity rule 2: resting particles become falling particles (if not above # rest or wall?) #        xn_list.append( Transition((1,7,0), (1,4,0), g, 'gravity 2') ) #        xn_list.append( Transition((2,7,0), (2,4,0), g, 'gravity 2') ) #        xn_list.append( Transition((3,7,0), (3,4,0), g, 'gravity 2') ) #        xn_list.append( Transition((4,7,0), (4,4,0), g, 'gravity 2') ) #        xn_list.append( Transition((5,7,0), (5,4,0), g, 'gravity 2') ) #        xn_list.append( Transition((6,7,0), (6,4,0), g, 'gravity 2') ) # Gravity rule 3: up/sideways particles become down/sideways particles # Gravity rule 4: down/side to straight down # Gravity rule for lateral destabilization (represents grain # motion above angle of repose on sloping surface)