Загрузка данных
#define _POSIX_C_SOURCE 199309L
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#define NX 1000
#define NY 1000
#define STEPS 500
#define L 0.01
#define DX (L/NX)
#define DY (L/NY)
#define Kk 6.196522218e-6
#define DT (0.9 * DX * DY / (4 * Kk))
#define PI 3.141592653589793
// Вспомогательные функции
__host__ __device__ double lamda(double u) {
return 1.45 + 0.023 * u - 2e-6 * u * u;
}
__host__ __device__ double c(double u) {
return 1 / (2.25 * 0.001 - 6.08e-10 * u * u);
}
__host__ __device__ double p(double u) {
return 7860.0 + 41500.0 / u;
}
__host__ __device__ double u0(double x, double y) {
x /= L;
y /= L;
return 400.0 + 50.0 * x * x + 50.0 * y * y;
}
__host__ __device__ double m1(double y, double t) {
y /= L;
t /= (DT * STEPS);
return 400.0 + 50.0 * y * y + 100.0 * t * sqrt(16.0 + 20.0 * y * y);
}
__host__ __device__ double m2(double y, double t) {
y /= L;
t /= (DT * STEPS);
return 450.0 + 50.0 * y * y + 150.0 * t * (1.0 - y);
}
__host__ __device__ double m3(double x, double t) {
x /= L;
t /= (DT * STEPS);
return 400.0 + 50.0 * x * x + 400.0 * t * cos(2.0 * PI * x) - 250.0 * t * x;
}
__host__ __device__ double m4(double x, double t) {
x /= L;
t /= (DT * STEPS);
return 450.0 + 50.0 * x * x + 600.0 * t * cos(PI * x / 2.0);
}
// Ядро CUDA для обновления внутренних точек
__global__ void update_kernel(double* u_curr, double* u_next, int nx, int ny, double dx, double dy, double dt) {
int i = blockIdx.x * blockDim.x + threadIdx.x + 1;
int j = blockIdx.y * blockDim.y + threadIdx.y + 1;
if (i < nx - 1 && j < ny - 1) {
int idx = i * ny + j;
double u_center = u_curr[idx];
double u_left = u_curr[(i-1) * ny + j];
double u_right = u_curr[(i+1) * ny + j];
double u_bottom = u_curr[i * ny + (j-1)];
double u_top = u_curr[i * ny + (j+1)];
double lamda_left = lamda((u_left + u_center) / 2.0);
double lamda_right = lamda((u_right + u_center) / 2.0);
double lamda_bottom = lamda((u_bottom + u_center) / 2.0);
double lamda_top = lamda((u_top + u_center) / 2.0);
double flux_x = lamda_right * (u_right - u_center) - lamda_left * (u_center - u_left);
double flux_y = lamda_top * (u_top - u_center) - lamda_bottom * (u_center - u_bottom);
double div = c(u_center) * p(u_center);
u_next[idx] = u_center + dt * (flux_x / (dx * dx) + flux_y / (dy * dy)) / div;
}
}
// Ядро для установки граничных условий
__global__ void boundary_kernel(double* u, int nx, int ny, double dx, double dy, double t) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
int j = blockIdx.y * blockDim.y + threadIdx.y;
if (i == 0 && j < ny) {
u[0 * ny + j] = m1(j * dy, t);
}
else if (i == nx - 1 && j < ny) {
u[(nx - 1) * ny + j] = m2(j * dy, t);
}
else if (j == 0 && i < nx) {
u[i * ny + 0] = m3(i * dx, t);
}
else if (j == ny - 1 && i < nx) {
u[i * ny + (ny - 1)] = m4(i * dx, t);
}
}
// Функция замера времени
double get_time() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec + ts.tv_nsec * 1e-9;
}
int main() {
// Выделение памяти на хосте
double *h_u_curr = (double*)malloc(NX * NY * sizeof(double));
double *h_u_next = (double*)malloc(NX * NY * sizeof(double));
if (!h_u_curr || !h_u_next) {
printf("Ошибка выделения памяти на хосте!\n");
return 1;
}
// Инициализация начальными условиями
for (int i = 0; i < NX; i++) {
for (int j = 0; j < NY; j++) {
int idx = i * NY + j;
h_u_curr[idx] = u0(i * DX, j * DY);
h_u_next[idx] = 0.0;
}
}
// Выделение памяти на устройстве
double *d_u_curr, *d_u_next;
cudaMalloc(&d_u_curr, NX * NY * sizeof(double));
cudaMalloc(&d_u_next, NX * NY * sizeof(double));
// Копирование начальных данных на GPU
cudaMemcpy(d_u_curr, h_u_curr, NX * NY * sizeof(double), cudaMemcpyHostToDevice);
cudaMemcpy(d_u_next, h_u_next, NX * NY * sizeof(double), cudaMemcpyHostToDevice);
// Настройка сетки блоков и потоков
dim3 threads(16, 16);
dim3 blocks((NX - 2 + threads.x - 1) / threads.x, (NY - 2 + threads.y - 1) / threads.y);
dim3 blocks_boundary((NX + threads.x - 1) / threads.x, (NY + threads.y - 1) / threads.y);
double start_time = get_time();
// Основной цикл по времени
for (int step = 0; step < STEPS; step++) {
double t = DT * (step + 1);
update_kernel<<<blocks, threads>>>(d_u_curr, d_u_next, NX, NY, DX, DY, DT);
boundary_kernel<<<blocks_boundary, threads>>>(d_u_next, NX, NY, DX, DY, t);
double *temp = d_u_curr;
d_u_curr = d_u_next;
d_u_next = temp;
}
cudaDeviceSynchronize();
double end_time = get_time();
// Копирование результата обратно на хост
cudaMemcpy(h_u_curr, d_u_curr, NX * NY * sizeof(double), cudaMemcpyDeviceToHost);
printf("\nРезультат:\n");
printf("Температура в центре: %.2f\n", h_u_curr[(NX/2) * NY + NY/2]);
printf("Время расчёта: %.3f секунд\n", end_time - start_time);
// Освобождение памяти
cudaFree(d_u_curr);
cudaFree(d_u_next);
free(h_u_curr);
free(h_u_next);
return 0;
}