264 lines
9.3 KiB
Plaintext
264 lines
9.3 KiB
Plaintext
/**
|
|
* lbm_cuda_daemon.cu
|
|
*
|
|
* CUDA LBM daemon that publishes real-time grid state
|
|
* to ZeroMQ for LBM-LLM bridge
|
|
*/
|
|
|
|
#include <cuda_runtime.h>
|
|
#include <nvml.h>
|
|
#include <zmq.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <json-c/json.h>
|
|
|
|
#define NX 512
|
|
#define NY 512
|
|
#define Q 9
|
|
#define PUBLISH_HZ 100 // 100Hz = 10ms
|
|
|
|
__constant__ int d_cx[Q] = {0, 1, 0, -1, 0, 1, -1, -1, 1};
|
|
__constant__ int d_cy[Q] = {0, 0, 1, 0, -1, 1, 1, -1, -1};
|
|
__constant__ float d_w[Q] = {4.0f/9.0f, 1.0f/9.0f, 1.0f/9.0f, 1.0f/9.0f, 1.0f/9.0f,
|
|
1.0f/36.0f, 1.0f/36.0f, 1.0f/36.0f, 1.0f/36.0f};
|
|
|
|
// Device arrays
|
|
float *d_f, *d_rho, *d_ux, *d_uy;
|
|
|
|
void load_etch(const char* filename) {
|
|
FILE* fp = fopen(filename, "rb");
|
|
if (!fp) {
|
|
printf("No etch file '%s' found, initializing equilibrium...\n", filename);
|
|
// Fresh equilibrium: rho=1, ux=uy=0
|
|
float *h_f = (float*)malloc(NX * NY * Q * sizeof(float));
|
|
const float w[Q] = {4.0f/9.0f, 1.0f/9.0f, 1.0f/9.0f, 1.0f/9.0f, 1.0f/9.0f,
|
|
1.0f/36.0f, 1.0f/36.0f, 1.0f/36.0f, 1.0f/36.0f};
|
|
for (int i = 0; i < NX * NY; i++) {
|
|
for (int j = 0; j < Q; j++) {
|
|
h_f[i * Q + j] = w[j]; // rho=1, u=0 → f_eq = w_i
|
|
}
|
|
}
|
|
cudaMemcpy(d_f, h_f, NX * NY * Q * sizeof(float), cudaMemcpyHostToDevice);
|
|
free(h_f);
|
|
return;
|
|
}
|
|
float *h_rho = (float*)malloc(NX * NY * sizeof(float));
|
|
float *h_ux = (float*)malloc(NX * NY * sizeof(float));
|
|
float *h_uy = (float*)malloc(NX * NY * sizeof(float));
|
|
fread(h_rho, sizeof(float), NX * NY, fp);
|
|
fread(h_ux, sizeof(float), NX * NY, fp);
|
|
fread(h_uy, sizeof(float), NX * NY, fp);
|
|
fclose(fp);
|
|
|
|
cudaMemcpy(d_rho, h_rho, NX * NY * sizeof(float), cudaMemcpyHostToDevice);
|
|
cudaMemcpy(d_ux, h_ux, NX * NY * sizeof(float), cudaMemcpyHostToDevice);
|
|
cudaMemcpy(d_uy, h_uy, NX * NY * sizeof(float), cudaMemcpyHostToDevice);
|
|
|
|
const int cx[Q] = {0, 1, 0, -1, 0, 1, -1, -1, 1};
|
|
const int cy[Q] = {0, 0, 1, 0, -1, 1, 1, -1, -1};
|
|
const float w[Q] = {4.0f/9.0f, 1.0f/9.0f, 1.0f/9.0f, 1.0f/9.0f, 1.0f/9.0f,
|
|
1.0f/36.0f, 1.0f/36.0f, 1.0f/36.0f, 1.0f/36.0f};
|
|
float *h_f = (float*)malloc(NX * NY * Q * sizeof(float));
|
|
for (int i = 0; i < NX * NY; i++) {
|
|
float uu = h_ux[i] * h_ux[i] + h_uy[i] * h_uy[i];
|
|
for (int j = 0; j < Q; j++) {
|
|
float cu = cx[j] * h_ux[i] + cy[j] * h_uy[i];
|
|
h_f[i * Q + j] = w[j] * h_rho[i] * (1.0f + 3.0f * cu + 4.5f * cu * cu - 1.5f * uu);
|
|
}
|
|
}
|
|
cudaMemcpy(d_f, h_f, NX * NY * Q * sizeof(float), cudaMemcpyHostToDevice);
|
|
free(h_rho); free(h_ux); free(h_uy); free(h_f);
|
|
printf("Etch loaded: %s\n", filename);
|
|
}
|
|
|
|
// Khra'gixx wave for live grid
|
|
__device__ float khra_gixx_live(int x, int y, int cycle) {
|
|
float khra = sinf(2.0f * M_PI * x / 64.0f + cycle * 0.05f) *
|
|
cosf(2.0f * M_PI * y / 64.0f + cycle * 0.03f) * 0.02f;
|
|
float gixx = sinf(2.0f * M_PI * x / 8.0f + cycle * 0.4f) *
|
|
cosf(2.0f * M_PI * y / 8.0f + cycle * 0.35f) * 0.008f;
|
|
return khra + gixx * (1.0f + sinf(cycle * 0.1f));
|
|
}
|
|
|
|
__global__ void collide_kernel_daemon(float *f, float *rho, float *ux, float *uy, float omega, int cycle) {
|
|
int x = blockIdx.x * blockDim.x + threadIdx.x;
|
|
int y = blockIdx.y * blockDim.y + threadIdx.y;
|
|
if (x >= NX || y >= NY) return;
|
|
int idx = y * NX + x;
|
|
|
|
float rho_local = 0.0f, ux_local = 0.0f, uy_local = 0.0f;
|
|
for (int i = 0; i < Q; i++) {
|
|
float fi = f[idx * Q + i];
|
|
rho_local += fi;
|
|
ux_local += d_cx[i] * fi;
|
|
uy_local += d_cy[i] * fi;
|
|
}
|
|
ux_local /= rho_local;
|
|
uy_local /= rho_local;
|
|
|
|
// Live Khra'gixx injection
|
|
float khragixx = khra_gixx_live(x, y, cycle);
|
|
ux_local += khragixx * (1.0f + sinf(y * 0.1f));
|
|
uy_local += khragixx * (1.0f + cosf(x * 0.1f)) * 0.7f;
|
|
|
|
rho[idx] = rho_local;
|
|
ux[idx] = ux_local;
|
|
uy[idx] = uy_local;
|
|
|
|
float uu = ux_local * ux_local + uy_local * uy_local;
|
|
for (int i = 0; i < Q; i++) {
|
|
float cu = d_cx[i] * ux_local + d_cy[i] * uy_local;
|
|
float f_eq = d_w[i] * rho_local * (1.0f + 3.0f * cu + 4.5f * cu * cu - 1.5f * uu);
|
|
f[idx * Q + i] = f[idx * Q + i] - omega * (f[idx * Q + i] - f_eq);
|
|
}
|
|
}
|
|
|
|
// Host: Compute metrics
|
|
void compute_metrics(float *h_ux, float *h_uy, float *h_rho,
|
|
float *coherence, float *h64, float *h32,
|
|
float *asymmetry, float *vorticity) {
|
|
// Coherence: mean velocity magnitude
|
|
float coh = 0.0f;
|
|
for (int i = 0; i < NX * NY; i++) {
|
|
coh += sqrtf(h_ux[i] * h_ux[i] + h_uy[i] * h_uy[i]);
|
|
}
|
|
*coherence = coh / (NX * NY);
|
|
|
|
// H64: correlate with 64-cell wave
|
|
float sum_64 = 0.0f;
|
|
int y = NY / 2;
|
|
for (int x = 0; x < NX; x++) {
|
|
int idx = y * NX + x;
|
|
sum_64 += h_ux[idx] * sinf(2.0f * M_PI * x / 64.0f);
|
|
}
|
|
*h64 = fabs(sum_64) / NX;
|
|
|
|
// H32: correlate with 32-cell wave
|
|
float sum_32 = 0.0f;
|
|
for (int x = 0; x < NX; x++) {
|
|
int idx = y * NX + x;
|
|
sum_32 += h_ux[idx] * sinf(2.0f * M_PI * x / 32.0f);
|
|
}
|
|
*h32 = fabs(sum_32) / NX;
|
|
|
|
// Asymmetry: left-right difference
|
|
float asym = 0.0f;
|
|
for (int y = 0; y < NY; y++) {
|
|
for (int x = 0; x < NX/2; x++) {
|
|
int left = y * NX + x;
|
|
int right = y * NX + (NX - 1 - x);
|
|
asym += fabs(h_ux[left] - h_ux[right]);
|
|
}
|
|
}
|
|
*asymmetry = asym / (NX * NY);
|
|
|
|
// Vorticity: curl of velocity field
|
|
float vort = 0.0f;
|
|
for (int y = 1; y < NY - 1; y++) {
|
|
for (int x = 1; x < NX - 1; x++) {
|
|
int idx = y * NX + x;
|
|
float dux_dy = h_ux[idx + NX] - h_ux[idx - NX];
|
|
float duy_dx = h_uy[idx + 1] - h_uy[idx - 1];
|
|
vort += fabs(duy_dx - dux_dy);
|
|
}
|
|
}
|
|
*vorticity = vort / ((NX - 2) * (NY - 2));
|
|
}
|
|
|
|
int main() {
|
|
printf("=== LBM CUDA DAEMON ===\n");
|
|
printf("Publishing real-time grid state to ZeroMQ\n\n");
|
|
|
|
// CUDA setup
|
|
cudaMalloc(&d_f, NX * NY * Q * sizeof(float));
|
|
cudaMalloc(&d_rho, NX * NY * sizeof(float));
|
|
cudaMalloc(&d_ux, NX * NY * sizeof(float));
|
|
cudaMalloc(&d_uy, NX * NY * sizeof(float));
|
|
|
|
// Initialize from Khra'gixx etch (or fresh equilibrium if file missing)
|
|
load_etch("etch_khragixx.bin");
|
|
|
|
// NVML setup (once, outside loop)
|
|
nvmlInit();
|
|
nvmlDevice_t device;
|
|
nvmlDeviceGetHandleByIndex(0, &device);
|
|
|
|
// ZeroMQ setup
|
|
void *context = zmq_ctx_new();
|
|
void *publisher = zmq_socket(context, ZMQ_PUB);
|
|
zmq_bind(publisher, "tcp://*:5555");
|
|
|
|
printf("Publishing on tcp://*:5555\n");
|
|
printf("Update rate: %dHz\n\n", PUBLISH_HZ);
|
|
|
|
// Host arrays
|
|
float *h_ux = (float*)malloc(NX * NY * sizeof(float));
|
|
float *h_uy = (float*)malloc(NX * NY * sizeof(float));
|
|
float *h_rho = (float*)malloc(NX * NY * sizeof(float));
|
|
|
|
dim3 blockSize(16, 16);
|
|
dim3 gridSize((NX + 15) / 16, (NY + 15) / 16);
|
|
|
|
int cycle = 0;
|
|
|
|
printf("[Daemon running - Ctrl+C to stop]\n");
|
|
|
|
while (1) {
|
|
// Run LBM step
|
|
collide_kernel_daemon<<<gridSize, blockSize>>>(d_f, d_rho, d_ux, d_uy, 1.95f, cycle);
|
|
cudaDeviceSynchronize();
|
|
|
|
// Copy to host
|
|
cudaMemcpy(h_ux, d_ux, NX * NY * sizeof(float), cudaMemcpyDeviceToHost);
|
|
cudaMemcpy(h_uy, d_uy, NX * NY * sizeof(float), cudaMemcpyDeviceToHost);
|
|
cudaMemcpy(h_rho, d_rho, NX * NY * sizeof(float), cudaMemcpyDeviceToHost);
|
|
|
|
// Compute metrics
|
|
float coherence, h64, h32, asymmetry, vorticity;
|
|
compute_metrics(h_ux, h_uy, h_rho, &coherence, &h64, &h32, &asymmetry, &vorticity);
|
|
|
|
// Get power from NVML (initialized once above)
|
|
unsigned int power_mw;
|
|
nvmlDeviceGetPowerUsage(device, &power_mw);
|
|
float power_w = power_mw / 1000.0f;
|
|
|
|
// Build JSON
|
|
struct json_object *jobj = json_object_new_object();
|
|
json_object_object_add(jobj, "coherence", json_object_new_double(coherence));
|
|
json_object_object_add(jobj, "h64", json_object_new_double(h64));
|
|
json_object_object_add(jobj, "h32", json_object_new_double(h32));
|
|
json_object_object_add(jobj, "asymmetry", json_object_new_double(asymmetry));
|
|
json_object_object_add(jobj, "vorticity", json_object_new_double(vorticity));
|
|
json_object_object_add(jobj, "power_w", json_object_new_double(power_w));
|
|
json_object_object_add(jobj, "cycle", json_object_new_int(cycle));
|
|
|
|
const char *json_str = json_object_to_json_string(jobj);
|
|
|
|
// Publish
|
|
zmq_send(publisher, json_str, strlen(json_str), 0);
|
|
|
|
json_object_put(jobj);
|
|
|
|
// Print status
|
|
if (cycle % 100 == 0) {
|
|
printf("Cycle %d: Coh=%.2f H64=%.2f H32=%.2f Asym=%.2f Pow=%.1fW\n",
|
|
cycle, coherence, h64, h32, asymmetry, power_w);
|
|
}
|
|
|
|
cycle++;
|
|
usleep(1000000 / PUBLISH_HZ); // 100Hz = 10ms
|
|
}
|
|
|
|
// Cleanup
|
|
nvmlShutdown();
|
|
free(h_ux); free(h_uy); free(h_rho);
|
|
cudaFree(d_f); cudaFree(d_rho); cudaFree(d_ux); cudaFree(d_uy);
|
|
zmq_close(publisher);
|
|
zmq_ctx_destroy(context);
|
|
|
|
return 0;
|
|
}
|