sandbox/Antoonvh/particle_reference.h

    Reference particles with scalar-field data

    It is possible to assign particles to cells, and find them.

    The user interface is something like this:

    ...
    Particles parts = new_...
    scalar p[];
    assign_particles (parts, p);
    foreach() {
     double ccx = x, ccy = y;
      foreach_particle_point(p) {
        printf ("%g %g %g %g", x, y, ccx, ccy);
      }
    }

    Except(!) qcc does not allow these nested loops and you must wrap it in some function (see test).

    attribute { 
      int plist; //offset by 1
    }
    
    #include "particle.h"
    
    typedef union {
      int * p;
      double d;
    } datic;
    
    #define pointer_v(a) ((datic){.d = a}.p)
    #define field_v(a) ((datic){.p = a}.d)
    
    bool particle_in_cell (particle part, Point point) {
      coord cc = {x, y, z};
      foreach_dimension() {
        //  Particles can be *on* the rhs face 
        if (part.x >  cc.x + Delta/2. ||
    	part.x <= cc.x - Delta/2.) 
          return false;
      }
      return true;
    }
    
    #if TREE
    void p_refine (Point point, scalar s) {
      if (!s[]) { // No particles
        foreach_child()
          s[] = 0;
      } else { // distribute particles among children
        int * ind = pointer_v(s[]);
        particles parts = pl[s.plist - 1];
        foreach_child() {
          int n = 0, c = 0;
          int * indc = NULL;
          while (ind[n] >= 0) {
    	if (particle_in_cell (parts[ind[n]], point)) {
    	  indc = realloc (indc, (c + 2)*sizeof(int));
    	  indc[c++] = ind[n];
    	  indc[c] = -1;
    	}
    	n++;
          }
          if (indc == NULL)
    	s[] = 0; 
          else
    	s[] = field_v(indc);
        }
      }
    }
    
    void p_coarsen (Point point, scalar s) {
      s[] = 0;
      int nt = 0;
      foreach_child()
        if (s[]) {
          int * indc = pointer_v(s[]);
          int n = 0; while(indc[n++] >= 0);
          nt += n - 1;
        }
      if (nt) {
        int * indp = NULL;
        indp = malloc (sizeof(int)*nt + 1);
        nt = 0;
        foreach_child()
          if (s[]) {
    	int * indc = pointer_v(s[]);
    	int n = 0;
    	while(indc[n] >= 0) 
    	  indp[nt++] = indc[n++]; 
          }
        indp[nt] = -1;
        s[] = field_v(indp);
      }
    }
    #endif
    
    void free_scalar_data (scalar s) {
      foreach_cell() {
        free(pointer_v(s[]));
      }
    }
    
    void assign_particles (Particles plist, scalar s) {
      if (s.plist > 0)
        free_scalar_data (s);
      s.plist = plist + 1;
      foreach()
        s[] = 0;
    #if TREE
      s.refine = s.prolongation = p_refine;
      s.coarsen = s.restriction = p_coarsen;
    #endif
      foreach_particle_in(plist) {
        Point point = locate (x, y, z);
        int * ind = NULL, n = 0;
        if (s[] == 0.) 
          n = 1;
        else {
          ind = pointer_v(s[]);
          while (ind[n++] >= 0);
        }
        ind = realloc (ind, (n + 1)*sizeof(int));
        ind[n - 1] = j;
        ind[n] = -1;
        s[] = field_v(ind);
      }
      boundary ({s}); // Find particles in ghosts and parent cells
    }
    
    // We need wrappers to help qcc
    double value_p (scalar s, Point point) {
      return s[]; // Macro may not be expanded during `@def` preprocessing
    }
    
    int plist_s (scalar s) {
      return s.plist - 1; // Atribute may not exists at `@def` preprocessing
    }
    
    @def foreach_particle_point(s) {
      int l = plist_s(s);				
      if (value_p(s, point)) {					
        int * ind = pointer_v(value_p(s, point));			
        for (int n = 0; ind[n] >= 0; n++) {		
          int j = ind[n];
          PARTICLE_VARIABLES;			
          @						
    	@def end_foreach_particle_point()
    	}					
        }						
        }						
          @

    Test

    Todo

    • MPI