передать многомерный массив в C ++ для программиста на фортране - PullRequest
1 голос
/ 02 января 2012

Я привык писать программы на Фортране (это f90), но мне нужно написать программу на C ++. Я очень запутался в том, как передать многомерный массив из функции C ++. В качестве примера я хочу прочитать список атомных координат в формате XYZ в подпрограмме, а затем передать координаты основной программе.

Вот оно в Фортране:

program findcenterofmass
character*2 atm(1000)
integer ttl
real*8 crd(1000,3)

call getxyz(atm,ttl,crd)
call centerofmass(ttl,crd)
end

subroutine getxyz(element,atomcount,coord)
character*2 element(1000)
integer atomcount
real*8 coord(1000,3)
open(file='test.xyz',unit=1)
read(1,*) atomcount
read(1,*)
do i=1,atomcount
   read(1,*) element(i),(coord(i,j),j=1,3)
enddo
close(unit=1)
end

subroutine centerofmass(atomcount,coord)
integer atomcount
real*8 coord(1000,3)
real*8 x,y,z
do i=1,atomcount
   x=x+coord(i,1)/atomcount
   y=y+coord(i,2)/atomcount
   z=z+coord(i,3)/atomcount
enddo
write(*,*) 'Center of mass is x: ',x,' y:',y,' z:',z
end

Прочитанный здесь тестовый файл представляет собой очень простую молекулу CO2:

3

C  0.0  0.0  0.0
O -1.4  0.0  0.0
O  1.4  0.0  0.0

Так что мне нужно сделать ту же процедуру в C ++ и ту часть, которая кажется наиболее запутанным является чтение координат в многомерном массив, а затем передать массив обратно в основную программу.

Вот C ++ (в нем есть ошибки) - любая помощь будет принята с благодарностью!

#include <stdio.h>
#include <iostream>
#include <string>
#include <sstream>

void readxyz(double& x);

int main () {

double x[100][3];
readxyz(double& x);
std::cout << " x " << x[0][0] << "\n";
return 0;
}

void readxyz(double& coord[][3])
{
  int i,j,k;
  int ttl;
  int MAXATOM=1000;
  int MAXLINE=72;
  char atoms[MAXATOM][2];
  long double coord[MAXATOM][3];
  char s[MAXLINE];
  const char* filename="test.xyz";

  using namespace std;
  cout.precision(12);

  FILE *fp = fopen(filename,"r");

  fgets(s,MAXLINE,fp);
  std::stringstream stream(s);
  stream >> ttl;

  fgets(s,MAXLINE,fp);

  for (i = 0; i < ttl; i++) {
    fgets(s,MAXLINE,fp);
    std::stringstream stream(s);
    stream >> atoms[i] >> coord[i][0] >> coord[i][1] >> coord[i][2];
    }
}

Ответы [ 3 ]

0 голосов
/ 02 января 2012

double& coord[][3] - это двумерный массив ссылок с одним фиксированным измерением и одним неопределенным измерением, если он даже компилируется.Может быть не то, что вы хотите.Вызванная функция не сможет определить размер неизвестного измерения во время выполнения.

Когда вы передаете многомерный массив в C ++, под капотом все, что передается, это указатель напервый элемент.Таким образом, чтобы везде работать с произвольными измерениями, вы передаете указатель и любые измерения в качестве дополнительных параметров:

void readxyz(double* coord, size_t numCoords, size_t numAxes)
{   // ... access array data with:
    coord[coordIndex * numAxes + axisIndex]

    // ... or somewhat optimized in loops:
    for(int coordIndex = 0; coordIndex < numCoords; ++coordIndex) {
        double* thisCoord = coord + coordIndex * numAxes;
        cout << "(";
        for(int axisIndex = 0; axisIndex < numAxes; ++axisIndex) {
            if(axisIndex)
                cout << ", ";
            cout << thisCoord[axisIndex];
        }
        cout << ")" << endl;
   }
}

Вы не вдавались в подробности того, что вы будете делать с информациейпосле того, как вы его прочитаете, я понятия не имею, должны ли данные храниться в виде многомерного массива для дальнейшей обработки.Лично я бы сохранил это как вектор объектов Atom и просто использовал бы ввод-вывод C ++ вместо смешивания C (функции, начинающиеся с f) и C ++ (классы, заканчивающиеся на stream):

#include <iostream>
#include <iomanip>
#include <fstream>
#include <vector>

struct Atom
// Defined as a `struct` to keep it as a simple "plain old data" (POD) type.
// It doesn't have to be this way.
{   // Store atom names as C strings; they can have up to 3 characters.
    // Possible optimization: store the atomic number (one byte) instead 
    // and use a lookup table (or std::map) to get names.
    char name[4];
    double x, y, z;
};

typedef std::vector<Atom> Atoms;

std::istream& operator >>(std::istream& is, Atom& a)
{   // Always use setw() with char* to avoid buffer overflow.
    is >> std::setw(4) >> a.name >> a.x >> a.y >> a.z;
    return is;
}

void ReadAtoms(const char* filename, Atoms& atoms)
{   std::ifstream ifs(filename);
    size_t numAtoms;
    ifs >> numAtoms;

    for(size_t i = 0; i < numAtoms; i++)
    {   Atom atom;
        ifs >> atom;
        atoms.push_back(atom);
    }
}

int main(int argc, char* argv[])
{   if(argc != 2)
    {   std::cerr << "Usage: " << argv[0] << " <filename>" << '\n';
        return 1;
    }

    Atoms atoms;
    ReadAtoms(argv[1], atoms);
    std::cout << " x " << atoms[0].x << '\n';
    return 0;
}

Это не обрабатывает поврежденные данные во входном файле, но это отправная точка.

0 голосов
/ 02 января 2012

Намерено ли считать фактическую длину массива из файла? Я догадываюсь из Фортрана:

read(1,*) atomcount
do i=1,atomcount
  read(1,*) element(i),(coord(i,j),j=1,3)
enddo

В таком случае было бы лучше сделать массив переменной длины, а не угадывать максимальную длину (1000). Что произойдет, если ваше предположение слишком мало? Что легко сделать в Fortran> = 90 или C ++. В Фортране сделайте массивы «выделяемыми» и выделите их после считывания размера atomcount из файла. Также нет необходимости явно передавать размеры массива между различными процедурами ...

0 голосов
/ 02 января 2012

Обратите внимание, что char atoms[MAXATOM][2];, но вы не даете достаточно индексов при записи в atoms в цикле: stream >> atoms[i] //...

...