/** * DATA STRUCTURES */ typedef struct MM { float x; float y; int carbone; int dormance; } MM; typedef struct OM { float x; float y; int carbone; int lock; } OM; typedef struct MiorWorld { int nbMM; int nbOM; int RA; float RR; float GR; float K; int width; int minSize; int CO2; int lock; } MiorWorld; typedef struct RandomGen { ulong a; ulong b; ulong c; } RandomGen; /** * HELPER FUNCTIONS */ // Compatibility macros for OpenCL <= 1.1 #if __OPENCL_VERSION__ <= CL_VERSION_1_0 #define atomic_inc(p) atom_inc(p) #define atomic_add(p, v) atom_add(p, v) //#define atomic_cmpxchg(p, o, v) atom_cmpxchg(p, o, v) #endif // End of compatibility macros //#define LOCK_P(lock, id) while (atomic_cmpxchg(&(lock), -1, id) != id) {} //#define LOCK_V(lock, id) while (atomic_cmpxchg(&(lock), id, -1) != -1) {} #define store_CO2(world, val) atomic_add(&(world->CO2), val) #define val(arr, iMM, iOM) arr[iMM * world->nbOM + iOM] // RNG void rng_init(RandomGen *r, long seed) { r->a = seed; r->b = 0; r->c = 362436; } unsigned long rng_rand(RandomGen *r) { const long old = r->b; r->b = r->a * 1103515245 + 12345; r->a = (~old ^ (r->b >> 3)) - r->c++; return r->b; } float rng_rand_01(RandomGen *r) { return (rng_rand(r) & 4294967295) / 4294967295.0f; } void shuffle_indexes(global int * array, int size, RandomGen *r) { for (int i = size - 1; i >= 1; i--) { const int j = rng_rand_01(r) * (i + 1); const int t = array[j]; array[j] = array[i]; array[i] = t; } } /** * KERNELS */ kernel void topology( global MM *mmList, global OM *omList, global int *topo, global MiorWorld *world, global int *totals) { const int iMM = get_global_id(0); const int iOM = get_global_id(1); const float dx = omList[iOM].x - mmList[iMM].x; const float dy = omList[iOM].y - mmList[iMM].y; const float dist = hypot(dx, dy); if (dist <= world->RA) { val(topo, iMM, iOM) = 0; atomic_inc(totals + iOM); } else { val(topo, iMM, iOM) = -1; } } kernel void carbon_scatter( global MM * mmList, global OM * omList, global int * topo, global MiorWorld * world, global int * totals) { const int iOM = get_global_id(0); const int total = totals[iOM]; if (total == 0) { return; } const int part = (world->K * omList[iOM].carbone) / total; int totalCarbon = 0; for (int iMM = 0; iMM < world->nbMM; iMM++) { const int offer = val(topo, iMM, iOM); if (offer >= 0) { val(topo, iMM, iOM) = part; totalCarbon += part; } } omList[iOM].carbone -= totalCarbon; } kernel void carbon_reduce( global MM * mmList, global OM * omList, global int * topo, global MiorWorld * world) { const int iOM = get_global_id(0); int totalCarbon = 0; for (int iMM = 0; iMM < world->nbMM; iMM++) { const int part = val(topo, iMM, iOM); if (part >= 0) { val(topo, iMM, iOM) = 0; totalCarbon += part; } } omList[iOM].carbone += totalCarbon; } kernel void live( global MM *mmList, global OM *omList, global int *topo, global MiorWorld *world, global int *omIndexes, long seed) { int iMM = get_global_id(0); global int * targets = topo + iMM * world->nbOM; global int * indexes = omIndexes + iMM * world->nbOM; RandomGen rng; for (int i = 0; i < world->nbOM; i++) { indexes[i] = i; } if (seed != 0) { rng_init(&rng, seed + iMM); shuffle_indexes(indexes, world->nbOM, &rng); } const int breathNeed = world->RR * mmList[iMM].carbone; int remainingBreathNeed = breathNeed; int iOM; // Check breath requirement for (int i = 0; i < world->nbOM; i++) { iOM = indexes[i]; if (val(topo, iMM, iOM) >= 0) { remainingBreathNeed -= val(topo, iMM, iOM); if (remainingBreathNeed <= 0) { break; } } } // Go into dormancy if breath requirement is not met if (remainingBreathNeed > 0) { mmList[iMM].dormance = 1; return; } else { mmList[iMM].dormance = 0; } // Actual breathing and growth remainingBreathNeed = breathNeed; const int growthNeed = world->GR * mmList[iMM].carbone + breathNeed; int remainingGrowthNeed = growthNeed; int consum = 0, growth = 0; for (int i = 0; i < world->nbOM; i++) { iOM = indexes[i]; int offer = val(topo, iMM, iOM); if (offer >= 0) { // If breathing phase is not finished if (remainingBreathNeed > 0) { consum = min(offer, remainingBreathNeed); atomic_add(&(world->CO2), consum); remainingBreathNeed -= consum; offer -= consum; } consum = min(offer, remainingGrowthNeed); remainingGrowthNeed -= consum; growth += consum; offer -= consum; val(topo, iMM, iOM) = offer; } if (remainingGrowthNeed <= 0) { break; } } mmList[iMM].carbone += growth; }