0

I know this is probably not a very smart question to ask, but I have a code written in C that generates a binary file and I have to read the data from this file with Fortran. I'm not really familiar with C but I tried to come up with a solution.

Here is how the file in written in the C program

unsigned nvarnew = 16;
std::vector<float> new_data( ncells*nvarnew, 0.0f );

sprintf(offname,"%s_pot.dat",snapname.c_str());
 for( size_t i=0; i<ncells*nvarnew; ++i )
    new_data[i] = bytereorder<float>(new_data[i]);

  std::ofstream offs(offname,std::ios::binary);
  for( size_t i=0; i<ncells; ++i )
  {
    offs.write( reinterpret_cast<char*>(&blocksize), sizeof(int) );
    offs.write( reinterpret_cast<char*>(&new_data[nvarnew*i]), blocksize );
    offs.write( reinterpret_cast<char*>(&blocksize), sizeof(int) );
  }
  offs.close();

and here I how I try to attempt to reading it with Fortran

subroutine read_output(filename)
implicit none
character (*),intent (in) :: filename
integer(4)::temp,temp2
real(4)::tempr
real(kind=8),allocatable,dimension(:)::dx,xgas,ygas,zgas,nH,divv
real(kind=8),allocatable,dimension(:)::force_dm_x,force_dm_y,force_dm_z
real(kind=8),allocatable,dimension(:)::force_gas_x,force_gas_y,force_gas_z
real(kind=8),allocatable,dimension(:)::force_stars_x,force_stars_y,force_stars_z
integer::Nmax,Ngas2

Nmax = 1000000

allocate(dx(Nmax),xgas(Nmax),ygas(Nmax),zgas(Nmax),nH(Nmax),divv(Nmax))
allocate(force_dm_x(Nmax),force_dm_y(Nmax),force_dm_z(Nmax))
allocate(force_gas_x(Nmax),force_gas_y(Nmax),force_gas_z(Nmax))
allocate(force_stars_x(Nmax),force_stars_y(Nmax),force_stars_z(Nmax))

open ( 19 , file = filename, form = 'unformatted',access='stream')

Ngas2=1

DO WHILE (Ngas2.lt.Nmax)
      read (19,end=6) temp,dx(Ngas2),xgas(Ngas2),ygas(Ngas2),zgas(Ngas2),nH(Ngas2),divv(Ngas2),&
      & force_dm_x(Ngas2),force_dm_y(Ngas2),force_dm_z(Ngas2),&
      & force_gas_x(Ngas2),force_gas_y(Ngas2),force_gas_z(Ngas2),&
      & force_stars_x(Ngas2),force_stars_y(Ngas2),force_stars_z(Ngas2)
read(19) temp

if(Ngas2==1) then
write(*,*) dx(Ngas2),xgas(Ngas2),ygas(Ngas2),zgas(Ngas2),nH(Ngas2),divv(Ngas2),&
& force_dm_x(Ngas2),force_dm_y(Ngas2),force_dm_z(Ngas2),&
& force_gas_x(Ngas2),force_gas_y(Ngas2),force_gas_z(Ngas2),&
& force_stars_x(Ngas2),force_stars_y(Ngas2),force_stars_z(Ngas2)
end if

       Ngas2=Ngas2+1
end do

6              continue
close (19)
if( Ngas2.eq.Nmax ) then
        print *, 'DATA GAS - error reading data: too many cells. Change Nmax.'
        print *, 'Ngas=',Ngas2
        stop
end if
        Ngas2=Ngas2-1


       ! print *,Ngas

!print*,dx(1),xgas2(1),ygas2(1),zgas2(1),nH(1),divv(1),vortx(1),vorty(1),vortz(1),baroc(1),vortentr(1),vortrho(1),strain(1),strainpar(1),strainper(1),qp(1),qp_per(1),qp_par(1),kin(1),gforce(1),qphi(1),qphi_par(1),qphi_per(1),gx(1),gy(1),gz(1),px(1),py(1),pz(1),sx(1),sy(1),sz(1),phi(1)

end subroutine read_output

program main
implicit none


call read_output("binary_c_code.dat")

end program main

But the problem is that when I try to print what I read I get numbers that don't really make sense. I guess the problem might be in the declaration of the variables. I even tried declaring them as real(kind=4), but I still get numbers that don't make sense.

alk
  • 69,737
  • 10
  • 105
  • 255
Brian
  • 13,996
  • 19
  • 70
  • 94
  • 3
    I think you mean C++ rather than C – Levi Jun 07 '15 at 07:10
  • 2
    oh sorry, as I said I'm not really familiar with either C or C++, that's why I'm struggling on this very basic issues – Brian Jun 07 '15 at 07:10
  • I normally don't use iostream for binary files but ``offs.write( reinterpret_cast(&blocksize), sizeof(int) );`` particularly the cast to char* looks a bit fishy to me. – BitTickler Jun 07 '15 at 07:15
  • I would probably write a c++ read-back function to see if what you read back is what you intended. This would help find the mistakes in the write-code and separate those from C++/fortran interaction issues. – BitTickler Jun 07 '15 at 07:16
  • 1
    Use just `reaɬ, that is the closest companion of `float` (or something more advanced). The hard coded kind values (4 and 8) are not portable. Anyway, the error will not be in this, but in the algorithm. – Vladimir F Героям слава Jun 07 '15 at 07:16
  • Also, the size type of a vectors length is usually ``size_t`` which is not the same as ``int``. There is also a ``vector::size_type`` typedef at your disposal. So in your writes you should either cast the vector size to some ``cstdint`` portable size integer (e.g. ``uint32_t``) or use the debugger or "go to definition" features of your IDE to find out about the size_t definition on your platform. – BitTickler Jun 07 '15 at 07:25
  • Next, C++ ``float`` is usually (but not necessarily) 4 bytes floating point number and ``double`` is 8 bytes. If I understand your fortran code correctly, you try to read doubles after writing floats. – BitTickler Jun 07 '15 at 07:28
  • 1
    Printing the values in hex format can help reveal endianness mismatches. – ysap Jun 07 '15 at 07:42
  • 1
    What is blocksize ? Why do you write it before and after each write of data ? Why don't you write all the vector in one go ? – Vincent Fourmond Jun 07 '15 at 07:52
  • How about reading only one integer from this file (temp in this case) using unformatted + stream or direct access with 4-byte record and print it? If not successful, it may be a so-called "endian" problem. (@VincentFourmond I guess the C++ code takes care of Fortran record separators http://stackoverflow.com/questions/8751185/fortran-unformatted-file-format – roygvib Jun 08 '15 at 03:37

1 Answers1

0
new_data[i] = bytereorder<float>(new_data[i]);

I am not sure what this line is for (probably you read data in another endianess). That aside, a normal C++ write (using old school file apis) would look as follows:

#include <cstdio>
#include <cstdint>
#include <vector>

bool SaveVector( const char * filePath, const std::vector<float>& v )
{
    bool success = false;
    FILE *hFile = fopen(filePath, "wb");
    if( NULL != hFile )
    {
        // Make sure we know it is a 4 byte signed int regarding vectors size.
        int32_t vsize = static_cast<int32_t>(v.size());
        if( 1 == fwrite (&vsize, sizeof(vsize), 1, hFile ) )
        {
            if( v.size() == fwrite( &v[0], sizeof(std::vector<float>::value_type), v.size(), hFile ) )
            {
                success = true;
            }
        }
        fclose(hFile);
    }
    return success;
}

I hope it helps. The function saves the length of the vector first as a 4 bytes signed integer, then the content of the vector as 4 byte floats (on a typical system).

EDIT: changed size to signed 4 byte int (thanks Vlad!), fixed a typo.

BitTickler
  • 10,905
  • 5
  • 32
  • 53