Ruby Fiddle - функция ведет себя по-разному между C и Ruby - PullRequest
2 голосов
/ 22 марта 2020

Я использую Ruby Fiddle для доступа к функции C для выполнения некоторых тяжелых вычислений. Функция C отлично работает при непосредственном вызове, но при использовании через Fiddle она возвращает непредсказуемым образом различные строки nan с и inf с. Функция работает с матрицами, которые передаются как указатели на массивы.

Я отладил код C, и все работает нормально. Я также сохранил различные параметры, переданные в функцию C, в файл, чтобы быть уверенным, что Fiddle не передает некоторые странные значения, но нет очевидной (по крайней мере для меня) проблемы.

Кроме того, кажется, что для «меньших» матриц этого не происходит. Заранее извиняюсь за то, что код очень длинный, но это единственный способ точно воспроизвести то, что происходит. Данные для тестирования находятся в этом файле. ( 1012 * суть *). Вы можете просто скопировать и вставить тестовые данные Ruby и C в приведенный ниже код.

Если это поможет, я работаю над macos Catalina и Ruby 2.2.4 (требование).

Код C следующий:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

double *mrt(
    double *person_total_shortwave,
    int rows_pts, int cols_pts,
    double *dry_bulb_temperatures,
    double *ground_temperatures,
    double *atmospheric_longwave,
    double *sky_view_factors,
    double *shading_view_factors_matrix,
    int rows_svf, int cols_svf,
    double *shading_temperatures_matrix,
    int rows_st, int cols_st,
    int size_dbt,
    double person_emissivity_shortwave, double person_emissivity_longwave,
    double surroundings_emissivity, double ground_emissivity, double ground_person_view_factor);

int save_to_file(char *filename, int m, int n, double *mat)
{
    FILE *fp;
    int i, j;

    if ((fp = freopen(filename, "w", stdout)) == NULL)
    {
        printf("Cannot open file.\n");
        exit(1);
    }
    for (i = 0; i < m; i++)
        for (j = 0; j < n; j++)
            if (j == n - 1)
                printf("%.17g \n", mat[i * n + j]);
            else
                printf("%.17g ", mat[i * n + j]);
    fclose(fp);
    return 0;
}

int main(void)
{

    // REPLACE WITH C DATA FROM FILE

    double *mrt_result;
    mrt_result = mrt(person_total_shortwave[0],
                     rows_pts, cols_pts,
                     dry_bulb_temperatures,
                     ground_temperatures,
                     atmospheric_longwave,
                     sky_view_factors,
                     shading_view_factors_matrix[0],
                     rows_svf, cols_svf,
                     shading_temperatures_matrix[0],
                     rows_st, cols_st,
                     size_dbt,
                     person_emissivity_shortwave, person_emissivity_longwave,
                     surroundings_emissivity, ground_emissivity, ground_person_view_factor);

    // save_to_file(rows_pts, cols_pts, mrt_result);

    return 0;
}
// https://www.dropbox.com/s/ix06edrrctad421/Calculation%20of%20Mean%20Radiant%20Temperature3.odt?dl=0

double *mrt(
    double *person_total_shortwave,
    int rows_pts, int cols_pts,
    double *dry_bulb_temperatures,
    double *ground_temperatures,
    double *atmospheric_longwave,
    double *sky_view_factors,
    double *shading_view_factors_matrix,
    int rows_svf, int cols_svf,
    double *shading_temperatures_matrix,
    int rows_st, int cols_st,
    int size_dbt,
    double person_emissivity_shortwave, double person_emissivity_longwave,
    double surroundings_emissivity, double ground_emissivity, double ground_person_view_factor)
{
    save_to_file("PTS.txt", rows_pts, cols_pts, person_total_shortwave);
    save_to_file("SVF.txt", 1, cols_svf, sky_view_factors);
    save_to_file("SVSM.txt", rows_svf, cols_svf, shading_view_factors_matrix);
    save_to_file("STM.txt", rows_st, cols_st, shading_temperatures_matrix);

    double *mrt_mat = (double *)calloc(rows_pts * cols_pts, sizeof(double));
    save_to_file("MRT_MAT0.txt", rows_pts, cols_pts, mrt_mat);

    int t, c, k;
    double sigma = 5.67E-8;
    double body_area = 1.94;

    double tmrt4_shortwave, tmrt4_longwave_ground, tmrt4_atm_longwave, tmrt4_surroundings;
    double tmrt4_shading[rows_svf];
    memset(tmrt4_shading, 0.0, rows_svf * sizeof(double));
    double tmrt4;
    double surroundings_view_factor;

    // t runs through the timesteps
    // c runs through the points in the mesh
    for (t = 0; t < rows_pts; t++)
    {
        for (c = 0; c < cols_pts; c++)
        {
            tmrt4_shortwave = 1.0 / (sigma * body_area) * (person_emissivity_shortwave / person_emissivity_longwave) * person_total_shortwave[t * cols_pts + c];

            // We are assuming that the ground is at ambient temperature
            tmrt4_longwave_ground = ground_person_view_factor * ground_emissivity * pow((273.15 + ground_temperatures[t]), 4);

            // Here we are using the actual long wave radiation from the sky
            tmrt4_atm_longwave = (1.0 - ground_person_view_factor) / sigma * sky_view_factors[c] * atmospheric_longwave[t];

            // We need to remove the contribution of all the shading devices
            // k runs through the shading devices
            surroundings_view_factor = 1.0 - sky_view_factors[c];
            for (k = 0; k < rows_svf; k++)
            {
                surroundings_view_factor -= shading_view_factors_matrix[k * cols_svf + c];
            }
            tmrt4_surroundings = (1.0 - ground_person_view_factor) * surroundings_view_factor * surroundings_emissivity * pow((273.15 + dry_bulb_temperatures[t] - 0.0), 4);

            // We now need to account for all contributions of the shading devices
            for (k = 0; k < rows_svf; k++)
            {
                tmrt4_shading[k] = (1.0 - ground_person_view_factor) * (shading_view_factors_matrix[k * cols_svf + c]) * surroundings_emissivity * pow((273.15 + shading_temperatures_matrix[k * cols_svf + t]), 4);
            }

            // Finally we add them all (see paper) for the total contribution
            tmrt4 = tmrt4_shortwave + tmrt4_longwave_ground + tmrt4_atm_longwave + tmrt4_surroundings;
            for (k = 0; k < rows_svf; k++)
            {
                tmrt4 += tmrt4_shading[k];
            }

            // Just convert to celsius
            mrt_mat[t * cols_pts + c] = pow(tmrt4, 0.25) - 273.15;
        }
    }
    save_to_file("MRT_MAT.txt", rows_pts, cols_pts, mrt_mat);

    // double x = 1.5;
    // while (1)
    // {
    //     x *= sin(x) / atan(x) * tanh(x) * sqrt(x);
    // }
    return mrt_mat;
}

и я компилирую с clang -g --extra-warnings utils.c -o utils.

Код Ruby следующий

require "fiddle"
require "fiddle/import"

module RG
  extend Fiddle::Importer
  @handler.handlers.each { |h| h.close unless h.close_enabled? } unless @handler.nil?
  GC.start

  dlload File.join("utils")
  extern "double* mrt(double*, int, int, double*, double*, double*, double*, double*, int, int, double*, int, int, int, double, double, double, double, double)"

  def self.mat_to_ptr(matrix)
    return Fiddle::Pointer[matrix.flatten.pack("E*")]
  end

  def self.ptr_to_mat(ptr, rows, cols)
    length = rows * cols * Fiddle::SIZEOF_DOUBLE
    mat = ptr[0, length]
    return mat.unpack("E*").each_slice(cols).to_a
  end

  def self.mean_radiant_temperature(
    person_total_shortwave,
    dry_bulb_temperatures,
    ground_temperatures,
    atmospheric_longwave,
    sky_view_factors,
    shading_view_factors_matrix,
    shading_temperatures_matrix,
    person_emissivity_shortwave,
    person_emissivity_longwave,
    surroundings_emissivity,
    ground_emissivity,
    ground_person_view_factor
  )
    rows_pts = person_total_shortwave.size
    cols_pts = person_total_shortwave[0].size
    person_total_shortwave_pointer = RG.mat_to_ptr(person_total_shortwave)

    dry_bulb_temperatures_pointer = RG.mat_to_ptr(dry_bulb_temperatures)
    ground_temperatures_pointer = RG.mat_to_ptr(ground_temperatures)
    size_dbt = dry_bulb_temperatures.size

    atmospheric_longwave_pointer = RG.mat_to_ptr(atmospheric_longwave)
    sky_view_factors_pointer = RG.mat_to_ptr(sky_view_factors)

    rows_svf = shading_view_factors_matrix.size
    if rows_svf > 0
      cols_svf = shading_view_factors_matrix[0].size
    else
      cols_svf = 0
    end

    shading_view_factors_matrix_pointer = RG.mat_to_ptr(shading_view_factors_matrix)

    rows_st = shading_temperatures_matrix.size
    if rows_st > 0
      cols_st = shading_temperatures_matrix[0].size
    else
      cols_st = 0
    end

    shading_temperatures_matrix_pointer = RG.mat_to_ptr(shading_temperatures_matrix)

    mrt_pointer = mrt(
      person_total_shortwave_pointer,
      rows_pts, cols_pts,
      dry_bulb_temperatures_pointer,
      ground_temperatures_pointer,
      atmospheric_longwave_pointer,
      sky_view_factors_pointer,
      shading_view_factors_matrix_pointer,
      rows_svf, cols_svf,
      shading_temperatures_matrix_pointer,
      rows_st, cols_st,
      size_dbt,
      person_emissivity_shortwave,
      person_emissivity_longwave,
      surroundings_emissivity,
      ground_emissivity,
      ground_person_view_factor
    )
    return RG.ptr_to_mat(mrt_pointer, rows_pts, cols_pts)
  end
end

// REPLACE WITH RUBY DATA FROM FILE

mean_radiant_temperatures = RG.mean_radiant_temperature(
  person_total_shortwave,
  dry_bulb_temperatures,
  ground_temperatures,
  atmospheric_longwave,
  sky_view_factors,
  shading_view_factors_matrix,
  shading_temperatures_matrix,
  person_emissivity_shortwave,
  person_emissivity_longwave,
  surroundings_emissivity,
  ground_emissivity,
  ground_person_view_factor
)
File.open("MRT_MAT_RUBY.txt", "w") do |f|
  mean_radiant_temperatures.each do |row|
    f.puts row.join(' ')
  end
end

Если вы хотите проверить это, сначала запустите ./utils. Это сохранит несколько файлов в локальной папке. Взгляните на MRT_MAT.txt.

Теперь запустите ruby код несколько раз . Он сгенерирует тот же файл, но вы заметите, что «часто» файл будет содержать случайные строки с nan и inf. Те же самые данные возвращаются в Ruby и сохраняются в файле MRT_MAT_RUBY.txt в локальном каталоге.

Я довольно знаком с Ruby, но C на самом деле не моя сила. Было бы здорово иметь возможность отлаживать код C при вызове из Ruby, но я не знаю, как это сделать.

1 Ответ

4 голосов
/ 27 марта 2020

Полагаю, строка, созданная методом pack в методе mat_to_ptr, собирается G C.

Вы должны хранить строку до вызова pte_to_mat.

...