|  | 
To start using symbolic expressions, you first need to define a set of symbols to represent your variables, as well as labels to represent your particle set(s).
        A symbol representing each particle's position
        is defined as
      
Symbol<position> p;
        A symbol representing the variable alive
        is defined as
      
Symbol<alive> _alive;
        A label representing the particle set particles
        with type MyParticles is
        defined as
      
Label<0, Particles_t> a(particles); Label<1, Particles_t> b(particles);
The first template argument is the id of the label, and the second is the type of the particle set label refers to. Note that the type of each label must be unique, as this type is used to determine which particle you mean when using the label within expressions. You can use the id template argument to ensure that each label type is unique.
        Labels refer to a specific particle set. For example, given a bivariate neighbour
        expression involving two particles from particles,
        the label a defined above
        would refer to the first particle, and b
        would refer to the second. Note that the result values of a bivariate expression
        will form a matrix of values, with particle a
        corresponding to the row of the matrix, and particle b
        corresponding to the column.
      
        Now we have defined our labels and symbols, we can create a simple expression
        to set the position of all particles to double3(0,0,1)
      
p[a] = vdouble3(0, 0, 1);
        If we want to add 1 to every
        particle position, we can use an increment expression
      
p[a] += 1;
        A special case involves the alive
        variable flag. If we use our _alive
        symbol to set all the alive flag's to false
        like so
      
_alive[a] = false;
        Then after the expression is complete Aboria will call the delete_particles
        member function to delete all the particles with get<alive>(particle) == false
        (i.e. all of them). After this expression the particle container will be
        empty.
      
        Single-particle dependent expressions can easily be built by using a single
        label on the RHS of the expression. For example we can also add a constant
        value 1 to each particle position
        like so
      
p[a] = p[a] + 1;
        Or we can use a flag variable flag,
        along with its symbol f,
        so define two sets of particles within the same container and only change
        the particle position if get<flag>(particle) == true.
        Recall that we can't use boolean flags so we can instead use a uint8_t datatype. For example
      
ABORIA_VARIABLE(flag,uint8_t,"my flag"); // create particle container and set flags and positions here
Symbol<flag> f; p[a] = if_else(f[a], p[a] + 1, p[a]);
        We can even selectively delete particle using our f
        and _alive symbols.
      
_alive[a] = if_else(f[a], true, false);
        So far we have used constant or univariate expressions on the RHS of an assignment
        operator. This makes sense because we can only assign an expression to a
        single particle if that expression depends on a constant or that particle's
        variables. However, we might also want our expression to depend on the other
        particles in the container, or another container. In this case we wish to
        use a bivariate expression. For example, the following equation defined a
        sum over all other particles (with label b),
        using the function w, which
        depends on the separation of particle a
        and b.
      
$$ p_a = \sum_b w(p_b-p_a) $$
        Normally for a bivariate expression we wish to accumulate the contribution
        from other particles in order to arrive at a result. This is most often done
        with a summation. In Aboria, we can define an accumulator object, which takes
        a single template argument which is the function or functor to accumulate
        with. For example, the following defines a summation accumulator using std::plus
      
Accumulate<std::plus<vdouble3>> sum;
Once we have this summation accumulator, we can write an expression similar to the equation above like so, using $w(p_b) = (p_b - p_a)$.Note that the first argument to `sum` is set to `true`, indicating the summation is over
b.
          p[a] = sum(b, p[b] - p[a]);
We might also want to sum the inverse distance between particle pairs, like so
p[a] = sum(b, 1.0 / (p[b] - p[a]));
        Unfortunately if a and b refer to the same particle container
        this will result a divide-by-zero at runtime, when a
        and b are labels for the
        same particle. Therefore we can restrict the evaluation of the sum by setting
        the second argument to ensure that the id
        of particles a and b are non-identical. Recall that id is a built-in variable that contains
        a unique id for each particle in the container.
      
Symbol<id> _id; p[a] = sum(b, if_else(_id[a] != _id[b], 1.0, 0.0) / (p[b] - p[a]));
        So the first argument to sum
        is the label to sum over, the second is the conditional expression that must
        evaluate to true to be included
        in the summation, and the third is an expression that provides the value
        to be included in the summation.
      
There is a special case for the conditional expression, when you want to sum over all particles within a certain radius. This can be expressed using the Aboria::AccumulateWithinDistance summation
AccumulateWithinDistance<std::plus<vdouble3>> sum_within_two(2); auto dx = create_dx(a, b); p[a] = sum_within_two(b, dx / pow(norm(dx), 2));
        where dx is a Dx symbol representing the shortest
        vector from b to a. Note that this might be different from
        p[a]-p[b]
        for periodic domains. The symbolic function [functionref Aboria::norm norm]
        returns the 2-norm, or magnitude, of the vector dx,
        and the symbolic function [functionref Aboria::pow pow] returns the first
        argument to the power of the second (in much the same way as std::pow, but
        lazily evaluated).
      
        In this case Aboria will perform a summation over neighbours closer than
        a radius of 2, and will use the neighbourhood searching facility described
        in aboria.neighbourhood_searching
        to find these neighbouring particles. Note that you need to call Aboria::Particles::init_neighbour_search
        before any bivariate expressions using neighbourhood searching.
      
As another, more complete example, we can use the
        neighbourhood-searching expressions to count the number of particles within
        a distance of 2 of each individual
        particle, storing the result in a variable called count.
      
ABORIA_VARIABLE(count, int, "number of surrounding particles") typedef Particles<std::tuple<count>> MyParticles; typedef MyParticles::position position_t; // add some particles MyParticles particles2(N); std::uniform_real_distribution<double> uni(0, 1); for (size_t i = 0; i < N; ++i) { auto &gen = get<generator>(particles2)[i]; get<position_t>(particles2)[i] = vdouble3(uni(gen), uni(gen), uni(gen)); } // initialise neighbour searching particles2.init_neighbour_search( vdouble3::Constant(0), vdouble3::Constant(1), vbool3::Constant(false)); // define symbols and labels, and sum Symbol<count> c; Label<0, MyParticles> i(particles2); Label<1, MyParticles> j(particles2); AccumulateWithinDistance<std::plus<int>> neighbour_sum(2); auto dx_ij = create_dx(i, j); // count neighbouring particles within a distance of 2 c[i] = neighbour_sum(j, 1);