2

我正在使用Ruby Fiddle访问 C 函数来执行一些繁重的计算。C 函数在直接调用时工作得非常好,但是当通过Fiddle它使用时,它会以不可预知的方式返回多行nans 和infs。该函数对作为指向数组的指针传递的矩阵进行操作。

我已经调试了 C 代码,一切正常。我还将传递给 C 函数的各种参数保存到文件中,以确保 Fiddle 没有传递一些奇怪的值,但没有明显的(至少对我而言)问题。

此外,对于“较小”的矩阵,这似乎不会发生。提前为代码很长道歉,但这是准确重现它正在发生的事情的唯一方法。用于测试的数据在文件中。(要点)。您只需将 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 并不是我的强项。从Ruby调用时能够调试C代码会很棒,但我真的不知道该怎么做。

4

1 回答 1

4

pack我猜方法中方法创建的字符串mat_to_ptr是由GC收集的。

您应该保留字符串直到pte_to_mat被调用。

于 2020-03-27T14:12:12.903 回答