熱伝導シミュレーション

下記のソースコードは,有限差分法によって熱伝導方程式をシミュレーションするためのプログラムです.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

struct simulator {
    int n;
    float *T1;
    float *T2;
    int tmax;
    float alpha;
    float w1;
    float w2;
    float dx;
    float dt;
};

static struct timespec diff(struct timespec start, struct timespec end)
{
    struct timespec ts;

    if (end.tv_nsec - start.tv_nsec < 0) {
        ts.tv_sec = end.tv_sec - start.tv_sec - 1;
        ts.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
    } else {
        ts.tv_sec = end.tv_sec - start.tv_sec;
        ts.tv_nsec = end.tv_nsec - start.tv_nsec;
    }
    return ts;
}

static void write_result(struct simulator *sim, int step)
{
    char fname[32];
    sprintf(fname, "result_%d.csv", step);

    FILE *fd = fopen(fname, "w");

    fprintf(fd, "x,y,T\n");

    for (int y = 1; y <= sim->n; y++) {
        for (int x = 1; x <= sim->n; x++) {
            fprintf(fd, "%f,%f,%f\n", (x - 1) * sim->dx, (y - 1) * sim->dx,
                    sim->T1[x + y * (sim->n + 2)]);
        }
    }

    fclose(fd);
}

static void step(struct simulator *sim)
{
    for (int y = 1; y <= sim->n; y++) {
        for (int x = 1; x <= sim->n; x++) {
            sim->T2[x + y * (sim->n + 2)] =
                sim->w1 * sim->T1[x + y * (sim->n + 2)] +
                sim->w2 * (sim->T1[(x - 1) + y * (sim->n + 2)] +
                           sim->T1[x + (y - 1) * (sim->n + 2)] +
                           sim->T1[(x + 1) + y * (sim->n + 2)] +
                           sim->T1[x + (y + 1) * (sim->n + 2)]);
        }
    }

    float *tmp = sim->T2;
    sim->T2 = sim->T1;
    sim->T1 = tmp;
}

static void compute(struct simulator *sim)
{
    for (int i = 0; i < sim->tmax; i++) {
        step(sim);

        if (i % 10 == 0) {
            printf("Writing out step %d\n", i);
            write_result(sim, i);
        }
    }
}

static void print_settings(struct simulator *sim)
{
    printf("grid size:  %dx%d\n", sim->n, sim->n);
    printf("tmax:       %d\n", sim->tmax);
    printf("alpha:      %f\n", sim->alpha);
    printf("delta x:    %f\n", sim->dx);
    printf("delta t:    %f\n", sim->dt);
}

static void initialize(struct simulator *sim)
{
    for (int y = 0; y <= sim->n + 1; y++) {
        for (int x = 0; x <= sim->n + 1; x++) {
            float temp = 50.0f;

            if (x == 0 || y == 0) {
                temp = 0.0f;
            } else if (x == sim->n + 1 || y == sim->n + 1) {
                temp = 100.0f;
            }

            sim->T1[x + y * (sim->n + 2)] = temp;
            sim->T2[x + y * (sim->n + 2)] = temp;
        }
    }
}

int main(int argc, char *argv[])
{
    int ch;
    int n = 100, tmax = 100;

    while ((ch = getopt(argc, argv, "t:n:")) != -1) {
        switch (ch) {
        case 'n':
            n = atoi(optarg);
            break;
        case 't':
            tmax = atoi(optarg);
            break;
        case 'h':
        default:
            fprintf(stderr, "Usage: %s -n <size>\n", argv[0]);
            break;
        }
    }

    struct simulator sim;

    sim.n = n;
    sim.T1 = (float *)calloc((n + 2) * (n + 2), sizeof(float));
    sim.T2 = (float *)calloc((n + 2) * (n + 2), sizeof(float));
    sim.tmax = tmax;

    sim.alpha = 0.1f;
    sim.dx = 0.01f;
    sim.dt = 0.0001f;
    sim.w1 = 1.0f - (4.0f * sim.alpha * sim.dt) / (sim.dx * sim.dx);
    sim.w2 = (sim.alpha * sim.dt) / (sim.dx * sim.dx);

    print_settings(&sim);

    initialize(&sim);

    struct timespec start_ts, end_ts;
    clock_gettime(CLOCK_MONOTONIC, &start_ts);

    compute(&sim);

    clock_gettime(CLOCK_MONOTONIC, &end_ts);

    struct timespec elapsed_ts = diff(start_ts, end_ts);

    printf("elapsed:    %ld.%09ld [s]\n", elapsed_ts.tv_sec,
           elapsed_ts.tv_nsec);

    return 0;
}

やってみよう

  • このプログラムをコンパイルし,実行してみましょう.
  • ParaViewを用いて,出力されたCSVファイルを可視化してみましょう.
  • 初期条件を変更し,シミュレーション結果がどのように変わるか調べてみましょう.