toylas
toylas

Reputation: 437

Unformatted input/output for Fortran

I'm trying to use FortranFile to get an output that I can use in my simulation code written in F95. I'm having troubles getting fortranfile to work properly. Maybe it's because I do not understand how it works. Here's my problem:

If I want to write a 1D array using FortranFile, it works fine:

nx = 128
bxo = np.zeros(nx, dtype=float)
bxo = something
import fortranfile as fofi
bxf=fofi.FortranFile('Fbx.dat',mode='w')
bxf.writeReals(bxo,prec='d')
bxf.close()

The above 1D version works like a charm. As soon as I try to do it for a 2D array, I get problems

nx = 128; ny = 128
bxo = np.zeros((nx,ny), dtype=float)
bxo = something
import fortranfile as fofi
bxf=fofi.FortranFile('Fbx.dat',mode='w')
bxf.writeReals(bxo,prec='d')
bxf.close()

When I try to do this, I get the following error:

---------------------------------------------------------------------------
error                                     Traceback (most recent call last)
/Library/Frameworks/Python.framework/Versions/7.3/lib/python2.7/site-packages/IPython/utils/py3compat.py in execfile(fname, *where)
    173             else:
    174                 filename = fname
--> 175             __builtin__.execfile(filename, *where)

/Users/parashar/Dropbox/sandbox/test2d.py in <module>()
    130 vyf=fofi.FortranFile('Fvy.dat',mode='w')
    131 vzf=fofi.FortranFile('Fvz.dat',mode='w')
--> 132 bxf.writeReals(bxo,prec='d')
    133 byf.writeReals(byo,prec='d')
    134 bzf.writeReals(bzo,prec='d')

/Users/parashar/Dropbox/sandbox/fortranfile.py in writeReals(self, reals, prec)
    215         _fmt = self.ENDIAN + prec
    216         for r in reals:
--> 217             self.write(struct.pack(_fmt,r))
    218         self._write_check(length_bytes)
    219

error: required argument is not a float

Any ideas what could be going on?

Thanks!

Upvotes: 1

Views: 1322

Answers (2)

toylas
toylas

Reputation: 437

I like Emmet's suggestion but given that my knowledge of python is very basic, it would be a lot of effort for a short goal. I just realized that I could handle the situation in a slightly different manner.

Direct access files in Fortran do not have the unnecessary leading/trailing information that the usual unformatted fortran files do. So the simplest way to deal with unformatted data exchanged between Fortran and Python is to treat the files as direct access in Fortran. Here is an example of how we can use data to/from python/fortran.

Python code:

import numpy as np
nx=128; ny=128;
bxo=np.zeros((nx,ny),dtype=float)
bxo=something
bxf=open('Fbx.dat',mode='wb')
np.transpose(bxo).tofile(bxf) # We transpose the array to map indices 
                              # from python to fortran properly
bxo.close()

Fortran code:

  program test
  implicit none
  double precision, dimension(128,128) :: bx
  integer :: NNN, i, j
  inquire(iolength=NNN) bx
  open(unit=23,file='Fbx.dat',form='unformatted',status='old',&
        access='direct',recl=NNN)
  read(23,rec=1) bx
  close(23)
  ! Write it out to a text file to test it
  ! by plotting in gnuplot
  do i=1,128; do j=1,128
     write(23,*) i,j,bx(i,j)
  enddo; enddo
  end

Because we are using the standard binary formate to read/write data, this method will work with arrays of any size unlike the FortranFile method.

I have realized that by sticking to direct access files in Fortran, we can have wider compatibility with other languages e.g. Python, IDL etc. This way we do not have to worry about the weird leading trailing markers, endian-ness etc.

I hope this will help someone else in my situation too.

Upvotes: 1

Emmet
Emmet

Reputation: 6411

I've never had to do anything with Fortran and Python at the same time, and know absolutely nothing about fortranfile.py, but my best guess is that fortranfile is not numpy-aware.

When you use 1D numpy arrays, or Python arrays, lists, etc. the iteration in the last part of the stack trace suggests that it's expecting an iterable of numbers ("for r in reals"), whereas when you try to serialize a 2D numpy array, I'm not sure what it gets (i.e. what "r" ends up being), maybe an iterable of iterators, or an iterable of 1D arrays. In short, 'r' is not just the number that's expected ("required argument is not a float"), but something else (like a 1D array, list, etc.).

I would try have a look and see if there's an alternative to writeReals() in fortranfile and, if not, hack one in that can handle 2D arrays with a bit of copypasta.

I would start by putting in a diagnostic print before that "self.write()" line (217) that tells you what 'r' actually is, since it isn't the expected float.

Upvotes: 0

Related Questions