My current project requires a turbulence code that will use the velocity components of a fluid to advect a distribution of passive tracers.
I have written a code below that uses a structure of arrays (SoA) to define the fluid so that I can make FFTW calls to transform the velocity components later. The passive tracers are defined as array of structures (AoS) as this allows me to enable quick sort the tracers along any given direction.
#include<stdio.h>
#include<stddef.h>
#include<stdlib.h>
#include<complex.h>
// 2D fluid (SoA)
typedef struct {
ptrdiff_t n;
ptrdiff_t size;
double *u0;
double *u1;
ptrdiff_t numTracers;
} myfluid;
// Passive tracers (AoS)
typedef struct {
ptrdiff_t ID;
double r0;
double r1;
} mytracer;
void allocate_memory (myfluid *f, mytracer **t){
f->u0 = malloc (f->size * sizeof(double));
f->u1 = malloc (f->size * sizeof(double));
*t = malloc (f->numTracers * sizeof(mytracer));
}
void initialize (myfluid *f, mytracer *t) {
// random velocity component in the range (-1, 1)
srand48 (1);
for (ptrdiff_t i = 0; i < f->size; i ++){
f->u0[i] = -1. + 2 * drand48 ();
f->u1[i] = -1. + 2 * drand48 ();
}
// random positions inside the unit square (0, 1)
for (ptrdiff_t i = 0; i < f->numTracers; i ++){
t[i].ID = i;
t[i].r0 = drand48 ();
t[i].r1 = drand48 ();
}
}
int compare (const void *a, const void *b){
mytracer *A = (mytracer *)a;
mytracer *B = (mytracer *)b;
return (A->r0 > B->r0) - (A->r0 < B->r0);
}
void printout (myfluid *f, mytracer *t) {
printf("\n2D fluid\n");
for (ptrdiff_t i = 0; i < f->n; i ++){
for (ptrdiff_t j = 0; j < f->n; j ++){
ptrdiff_t idx = j + i * f->n;
printf ("(%.2lf, %.2lf)\t", f->u0[idx], f->u1[idx]);
}
printf ("\n");
}
printf("\nTracers\n");
for (ptrdiff_t i = 0; i < f->numTracers; i ++){
printf ("%td\t %.2lf\t %.2lf\n", t[i].ID, t[i].r0, t[i].r1);
}
}
void deallocate_memory (myfluid *fluid, mytracer **t) {
free (fluid->u0);
free (fluid->u1);
free (*t);
}
int main (int argc, char **argv){
myfluid fluid;
mytracer *tracer;
fluid.n = 8;
fluid.numTracers = 8;
fluid.size = fluid.n * fluid.n;
allocate_memory (&fluid, &tracer);
initialize (&fluid, tracer);
// sort the tracers along r0 direction
qsort(tracer, fluid.numTracers, sizeof(mytracer), compare);
printout (&fluid, tracer);
deallocate_memory(&fluid, &tracer);
return 0;
}
Is it possible to pass the tracers also as SoA, without losing the ability to quick sort them as shown here?
qsort()can only swap whole elements of a single array-like object around, so the answer is "no".qsort_rfunction (likeqsortbut with a context pointer value that also gets passed to the comparison function), using a pointer to the SoA as context. Then you just have the problem of swapping the elements of each array according to the sorted array of indices. It's a bit tricky to do the swapping in-place because in general there will be multiple cyclic chains of elements to be rotated, but there are algorithms to deal with that sort of thing..n,.sizeand.numTracersof typeptrdiff_tinstead of typesize_t?tracer[0], tracer[1], ...becomestracer[helper[0]], tracer[helper[1]], ...keepingtracer[0],tracer[1], ... unmoved