Files
2026-07-16 11:57:36 +07:00

1285 lines
53 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// khra_gixx_1024_v5.cu
// v5: Golden-Weave integration — inject_density + stress field snapshots
// NEW: inject_density command — spatially-targeted Gaussian perturbation on f_i
// NEW: stress_snapshot_now command — publishes per-cell stress tensor on port 5560
// v4 base: Enhanced telemetry + Vision snapshots + Resilient comms + Time series logging
// — Velocity field, stress tensor, vorticity in telemetry (read-only on f)
// — Density snapshot export on ZMQ port 5558 (raw float32)
// — Command acknowledgment on ZMQ port 5559
// — Ring buffer telemetry.jsonl (100MB max)
// — ZMQ reconnect on consecutive send failures
// v3 base: save_state/load_state + periodic autosave
// Bidirectional daemon: PUB telemetry + SUB commands
// NVML hardware metadata in every frame
// Dynamic omega, khra_amp, gixx_amp via command channel
// Checkpoint format: 64-byte header (KHRG magic + metadata + CRC32) + raw f_data
// *** Physics kernels, wave function, clamping, omega, checkpoint format: UNCHANGED from v3 ***
#include <cuda_runtime.h>
#include <nvml.h>
#include <zmq.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <time.h>
#include <stdint.h>
#include <signal.h>
#include <execinfo.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#define NX 1024
#define NY 1024
#define Q 9
#define CUDA_CHECK(call) do { \
cudaError_t err = call; \
if (err != cudaSuccess) { \
fprintf(stderr, "CUDA error at %s:%d — %s\n", __FILE__, __LINE__, cudaGetErrorString(err)); \
fflush(stderr); \
exit(1); \
} \
} while(0)
__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-side parameters for wave function — updated via command channel
__device__ float d_khra_amp = 0.03f;
__device__ float d_gixx_amp = 0.008f;
__device__ float khra_gixx_wave_1024(int x, int y, int cycle) {
float khra = sinf(2.0f * M_PI * x / 128.0f + cycle * 0.025f) *
cosf(2.0f * M_PI * y / 128.0f + cycle * 0.015f) * d_khra_amp;
float gixx = sinf(2.0f * M_PI * x / 8.0f + cycle * 0.4f) *
cosf(2.0f * M_PI * y / 8.0f + cycle * 0.35f) * d_gixx_amp;
float asymmetry_factor = 1.0f + sinf(cycle * 0.05f) * 0.5f;
return khra + gixx * asymmetry_factor;
}
__global__ void streaming_kernel(float* f_in, float* f_out, int nx, int ny) {
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) * Q;
for (int i = 0; i < Q; i++) {
int x_src = x - d_cx[i];
int y_src = y - d_cy[i];
if (x_src < 0) x_src = nx - 1;
if (x_src >= nx) x_src = 0;
if (y_src < 0) y_src = ny - 1;
if (y_src >= ny) y_src = 0;
int src_idx = (y_src * nx + x_src) * Q;
f_out[idx + i] = f_in[src_idx + i];
}
}
__global__ void collide_kernel_khragixx(float* f, 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) * Q;
float rho = 0.0f, ux = 0.0f, uy = 0.0f;
for (int i = 0; i < Q; i++) {
rho += f[idx + i];
ux += f[idx + i] * d_cx[i];
uy += f[idx + i] * d_cy[i];
}
if (rho < 0.1f) rho = 0.1f;
if (rho > 10.0f) rho = 10.0f;
ux /= rho;
uy /= rho;
float u_mag = sqrtf(ux*ux + uy*uy);
if (u_mag > 0.25f) {
ux = ux * 0.25f / u_mag;
uy = uy * 0.25f / u_mag;
}
float kx = khra_gixx_wave_1024(x, y, cycle);
float ky = kx * 0.5f;
ux += kx;
uy += ky;
u_mag = sqrtf(ux*ux + uy*uy);
if (u_mag > 0.25f) {
ux = ux * 0.25f / u_mag;
uy = uy * 0.25f / u_mag;
}
float u_sq = ux*ux + uy*uy;
for (int i = 0; i < Q; i++) {
float eu = d_cx[i]*ux + d_cy[i]*uy;
float feq = d_w[i] * rho * (1.0f + 3.0f*eu + 4.5f*eu*eu - 1.5f*u_sq);
f[idx + i] = f[idx + i] - omega * (f[idx + i] - feq);
if (isnan(f[idx + i]) || isinf(f[idx + i])) f[idx + i] = d_w[i];
}
}
__global__ void compute_rho(float* f, float* rho_out) {
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;
int f_idx = idx * Q;
float rho = 0.0f;
for (int i = 0; i < Q; i++) rho += f[f_idx + i];
if (rho < 0.1f) rho = 0.1f;
rho_out[idx] = rho;
}
// === v4: COMBINED VELOCITY + STRESS KERNEL (read-only on f) ===
// Extracts macroscopic velocity (ux, uy) and non-equilibrium stress tensor
// sigma_ab = sum_i (f_i - f_i_eq) * c_ai * c_bi
// Self-contained: computes its own rho/ux/uy from f, no dependency on compute_rho
__global__ void compute_velocity_stress_v4(float* f,
float* ux_out, float* uy_out,
float* sxx_out, float* syy_out, float* sxy_out) {
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;
int f_idx = idx * Q;
float rho = 0.0f, ux = 0.0f, uy = 0.0f;
for (int i = 0; i < Q; i++) {
rho += f[f_idx + i];
ux += f[f_idx + i] * d_cx[i];
uy += f[f_idx + i] * d_cy[i];
}
if (rho < 0.1f) rho = 0.1f;
ux /= rho;
uy /= rho;
ux_out[idx] = ux;
uy_out[idx] = uy;
float u_sq = ux * ux + uy * uy;
float stress_xx = 0.0f, stress_yy = 0.0f, stress_xy = 0.0f;
for (int i = 0; i < Q; i++) {
float eu = d_cx[i] * ux + d_cy[i] * uy;
float feq = d_w[i] * rho * (1.0f + 3.0f * eu + 4.5f * eu * eu - 1.5f * u_sq);
float fneq = f[f_idx + i] - feq;
stress_xx += fneq * d_cx[i] * d_cx[i];
stress_yy += fneq * d_cy[i] * d_cy[i];
stress_xy += fneq * d_cx[i] * d_cy[i];
}
sxx_out[idx] = stress_xx;
syy_out[idx] = stress_yy;
sxy_out[idx] = stress_xy;
}
// === v5: INJECT DENSITY KERNEL ===
// Applies a Gaussian density perturbation centred at (cx, cy) with width sigma.
// delta_rho(x,y) = strength * exp(-r²/(2σ²)), periodic distance.
// Each f_i += w_i * delta_rho — this adds density while preserving zero net momentum.
__global__ void inject_density_kernel(float* f,
float cx, float cy,
float sigma, float strength) {
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
if (x >= NX || y >= NY) return;
// Periodic distance
float dx = (float)x - cx;
float dy = (float)y - cy;
if (dx > NX * 0.5f) dx -= NX;
if (dx < -NX * 0.5f) dx += NX;
if (dy > NY * 0.5f) dy -= NY;
if (dy < -NY * 0.5f) dy += NY;
float r2 = dx * dx + dy * dy;
float inv_2sig2 = 1.0f / (2.0f * sigma * sigma);
float delta_rho = strength * expf(-r2 * inv_2sig2);
int f_idx = (y * NX + x) * Q;
for (int i = 0; i < Q; i++) {
f[f_idx + i] += d_w[i] * delta_rho;
}
}
float calculate_asymmetry_magnifying(float* h_rho) {
float sum_sq = 0.0f;
for (int i = 0; i < NX * NY; i++) {
float dev = h_rho[i] - 1.0f;
sum_sq += dev * dev;
}
return (sum_sq / (NX * NY)) * 100.0f;
}
// === v4: HOST-SIDE MEAN ABSOLUTE VORTICITY ===
// omega = duy/dx - dux/dy via central finite differences, periodic BC
static float compute_mean_vorticity(float* h_ux, float* h_uy) {
float sum_vort = 0.0f;
for (int y = 0; y < NY; y++) {
for (int x = 0; x < NX; x++) {
int xp = (x + 1) % NX;
int xm = (x - 1 + NX) % NX;
int yp = (y + 1) % NY;
int ym = (y - 1 + NY) % NY;
float duy_dx = (h_uy[y * NX + xp] - h_uy[y * NX + xm]) * 0.5f;
float dux_dy = (h_ux[yp * NX + x] - h_ux[ym * NX + x]) * 0.5f;
sum_vort += fabsf(duy_dx - dux_dy);
}
}
return sum_vort / (float)(NX * NY);
}
// Host-side omega and wave amplitudes — modifiable via command channel
static float h_omega = 1.97f;
static float h_khra_amp = 0.03f;
static float h_gixx_amp = 0.008f;
// === CRC32 (zlib-compatible, for checkpoint integrity) ===
static uint32_t crc32_table[256];
static int crc32_table_ready = 0;
static void crc32_init(void) {
for (uint32_t i = 0; i < 256; i++) {
uint32_t c = i;
for (int j = 0; j < 8; j++)
c = (c >> 1) ^ ((c & 1) ? 0xEDB88320u : 0);
crc32_table[i] = c;
}
crc32_table_ready = 1;
}
static uint32_t crc32_compute(const void* data, size_t len) {
if (!crc32_table_ready) crc32_init();
const unsigned char* p = (const unsigned char*)data;
uint32_t crc = 0xFFFFFFFF;
for (size_t i = 0; i < len; i++)
crc = crc32_table[(crc ^ p[i]) & 0xFF] ^ (crc >> 8);
return crc ^ 0xFFFFFFFF;
}
// === CHECKPOINT SAVE ===
// Format: 64-byte header + NX*NY*Q float32
// Header: "KHRG" | version(u32) | cycle(u32) | NX(u32) | NY(u32) | Q(u32) |
// omega(f32) | khra_amp(f32) | gixx_amp(f32) | crc32(u32) | reserved
static int save_checkpoint(float* h_f, float* d_f_active, int cycle, const char* dir) {
size_t f_size = (size_t)NX * NY * Q * sizeof(float);
cudaError_t err = cudaMemcpy(h_f, d_f_active, f_size, cudaMemcpyDeviceToHost);
if (err != cudaSuccess) {
fprintf(stderr, "[SAVE] cudaMemcpy failed: %s\n", cudaGetErrorString(err));
fflush(stderr);
return -1;
}
char path[512], tmp_path[520];
time_t now = time(NULL);
struct tm* t = localtime(&now);
snprintf(path, sizeof(path), "%s/ckpt_%04d%02d%02d_%02d%02d%02d_c%d.bin",
dir, t->tm_year+1900, t->tm_mon+1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec, cycle);
snprintf(tmp_path, sizeof(tmp_path), "%s.tmp", path);
FILE* fp = fopen(tmp_path, "wb");
if (!fp) {
fprintf(stderr, "[SAVE] fopen failed: %s\n", tmp_path);
fflush(stderr);
return -2;
}
// Build 64-byte header
unsigned char header[64];
memset(header, 0, 64);
memcpy(header, "KHRG", 4);
uint32_t v;
v = 1; memcpy(header+4, &v, 4); // version
v = (uint32_t)cycle; memcpy(header+8, &v, 4); // cycle
v = NX; memcpy(header+12, &v, 4);
v = NY; memcpy(header+16, &v, 4);
v = Q; memcpy(header+20, &v, 4);
memcpy(header+24, &h_omega, 4);
memcpy(header+28, &h_khra_amp, 4);
memcpy(header+32, &h_gixx_amp, 4);
uint32_t crc = crc32_compute(h_f, f_size);
memcpy(header+36, &crc, 4);
size_t w1 = fwrite(header, 1, 64, fp);
size_t w2 = fwrite(h_f, 1, f_size, fp);
fclose(fp);
if (w1 != 64 || w2 != f_size) {
fprintf(stderr, "[SAVE] write incomplete: header=%zu data=%zu\n", w1, w2);
fflush(stderr);
unlink(tmp_path);
return -3;
}
if (rename(tmp_path, path) != 0) {
fprintf(stderr, "[SAVE] rename failed\n");
fflush(stderr);
return -4;
}
printf("[SAVE] %s (cycle %d, CRC32=0x%08X, %.1f MB)\n",
path, cycle, crc, (64.0 + f_size) / (1024.0*1024.0));
fflush(stdout);
return 0;
}
// === CHECKPOINT LOAD ===
// Returns loaded cycle number, or -1 on error
// Supports: v3 format (64-byte header + f_data) and raw format (exactly f_size bytes, no header)
static int load_checkpoint(const char* path, float* h_f, float* d_f_target) {
size_t f_size = (size_t)NX * NY * Q * sizeof(float);
FILE* fp = fopen(path, "rb");
if (!fp) {
fprintf(stderr, "[LOAD] Cannot open: %s\n", path);
fflush(stderr);
return -1;
}
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
fseek(fp, 0, SEEK_SET);
int loaded_cycle = 0;
if (file_size == (long)(64 + f_size)) {
// v3 format with header
unsigned char header[64];
if (fread(header, 1, 64, fp) != 64) { fclose(fp); return -1; }
if (memcmp(header, "KHRG", 4) != 0) {
fprintf(stderr, "[LOAD] Bad magic in %s\n", path);
fclose(fp);
return -1;
}
uint32_t v;
memcpy(&v, header+8, 4); loaded_cycle = (int)v;
memcpy(&v, header+12, 4); if (v != NX) { fprintf(stderr, "[LOAD] NX mismatch\n"); fclose(fp); return -1; }
memcpy(&v, header+16, 4); if (v != NY) { fprintf(stderr, "[LOAD] NY mismatch\n"); fclose(fp); return -1; }
memcpy(&v, header+20, 4); if (v != Q) { fprintf(stderr, "[LOAD] Q mismatch\n"); fclose(fp); return -1; }
// Restore parameters
memcpy(&h_omega, header+24, 4);
memcpy(&h_khra_amp, header+28, 4);
memcpy(&h_gixx_amp, header+32, 4);
cudaMemcpyToSymbol(d_khra_amp, &h_khra_amp, sizeof(float));
cudaMemcpyToSymbol(d_gixx_amp, &h_gixx_amp, sizeof(float));
if (fread(h_f, 1, f_size, fp) != f_size) { fclose(fp); return -1; }
// Verify CRC
uint32_t stored_crc;
memcpy(&stored_crc, header+36, 4);
uint32_t actual_crc = crc32_compute(h_f, f_size);
if (stored_crc != actual_crc) {
fprintf(stderr, "[LOAD] CRC mismatch! stored=0x%08X actual=0x%08X\n", stored_crc, actual_crc);
fflush(stderr);
fclose(fp);
return -1;
}
printf("[LOAD] v3 checkpoint: cycle=%d omega=%.3f khra=%.4f gixx=%.4f CRC OK\n",
loaded_cycle, h_omega, h_khra_amp, h_gixx_amp);
} else if (file_size == (long)f_size) {
// Raw format (emergency extract or legacy) — no header
if (fread(h_f, 1, f_size, fp) != f_size) { fclose(fp); return -1; }
printf("[LOAD] Raw checkpoint (no header): %s (%ld bytes)\n", path, file_size);
} else {
fprintf(stderr, "[LOAD] Unknown format: %s (%ld bytes, expected %zu or %zu)\n",
path, file_size, 64 + f_size, f_size);
fclose(fp);
return -1;
}
fclose(fp);
cudaError_t err = cudaMemcpy(d_f_target, h_f, f_size, cudaMemcpyHostToDevice);
if (err != cudaSuccess) {
fprintf(stderr, "[LOAD] cudaMemcpy failed: %s\n", cudaGetErrorString(err));
fflush(stderr);
return -1;
}
printf("[LOAD] State loaded from %s\n", path);
fflush(stdout);
return loaded_cycle;
}
// === v4: TELEMETRY RING BUFFER ===
#define TELEMETRY_MAX_BYTES (100L * 1024L * 1024L)
#define TELEMETRY_TRIM_BYTES (50L * 1024L * 1024L)
static FILE* telemetry_fp = NULL;
static long telemetry_file_size = 0;
static void telemetry_ring_init(void) {
telemetry_fp = fopen("telemetry.jsonl", "a");
if (telemetry_fp) {
fseek(telemetry_fp, 0, SEEK_END);
telemetry_file_size = ftell(telemetry_fp);
printf("[v4] Telemetry ring buffer: telemetry.jsonl (%ld bytes existing)\n", telemetry_file_size);
fflush(stdout);
} else {
fprintf(stderr, "[v4] WARNING: Cannot open telemetry.jsonl\n");
fflush(stderr);
}
}
static void telemetry_ring_write(const char* line) {
if (!telemetry_fp) return;
int len = fprintf(telemetry_fp, "%s\n", line);
if (len > 0) {
telemetry_file_size += len;
fflush(telemetry_fp);
}
if (telemetry_file_size > TELEMETRY_MAX_BYTES) {
fclose(telemetry_fp);
FILE* rf = fopen("telemetry.jsonl", "rb");
if (!rf) { telemetry_fp = NULL; return; }
long seek_pos = telemetry_file_size - TELEMETRY_TRIM_BYTES;
fseek(rf, seek_pos, SEEK_SET);
// Skip partial line
int c;
while ((c = fgetc(rf)) != EOF && c != '\n');
long tail_start = ftell(rf);
long tail_size = telemetry_file_size - tail_start;
char* buf = (char*)malloc(tail_size);
if (!buf) { fclose(rf); telemetry_fp = fopen("telemetry.jsonl", "a"); return; }
size_t got = fread(buf, 1, tail_size, rf);
fclose(rf);
telemetry_fp = fopen("telemetry.jsonl", "w");
if (telemetry_fp) {
fwrite(buf, 1, got, telemetry_fp);
fflush(telemetry_fp);
long old_size = telemetry_file_size;
telemetry_file_size = (long)got;
printf("[v4] Telemetry ring trimmed: %ld → %ld bytes\n", old_size, telemetry_file_size);
fflush(stdout);
}
free(buf);
}
}
static void telemetry_ring_close(void) {
if (telemetry_fp) { fclose(telemetry_fp); telemetry_fp = NULL; }
}
// === v4: EXPORT TIMESERIES ===
static int export_timeseries(const char* out_path, int last_n) {
FILE* rf = fopen("telemetry.jsonl", "r");
if (!rf) { fprintf(stderr, "[EXPORT] Cannot open telemetry.jsonl\n"); fflush(stderr); return -1; }
int total_lines = 0;
char line_buf[2048];
while (fgets(line_buf, sizeof(line_buf), rf)) total_lines++;
rewind(rf);
int skip = total_lines - last_n;
if (skip < 0) skip = 0;
for (int i = 0; i < skip; i++) {
if (!fgets(line_buf, sizeof(line_buf), rf)) break;
}
FILE* wf = fopen(out_path, "w");
if (!wf) { fclose(rf); fprintf(stderr, "[EXPORT] Cannot write %s\n", out_path); fflush(stderr); return -2; }
int written = 0;
while (fgets(line_buf, sizeof(line_buf), rf)) {
fputs(line_buf, wf);
written++;
}
fclose(wf);
fclose(rf);
printf("[EXPORT] %d entries → %s\n", written, out_path);
fflush(stdout);
return 0;
}
// === v4: RESILIENT ZMQ SEND ===
#define ZMQ_MAX_SEND_FAILS 3
static int zmq_send_resilient(void** sock, void* ctx, const char* endpoint,
int sock_type, int hwm,
const void* data, size_t len, int flags,
int* fail_count) {
int rc = zmq_send(*sock, data, len, flags);
if (rc < 0) {
(*fail_count)++;
fprintf(stderr, "[ZMQ] Send fail #%d on %s: %s\n",
*fail_count, endpoint, zmq_strerror(zmq_errno()));
fflush(stderr);
if (*fail_count >= ZMQ_MAX_SEND_FAILS) {
printf("[ZMQ] Reconnecting PUB %s after %d failures\n", endpoint, *fail_count);
fflush(stdout);
zmq_close(*sock);
*sock = zmq_socket(ctx, sock_type);
if (*sock) {
int linger = 0;
zmq_setsockopt(*sock, ZMQ_SNDHWM, &hwm, sizeof(hwm));
zmq_setsockopt(*sock, ZMQ_LINGER, &linger, sizeof(linger));
int bind_rc = zmq_bind(*sock, endpoint);
if (bind_rc == 0) {
printf("[ZMQ] Reconnected %s\n", endpoint);
} else {
fprintf(stderr, "[ZMQ] Reconnect bind failed on %s: %s\n",
endpoint, zmq_strerror(zmq_errno()));
}
fflush(stdout);
fflush(stderr);
}
*fail_count = 0;
}
} else {
*fail_count = 0;
}
return rc;
}
// === JSON FIELD HELPERS ===
// Find the start of a JSON string value for a given key, handling both
// compact ("key":"val") and spaced ("key": "val") formats.
// Returns pointer to first char of value (after opening quote), or NULL.
static const char* json_find_str(const char* msg, const char* key) {
// Build pattern: "key":"
char pat[128];
snprintf(pat, sizeof(pat), "\"%s\":\"", key);
const char* p = strstr(msg, pat);
if (p) return p + strlen(pat);
// Try spaced: "key": "
snprintf(pat, sizeof(pat), "\"%s\": \"", key);
p = strstr(msg, pat);
if (p) return p + strlen(pat);
return NULL;
}
// Extract a JSON string value into buf, returns length or -1.
static int json_extract_str(const char* msg, const char* key, char* buf, int buf_size) {
const char* start = json_find_str(msg, key);
if (!start) return -1;
const char* end = strchr(start, '"');
if (!end || (end - start) >= buf_size) return -1;
memcpy(buf, start, end - start);
buf[end - start] = '\0';
return (int)(end - start);
}
// === v4: SNAPSHOT + COMMAND STATE ===
static int snapshot_interval = 10;
static int snapshot_now_flag = 0;
static int export_last_n_request = 10000;
static char last_cmd_name[64] = "";
// === v5: INJECT DENSITY + STRESS SNAPSHOT STATE ===
static float inject_cx = 0.0f, inject_cy = 0.0f;
static float inject_sigma = 16.0f, inject_strength = 0.1f;
static int stress_snapshot_now_flag = 0;
static unsigned long long total_injections = 0;
static int health_check_pending = 0;
// === COMMAND HANDLER ===
// v3 commands: set_omega, set_khra_amp, set_gixx_amp, reset_equilibrium,
// save_state, load_state, set_autosave
// v4 commands: set_snapshot_interval, snapshot_now, export_timeseries
// v5 commands: inject_density, stress_snapshot_now
// Returns: 1=save, 2=load, 3=autosave, 4=snapshot_now, 5=export_timeseries,
// 6=inject_density, 7=stress_snapshot_now, 0=handled/noop
static int handle_command(const char* msg, float* h_f, float* d_f_current,
char* out_path, int out_path_size) {
const char* cmd_start = strstr(msg, "\"cmd\":\"");
if (!cmd_start) {
cmd_start = strstr(msg, "\"cmd\": \"");
if (!cmd_start) return 0;
cmd_start += 8;
} else {
cmd_start += 7;
}
if (strncmp(cmd_start, "set_omega", 9) == 0) {
strncpy(last_cmd_name, "set_omega", sizeof(last_cmd_name) - 1);
const char* val = strstr(msg, "\"value\":");
if (val) {
float v = strtof(val + 8, NULL);
if (v >= 0.5f && v <= 1.99f) {
h_omega = v;
printf("[CMD] omega → %.3f\n", h_omega);
fflush(stdout);
}
}
} else if (strncmp(cmd_start, "set_khra_amp", 12) == 0) {
strncpy(last_cmd_name, "set_khra_amp", sizeof(last_cmd_name) - 1);
const char* val = strstr(msg, "\"value\":");
if (val) {
float v = strtof(val + 8, NULL);
if (v >= 0.0f && v <= 0.2f) {
h_khra_amp = v;
cudaMemcpyToSymbol(d_khra_amp, &h_khra_amp, sizeof(float));
printf("[CMD] khra_amp → %.4f\n", h_khra_amp);
fflush(stdout);
}
}
} else if (strncmp(cmd_start, "set_gixx_amp", 12) == 0) {
strncpy(last_cmd_name, "set_gixx_amp", sizeof(last_cmd_name) - 1);
const char* val = strstr(msg, "\"value\":");
if (val) {
float v = strtof(val + 8, NULL);
if (v >= 0.0f && v <= 0.1f) {
h_gixx_amp = v;
cudaMemcpyToSymbol(d_gixx_amp, &h_gixx_amp, sizeof(float));
printf("[CMD] gixx_amp → %.4f\n", h_gixx_amp);
fflush(stdout);
}
}
} else if (strncmp(cmd_start, "reset_equilibrium", 17) == 0) {
strncpy(last_cmd_name, "reset_equilibrium", sizeof(last_cmd_name) - 1);
const float h_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};
size_t f_size = NX * NY * Q * sizeof(float);
for (int y = 0; y < NY; y++)
for (int x = 0; x < NX; x++)
for (int i = 0; i < Q; i++)
h_f[(y*NX+x)*Q + i] = h_w[i];
cudaMemcpy(d_f_current, h_f, f_size, cudaMemcpyHostToDevice);
printf("[CMD] Grid reset to equilibrium\n");
fflush(stdout);
} else if (strncmp(cmd_start, "save_state", 10) == 0) {
strncpy(last_cmd_name, "save_state", sizeof(last_cmd_name) - 1);
if (json_extract_str(msg, "path", out_path, out_path_size) < 0)
strncpy(out_path, ".", out_path_size - 1);
return 1;
} else if (strncmp(cmd_start, "load_state", 10) == 0) {
strncpy(last_cmd_name, "load_state", sizeof(last_cmd_name) - 1);
if (json_extract_str(msg, "path", out_path, out_path_size) >= 0)
return 2;
fprintf(stderr, "[CMD] load_state requires \"path\" field\n");
fflush(stderr);
} else if (strncmp(cmd_start, "set_autosave", 12) == 0) {
strncpy(last_cmd_name, "set_autosave", sizeof(last_cmd_name) - 1);
const char* val = strstr(msg, "\"interval\":");
if (val) {
int v = atoi(val + 11);
if (v >= 0 && v <= 10000000) {
snprintf(out_path, out_path_size, "%d", v);
return 3;
}
}
// === v4 commands ===
} else if (strncmp(cmd_start, "set_snapshot_interval", 21) == 0) {
strncpy(last_cmd_name, "set_snapshot_interval", sizeof(last_cmd_name) - 1);
const char* val = strstr(msg, "\"interval\":");
if (val) {
int v = atoi(val + 11);
if (v >= 0 && v <= 10000000) {
snapshot_interval = v;
printf("[CMD] Snapshot interval → %d cycles%s\n",
snapshot_interval, snapshot_interval == 0 ? " (disabled)" : "");
fflush(stdout);
}
}
} else if (strncmp(cmd_start, "snapshot_now", 12) == 0) {
strncpy(last_cmd_name, "snapshot_now", sizeof(last_cmd_name) - 1);
return 4;
} else if (strncmp(cmd_start, "export_timeseries", 17) == 0) {
strncpy(last_cmd_name, "export_timeseries", sizeof(last_cmd_name) - 1);
json_extract_str(msg, "path", out_path, out_path_size);
const char* n = strstr(msg, "\"last_n\":");
if (n) {
int v = atoi(n + 9);
if (v > 0) export_last_n_request = v;
} else {
export_last_n_request = 10000;
}
if (out_path[0] != '\0') return 5;
fprintf(stderr, "[CMD] export_timeseries requires \"path\" field\n");
fflush(stderr);
// === v5 commands ===
} else if (strncmp(cmd_start, "inject_density", 14) == 0) {
strncpy(last_cmd_name, "inject_density", sizeof(last_cmd_name) - 1);
// Required: x, y. Optional: sigma (default 16), strength (default 0.1)
const char* vx = strstr(msg, "\"x\":");
const char* vy = strstr(msg, "\"y\":");
if (vx && vy) {
inject_cx = strtof(vx + 4, NULL);
inject_cy = strtof(vy + 4, NULL);
// Clamp to grid
if (inject_cx < 0.0f) inject_cx = 0.0f;
if (inject_cx >= (float)NX) inject_cx = (float)(NX - 1);
if (inject_cy < 0.0f) inject_cy = 0.0f;
if (inject_cy >= (float)NY) inject_cy = (float)(NY - 1);
// Optional sigma
const char* vs = strstr(msg, "\"sigma\":");
if (vs) {
float s = strtof(vs + 8, NULL);
if (s >= 1.0f && s <= 256.0f) inject_sigma = s;
}
// Optional strength
const char* vst = strstr(msg, "\"strength\":");
if (vst) {
float st = strtof(vst + 11, NULL);
if (st >= -1.0f && st <= 1.0f) inject_strength = st;
}
printf("[CMD] inject_density at (%.1f, %.1f) sigma=%.1f strength=%.4f\n",
inject_cx, inject_cy, inject_sigma, inject_strength);
fflush(stdout);
return 6;
} else {
fprintf(stderr, "[CMD] inject_density requires \"x\" and \"y\" fields\n");
fflush(stderr);
}
} else if (strncmp(cmd_start, "stress_snapshot_now", 19) == 0) {
strncpy(last_cmd_name, "stress_snapshot_now", sizeof(last_cmd_name) - 1);
return 7;
} else if (strncmp(cmd_start, "health_check", 12) == 0) {
strncpy(last_cmd_name, "health_check", sizeof(last_cmd_name) - 1);
return 8;
}
return 0;
}
static volatile int g_last_cycle = -1;
static volatile int g_last_cmd_len = -1;
static volatile int g_in_handle_cmd = 0;
static volatile int running = 1;
static time_t start_time;
static void crash_handler(int sig) {
const char* name = (sig == SIGSEGV) ? "SIGSEGV" : (sig == SIGABRT) ? "SIGABRT" : "SIGNAL";
fprintf(stderr, "\n[CRASH] %s at cycle %d, last_cmd_len=%d, in_handle_cmd=%d\n",
name, g_last_cycle, g_last_cmd_len, g_in_handle_cmd);
fflush(stderr);
void* bt[20];
int n = backtrace(bt, 20);
backtrace_symbols_fd(bt, n, STDERR_FILENO);
_exit(128 + sig);
}
static void shutdown_handler(int sig) {
printf("\n[SHUTDOWN] Received signal %d, initiating graceful shutdown...\n", sig);
fflush(stdout);
running = 0;
}
int main() {
signal(SIGSEGV, crash_handler);
signal(SIGABRT, crash_handler);
signal(SIGBUS, crash_handler);
signal(SIGTERM, shutdown_handler);
signal(SIGINT, shutdown_handler);
start_time = time(NULL);
printf("Khra'gixx 1024 v5 — GOLDEN-WEAVE INTEGRATION\n"); fflush(stdout);
printf("PUB telemetry on 5556 | SUB commands on 5557\n"); fflush(stdout);
printf("PUB snapshots on 5558 | PUB ack on 5559 | PUB stress on 5560\n"); fflush(stdout);
printf("Khra: %.3f | gixx: %.3f | omega: %.2f\n", h_khra_amp, h_gixx_amp, h_omega); fflush(stdout);
printf("Telemetry: coherence, asymmetry, velocity, stress, vorticity, NVML\n"); fflush(stdout);
printf("Checkpoint format: KHRG v1 (compatible with v3)\n"); fflush(stdout);
printf("Commands: save_state, load_state, set_autosave, set_omega, set_khra_amp,\n");
printf(" set_gixx_amp, reset_equilibrium, set_snapshot_interval,\n");
printf(" snapshot_now, export_timeseries, inject_density,\n");
printf(" stress_snapshot_now, health_check\n\n"); fflush(stdout);
// Init CRC32 table
crc32_init();
// v4: Init telemetry ring buffer
telemetry_ring_init();
// === NVML INIT ===
nvmlReturn_t nvml_rc = nvmlInit();
nvmlDevice_t nvml_device;
int nvml_ok = 0;
if (nvml_rc == NVML_SUCCESS) {
nvml_rc = nvmlDeviceGetHandleByIndex(0, &nvml_device);
if (nvml_rc == NVML_SUCCESS) {
nvml_ok = 1;
printf("NVML initialized — GPU hardware telemetry active\n"); fflush(stdout);
} else {
printf("NVML: got init but no device handle: %s\n", nvmlErrorString(nvml_rc)); fflush(stdout);
}
} else {
printf("NVML init failed: %s — running without hardware telemetry\n", nvmlErrorString(nvml_rc)); fflush(stdout);
}
// === GPU MEMORY ===
size_t f_size = NX * NY * Q * sizeof(float);
size_t scalar_size = NX * NY * sizeof(float);
printf("Allocating GPU memory: f_size=%zu MB, scalar_size=%zu MB\n", f_size/(1024*1024), scalar_size/(1024*1024)); fflush(stdout);
float *d_f[2], *d_rho;
CUDA_CHECK(cudaMalloc(&d_f[0], f_size));
CUDA_CHECK(cudaMalloc(&d_f[1], f_size));
CUDA_CHECK(cudaMalloc(&d_rho, scalar_size));
// v4: velocity + stress GPU buffers (5 scalar fields = 20MB)
float *d_ux, *d_uy, *d_sxx, *d_syy, *d_sxy;
CUDA_CHECK(cudaMalloc(&d_ux, scalar_size));
CUDA_CHECK(cudaMalloc(&d_uy, scalar_size));
CUDA_CHECK(cudaMalloc(&d_sxx, scalar_size));
CUDA_CHECK(cudaMalloc(&d_syy, scalar_size));
CUDA_CHECK(cudaMalloc(&d_sxy, scalar_size));
printf("GPU allocation successful (v4: +20MB for velocity/stress fields)\n"); fflush(stdout);
// Initialize equilibrium
printf("Initializing grid...\n"); fflush(stdout);
const float h_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(f_size);
if (!h_f) { fprintf(stderr, "malloc h_f failed\n"); return 1; }
for (int y = 0; y < NY; y++)
for (int x = 0; x < NX; x++)
for (int i = 0; i < Q; i++)
h_f[(y*NX+x)*Q + i] = h_w[i];
CUDA_CHECK(cudaMemcpy(d_f[0], h_f, f_size, cudaMemcpyHostToDevice));
printf("Grid initialized (rho=1.0 equilibrium)\n"); fflush(stdout);
// Copy initial wave amplitudes to device
cudaMemcpyToSymbol(d_khra_amp, &h_khra_amp, sizeof(float));
cudaMemcpyToSymbol(d_gixx_amp, &h_gixx_amp, sizeof(float));
// === ZMQ: PUB telemetry on 5556 ===
printf("Initializing ZMQ...\n"); fflush(stdout);
void* zmq_ctx = zmq_ctx_new();
if (!zmq_ctx) { fprintf(stderr, "zmq_ctx_new failed\n"); return 1; }
void* publisher = zmq_socket(zmq_ctx, ZMQ_PUB);
if (!publisher) { fprintf(stderr, "zmq_socket PUB failed\n"); return 1; }
int sndhwm = 1, linger = 0;
zmq_setsockopt(publisher, ZMQ_SNDHWM, &sndhwm, sizeof(sndhwm));
zmq_setsockopt(publisher, ZMQ_LINGER, &linger, sizeof(linger));
int rc = zmq_bind(publisher, "tcp://127.0.0.1:5556");
if (rc != 0) {
fprintf(stderr, "FATAL: zmq_bind PUB failed: %s (port 5556 in use — kill stale daemon first)\n", zmq_strerror(zmq_errno()));
fflush(stderr);
return 1;
}
printf("ZMQ PUB bound to port 5556\n"); fflush(stdout);
// === ZMQ: SUB commands on 5557 (receives from PUB sockets) ===
void* subscriber = zmq_socket(zmq_ctx, ZMQ_SUB);
if (!subscriber) { fprintf(stderr, "zmq_socket SUB failed\n"); return 1; }
int rcvhwm = 10;
zmq_setsockopt(subscriber, ZMQ_RCVHWM, &rcvhwm, sizeof(rcvhwm));
zmq_setsockopt(subscriber, ZMQ_LINGER, &linger, sizeof(linger));
zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, "", 0); // subscribe to all
rc = zmq_bind(subscriber, "tcp://127.0.0.1:5557");
if (rc != 0) {
fprintf(stderr, "FATAL: zmq_bind SUB failed: %s (port 5557 in use — kill stale daemon first)\n", zmq_strerror(zmq_errno()));
fflush(stderr);
return 1;
}
printf("ZMQ SUB bound to port 5557 (command channel)\n"); fflush(stdout);
// === v4: ZMQ PUB snapshots on 5558 ===
void* snapshot_pub = zmq_socket(zmq_ctx, ZMQ_PUB);
if (!snapshot_pub) { fprintf(stderr, "zmq_socket snapshot PUB failed\n"); return 1; }
int snap_hwm = 1; // only buffer latest frame
zmq_setsockopt(snapshot_pub, ZMQ_SNDHWM, &snap_hwm, sizeof(snap_hwm));
zmq_setsockopt(snapshot_pub, ZMQ_LINGER, &linger, sizeof(linger));
rc = zmq_bind(snapshot_pub, "tcp://127.0.0.1:5558");
if (rc != 0) {
fprintf(stderr, "FATAL: zmq_bind snapshot PUB failed: %s (port 5558 in use — kill stale daemon first)\n", zmq_strerror(zmq_errno()));
fflush(stderr);
return 1;
}
printf("ZMQ PUB bound to port 5558 (vision snapshots, raw float32)\n"); fflush(stdout);
// === v4: ZMQ PUB acknowledgments on 5559 ===
void* ack_pub = zmq_socket(zmq_ctx, ZMQ_PUB);
if (!ack_pub) { fprintf(stderr, "zmq_socket ACK PUB failed\n"); return 1; }
zmq_setsockopt(ack_pub, ZMQ_SNDHWM, &sndhwm, sizeof(sndhwm));
zmq_setsockopt(ack_pub, ZMQ_LINGER, &linger, sizeof(linger));
rc = zmq_bind(ack_pub, "tcp://127.0.0.1:5559");
if (rc != 0) {
fprintf(stderr, "FATAL: zmq_bind ACK PUB failed: %s (port 5559 in use — kill stale daemon first)\n", zmq_strerror(zmq_errno()));
fflush(stderr);
return 1;
}
printf("ZMQ PUB bound to port 5559 (command ACK)\n"); fflush(stdout);
// === v5: ZMQ PUB stress snapshots on 5560 ===
void* stress_pub = zmq_socket(zmq_ctx, ZMQ_PUB);
if (!stress_pub) { fprintf(stderr, "zmq_socket stress PUB failed\n"); return 1; }
int stress_hwm = 1;
zmq_setsockopt(stress_pub, ZMQ_SNDHWM, &stress_hwm, sizeof(stress_hwm));
zmq_setsockopt(stress_pub, ZMQ_LINGER, &linger, sizeof(linger));
rc = zmq_bind(stress_pub, "tcp://127.0.0.1:5560");
if (rc != 0) {
fprintf(stderr, "FATAL: zmq_bind stress PUB failed: %s (port 5560 in use — kill stale daemon first)\n", zmq_strerror(zmq_errno()));
fflush(stderr);
return 1;
}
printf("ZMQ PUB bound to port 5560 (stress field snapshots)\n"); fflush(stdout);
// === HOST MEMORY ===
float *h_rho = (float*)malloc(scalar_size);
if (!h_rho) { fprintf(stderr, "malloc h_rho failed\n"); return 1; }
// v4: velocity + stress host buffers
float *h_ux = (float*)malloc(scalar_size);
float *h_uy = (float*)malloc(scalar_size);
float *h_sxx = (float*)malloc(scalar_size);
float *h_syy = (float*)malloc(scalar_size);
float *h_sxy = (float*)malloc(scalar_size);
if (!h_ux || !h_uy || !h_sxx || !h_syy || !h_sxy) {
fprintf(stderr, "malloc v4 host buffers failed\n"); return 1;
}
// v4: snapshot send buffer (8-byte header + scalar_size rho data)
unsigned char* snap_buf = (unsigned char*)malloc(8 + scalar_size);
if (!snap_buf) { fprintf(stderr, "malloc snap_buf failed\n"); return 1; }
// v5: stress snapshot send buffer (8-byte header + 3 × scalar_size for sxx/syy/sxy)
size_t stress_snap_size = 8 + 3 * scalar_size;
unsigned char* stress_snap_buf = (unsigned char*)malloc(stress_snap_size);
if (!stress_snap_buf) { fprintf(stderr, "malloc stress_snap_buf failed\n"); return 1; }
dim3 block(16, 16);
dim3 grid((NX + 15) / 16, (NY + 15) / 16);
int cycle = 0, current = 0;
int autosave_interval = 100000;
int last_autosave = 0;
int pub_fail_count = 0; // v4: resilient send counter for telemetry
int snap_fail_count = 0; // v4: resilient send counter for snapshots
int stress_fail_count = 0; // v4: resilient send counter for stress
int ack_fail_count = 0; // v4: resilient send counter for acks
printf("[v5] Autosave every %d cycles | Snapshots every %d cycles\n", autosave_interval, snapshot_interval);
fflush(stdout);
printf("[Khra'gixx v5 Daemon] Starting main loop...\n"); fflush(stdout);
while (running) {
// === Check for commands (non-blocking) ===
char cmd_buf[512];
char cmd_path[512];
int cmd_len = zmq_recv(subscriber, cmd_buf, sizeof(cmd_buf) - 1, ZMQ_DONTWAIT);
g_last_cycle = cycle;
g_last_cmd_len = cmd_len;
if (cmd_len > 0) {
// Clamp to buffer size (zmq_recv returns actual msg size even if truncated)
if (cmd_len > (int)(sizeof(cmd_buf) - 1)) cmd_len = sizeof(cmd_buf) - 1;
cmd_buf[cmd_len] = '\0';
cmd_path[0] = '\0';
last_cmd_name[0] = '\0';
fprintf(stderr, "[DBG] Recv cmd (%d bytes) at cycle %d: %.80s\n", cmd_len, cycle, cmd_buf);
fflush(stderr);
g_in_handle_cmd = 1;
int cmd_result = handle_command(cmd_buf, h_f, d_f[current], cmd_path, sizeof(cmd_path));
g_in_handle_cmd = 0;
const char* ack_status = "ok";
if (cmd_result == 1) {
if (save_checkpoint(h_f, d_f[current], cycle, cmd_path) != 0)
ack_status = "error";
} else if (cmd_result == 2) {
int loaded_cycle = load_checkpoint(cmd_path, h_f, d_f[current]);
if (loaded_cycle >= 0) {
cycle = loaded_cycle;
printf("[CMD] Resumed at cycle %d\n", cycle);
fflush(stdout);
} else {
ack_status = "error";
}
} else if (cmd_result == 3) {
autosave_interval = atoi(cmd_path);
last_autosave = cycle;
printf("[CMD] Autosave interval → %d cycles%s\n",
autosave_interval, autosave_interval == 0 ? " (disabled)" : "");
fflush(stdout);
} else if (cmd_result == 4) {
snapshot_now_flag = 1;
} else if (cmd_result == 5) {
if (export_timeseries(cmd_path, export_last_n_request) != 0)
ack_status = "error";
} else if (cmd_result == 6) {
// v5: inject_density — launch kernel on current distribution
total_injections++;
inject_density_kernel<<<grid, block>>>(d_f[current],
inject_cx, inject_cy, inject_sigma, inject_strength);
CUDA_CHECK(cudaDeviceSynchronize());
printf("[v5] inject_density #%llu applied at (%.1f,%.1f) σ=%.1f str=%.4f\n",
total_injections, inject_cx, inject_cy, inject_sigma, inject_strength);
fflush(stdout);
// Emit injection metadata on ack_pub
char inj_msg[512];
snprintf(inj_msg, sizeof(inj_msg),
"{\"injection_id\":%llu,\"cycle\":%d,\"x\":%.1f,\"y\":%.1f,\"sigma\":%.1f,\"strength\":%.4f}",
total_injections, cycle, inject_cx, inject_cy, inject_sigma, inject_strength);
zmq_send_resilient(&ack_pub, zmq_ctx, "tcp://127.0.0.1:5559",
ZMQ_PUB, 1, inj_msg, strlen(inj_msg), 0, &ack_fail_count);
} else if (cmd_result == 7) {
// v5: stress snapshot — fires at next telemetry tick
stress_snapshot_now_flag = 1;
} else if (cmd_result == 8) {
// v5: health_check — set flag to respond in telemetry section with actual values
health_check_pending = 1;
}
// v4: Publish ACK on port 5559 (for commands that didn't already send a response)
if (last_cmd_name[0] != '\0' && cmd_result != 6 && cmd_result != 8) {
char ack_msg[256];
snprintf(ack_msg, sizeof(ack_msg),
"{\"ack\":\"%s\",\"cycle\":%d,\"status\":\"%s\"}",
last_cmd_name, cycle, ack_status);
zmq_send_resilient(&ack_pub, zmq_ctx, "tcp://127.0.0.1:5559",
ZMQ_PUB, 1, ack_msg, strlen(ack_msg), 0, &ack_fail_count);
}
} else if (cmd_len < 0 && zmq_errno() != EAGAIN) {
// v4: log unexpected receive errors (EAGAIN is normal for DONTWAIT)
fprintf(stderr, "[ZMQ] PULL recv error: %s\n", zmq_strerror(zmq_errno()));
fflush(stderr);
}
// === LBM step (UNCHANGED) ===
streaming_kernel<<<grid, block>>>(d_f[current], d_f[1-current], NX, NY);
CUDA_CHECK(cudaDeviceSynchronize());
collide_kernel_khragixx<<<grid, block>>>(d_f[1-current], h_omega, cycle);
CUDA_CHECK(cudaDeviceSynchronize());
current = 1 - current;
// === Telemetry every 10 cycles ===
if (cycle % 10 == 0) {
// Launch both kernels before sync — GPU can overlap them
compute_rho<<<grid, block>>>(d_f[current], d_rho);
compute_velocity_stress_v4<<<grid, block>>>(d_f[current],
d_ux, d_uy, d_sxx, d_syy, d_sxy);
CUDA_CHECK(cudaDeviceSynchronize());
// Copy all fields to host
CUDA_CHECK(cudaMemcpy(h_rho, d_rho, scalar_size, cudaMemcpyDeviceToHost));
CUDA_CHECK(cudaMemcpy(h_ux, d_ux, scalar_size, cudaMemcpyDeviceToHost));
CUDA_CHECK(cudaMemcpy(h_uy, d_uy, scalar_size, cudaMemcpyDeviceToHost));
CUDA_CHECK(cudaMemcpy(h_sxx, d_sxx, scalar_size, cudaMemcpyDeviceToHost));
CUDA_CHECK(cudaMemcpy(h_syy, d_syy, scalar_size, cudaMemcpyDeviceToHost));
CUDA_CHECK(cudaMemcpy(h_sxy, d_sxy, scalar_size, cudaMemcpyDeviceToHost));
// Combined host reduction
float sum_rho = 0.0f, sum_rho_sq = 0.0f;
float sum_vel = 0.0f, sum_vel_sq = 0.0f, max_vel = 0.0f;
float sum_sxx = 0.0f, sum_syy = 0.0f, sum_sxy = 0.0f;
for (int i = 0; i < NX * NY; i++) {
sum_rho += h_rho[i];
sum_rho_sq += h_rho[i] * h_rho[i];
float vel = sqrtf(h_ux[i] * h_ux[i] + h_uy[i] * h_uy[i]);
sum_vel += vel;
sum_vel_sq += vel * vel;
if (vel > max_vel) max_vel = vel;
sum_sxx += h_sxx[i];
sum_syy += h_syy[i];
sum_sxy += h_sxy[i];
}
float N_total = (float)(NX * NY);
float mean_rho = sum_rho / N_total;
float variance = sum_rho_sq / N_total - mean_rho * mean_rho;
float coherence = 1.0f / (1.0f + sqrtf(variance > 0.0f ? variance : 0.0f));
float asymmetry = calculate_asymmetry_magnifying(h_rho);
float vel_mean = sum_vel / N_total;
float vel_max = max_vel;
float vel_var = sum_vel_sq / N_total - vel_mean * vel_mean;
if (vel_var < 0.0f) vel_var = 0.0f;
float mean_sxx = sum_sxx / N_total;
float mean_syy = sum_syy / N_total;
float mean_sxy = sum_sxy / N_total;
float vorticity_mean = compute_mean_vorticity(h_ux, h_uy);
// === NVML reads ===
unsigned int gpu_temp = 0;
unsigned int gpu_power_mw = 0;
float gpu_mem_pct = 0.0f;
unsigned int gpu_util_pct = 0;
if (nvml_ok) {
nvmlDeviceGetTemperature(nvml_device, NVML_TEMPERATURE_GPU, &gpu_temp);
nvmlDeviceGetPowerUsage(nvml_device, &gpu_power_mw);
// WSL2 NVML bug: getPowerUsage often returns power limit (TDP)
// instead of actual draw. Detect and correct.
unsigned int power_limit_mw = 0;
if (nvmlDeviceGetEnforcedPowerLimit(nvml_device, &power_limit_mw) == NVML_SUCCESS) {
if (power_limit_mw > 0 && gpu_power_mw >= power_limit_mw * 95 / 100) {
// Power reading is at/near TDP limit — likely WSL2 bug.
// Use utilization-scaled estimate: idle=30W, full=TDP
nvmlUtilization_t util;
if (nvmlDeviceGetUtilizationRates(nvml_device, &util) == NVML_SUCCESS) {
float idle_w = 30000.0f; // ~30W idle
gpu_power_mw = (unsigned int)(idle_w + (power_limit_mw - idle_w) * util.gpu / 100.0f);
}
}
}
nvmlUtilization_t util;
if (nvmlDeviceGetUtilizationRates(nvml_device, &util) == NVML_SUCCESS) {
gpu_util_pct = util.gpu;
}
nvmlMemory_t mem_info;
if (nvmlDeviceGetMemoryInfo(nvml_device, &mem_info) == NVML_SUCCESS) {
gpu_mem_pct = (float)mem_info.used / (float)mem_info.total * 100.0f;
}
}
// === v4: Extended telemetry JSON with timestamp ===
time_t now_ts = time(NULL);
char msg[1024];
snprintf(msg, sizeof(msg),
"{\"cycle\":%d,\"ts\":%ld,"
"\"coherence\":%.4f,\"asymmetry\":%.4f,"
"\"omega\":%.3f,\"khra_amp\":%.4f,\"gixx_amp\":%.4f,\"grid\":1024,"
"\"vel_mean\":%.6f,\"vel_max\":%.6f,\"vel_var\":%.8f,\"vorticity_mean\":%.6f,"
"\"stress_xx\":%.6f,\"stress_yy\":%.6f,\"stress_xy\":%.6f,"
"\"gpu_temp_c\":%u,\"gpu_power_w\":%.1f,\"gpu_util_pct\":%u,\"gpu_mem_pct\":%.1f}",
cycle, (long)now_ts,
coherence, asymmetry,
h_omega, h_khra_amp, h_gixx_amp,
vel_mean, vel_max, vel_var, vorticity_mean,
mean_sxx, mean_syy, mean_sxy,
gpu_temp, gpu_power_mw / 1000.0f, gpu_util_pct, gpu_mem_pct);
// v4: resilient send with auto-reconnect
zmq_send_resilient(&publisher, zmq_ctx, "tcp://127.0.0.1:5556",
ZMQ_PUB, 1, msg, strlen(msg), 0, &pub_fail_count);
// v4: ring buffer log
telemetry_ring_write(msg);
// v5: health_check response (with actual coherence/asymmetry values)
if (health_check_pending) {
time_t now = time(NULL);
long uptime = (long)(now - start_time);
nvmlMemory_t mem_info;
float gpu_mem_mb = 0.0f;
if (nvml_ok && nvmlDeviceGetMemoryInfo(nvml_device, &mem_info) == NVML_SUCCESS) {
gpu_mem_mb = (float)mem_info.used / (1024.0f * 1024.0f);
}
char health_msg[1024];
snprintf(health_msg, sizeof(health_msg),
"{\"health\":{\"cycle\":%d,\"coherence\":%.4f,\"asymmetry\":%.4f,\"omega\":%.3f,\"gpu_temp_c\":%u,\"gpu_mem_used_mb\":%.1f,\"uptime_seconds\":%ld,\"total_injections\":%llu,\"last_checkpoint_cycle\":%d}}",
cycle, coherence, asymmetry, h_omega, gpu_temp, gpu_mem_mb, uptime, total_injections, last_autosave);
zmq_send_resilient(&ack_pub, zmq_ctx, "tcp://127.0.0.1:5559",
ZMQ_PUB, 1, health_msg, strlen(health_msg), 0, &ack_fail_count);
printf("[v5] health_check responded at cycle %d (coh=%.4f, asym=%.4f)\n", cycle, coherence, asymmetry);
fflush(stdout);
health_check_pending = 0;
}
if (cycle % 100 == 0) {
printf("Cycle %d: Coh=%.3f Asym=%.4f omega=%.3f T=%uC P=%.0fW GPU=%u%% Mem=%.0f%%\n",
cycle, coherence, asymmetry, h_omega, gpu_temp, gpu_power_mw/1000.0f, gpu_util_pct, gpu_mem_pct);
fflush(stdout);
}
if (cycle % 1000 == 0) {
printf("[v4] Vel: mean=%.4f max=%.4f var=%.6f | Vort=%.4f | Stress: %.4f/%.4f/%.4f\n",
vel_mean, vel_max, vel_var, vorticity_mean, mean_sxx, mean_syy, mean_sxy);
fflush(stdout);
}
// === v4: Density snapshot export ===
// Fires at telemetry ticks where cycle is a multiple of snapshot_interval
// snapshot_now_flag fires at the next telemetry tick (max 100ms delay)
if ((snapshot_interval > 0 && cycle > 0 && cycle % snapshot_interval == 0)
|| snapshot_now_flag) {
uint32_t snap_cycle = (uint32_t)cycle;
uint16_t snap_w = (uint16_t)NX, snap_h = (uint16_t)NY;
memcpy(snap_buf, &snap_cycle, 4);
memcpy(snap_buf + 4, &snap_w, 2);
memcpy(snap_buf + 6, &snap_h, 2);
memcpy(snap_buf + 8, h_rho, scalar_size);
// v4: resilient send with auto-reconnect
zmq_send_resilient(&snapshot_pub, zmq_ctx, "tcp://127.0.0.1:5558",
ZMQ_PUB, 1, snap_buf, 8 + scalar_size, 0, &snap_fail_count);
if (snapshot_now_flag) {
printf("[SNAP] On-demand snapshot at cycle %d\n", cycle);
fflush(stdout);
snapshot_now_flag = 0;
} else if (cycle % 10000 == 0) {
printf("[SNAP] Periodic snapshot at cycle %d (interval=%d)\n", cycle, snapshot_interval);
fflush(stdout);
}
}
// === v5: Stress field snapshot on port 5560 ===
// Fires on stress_snapshot_now command — sends per-cell sxx/syy/sxy
// Header: same 8-byte format (cycle u32, width u16, height u16)
// Data: sxx[NX*NY] + syy[NX*NY] + sxy[NX*NY] as packed float32
// h_sxx/h_syy/h_sxy are already on host from telemetry copy above
if (stress_snapshot_now_flag) {
uint32_t snap_cycle = (uint32_t)cycle;
uint16_t snap_w = (uint16_t)NX, snap_h = (uint16_t)NY;
memcpy(stress_snap_buf, &snap_cycle, 4);
memcpy(stress_snap_buf + 4, &snap_w, 2);
memcpy(stress_snap_buf + 6, &snap_h, 2);
memcpy(stress_snap_buf + 8, h_sxx, scalar_size);
memcpy(stress_snap_buf + 8 + scalar_size, h_syy, scalar_size);
memcpy(stress_snap_buf + 8 + 2 * scalar_size, h_sxy, scalar_size);
// v4: resilient send with auto-reconnect
zmq_send_resilient(&stress_pub, zmq_ctx, "tcp://127.0.0.1:5560",
ZMQ_PUB, 1, stress_snap_buf, stress_snap_size, 0, &stress_fail_count);
printf("[v5-SNAP] Stress snapshot at cycle %d (%.1f MB)\n",
cycle, stress_snap_size / (1024.0 * 1024.0));
fflush(stdout);
stress_snapshot_now_flag = 0;
}
}
// === Autosave check ===
if (autosave_interval > 0 && cycle > 0 && (cycle - last_autosave) >= autosave_interval) {
save_checkpoint(h_f, d_f[current], cycle, ".");
last_autosave = cycle;
}
cycle++;
usleep(10000);
}
// === Graceful shutdown ===
printf("[SHUTDOWN] Saving final checkpoint at cycle %d...\n", cycle);
fflush(stdout);
save_checkpoint(h_f, d_f[current], cycle, ".");
printf("[SHUTDOWN] Closing ZMQ sockets...\n");
fflush(stdout);
zmq_close(publisher);
zmq_close(subscriber);
zmq_close(snapshot_pub);
zmq_close(ack_pub);
zmq_close(stress_pub);
printf("[SHUTDOWN] Destroying ZMQ context...\n");
fflush(stdout);
zmq_ctx_destroy(zmq_ctx);
printf("[SHUTDOWN] Freeing CUDA memory...\n");
fflush(stdout);
cudaFree(d_f[0]); cudaFree(d_f[1]); cudaFree(d_rho);
cudaFree(d_ux); cudaFree(d_uy);
cudaFree(d_sxx); cudaFree(d_syy); cudaFree(d_sxy);
cudaDeviceReset();
printf("[SHUTDOWN] Freeing host memory...\n");
fflush(stdout);
telemetry_ring_close();
free(h_f);
free(h_rho);
free(h_ux); free(h_uy);
free(h_sxx); free(h_syy); free(h_sxy);
free(snap_buf);
free(stress_snap_buf);
if (nvml_ok) nvmlShutdown();
printf("[SHUTDOWN] Complete. Uptime: %ld seconds\n", (long)(time(NULL) - start_time));
fflush(stdout);
return 0;
}