Чтение Фортрана с юнитов из Python - PullRequest
0 голосов
/ 25 марта 2019

Я ищу работу со старыми подпрограммами Фортрана, которые читают из файловых номеров модулей. Вот пример некоторого фиксированного формата FORTRAN77, reader.f:

      SUBROUTINE READER(IN,FVAL)
        IMPLICIT NONE
        INTEGER IN
        REAL FVAL
        PRINT *,'READER READING FROM',IN
        READ(IN,*) FVAL
        PRINT *,'READER DONE',FVAL
        RETURN
      END

Скомпилируйте это в общую библиотеку в Linux:

gfortran -fPIC -shared -o libdusty.so reader.f

А вот базовый ctypes интерфейс с библиотечным объектом в Python:

import os
from ctypes import CDLL, POINTER, c_int32, c_float, byref

c_int32_p = POINTER(c_int32)
c_float_p = POINTER(c_float)

so = CDLL('./libdusty.so')
reader = so.reader_

reader.argtypes = [c_int32_p, c_float_p]
reader.restype = None

Без оператора open Фортран читает из файла #.fort, где # - номер единицы файла. Итак, продолжая сверху, я заставляю Python написать временный файл для чтения:

fval_in = 43.21
fnum_in = 12
fname_in = 'fort.' + str(fnum_in)
print('Writing ' + fname_in)
with open(fname_in, 'w') as fp:
    fp.write(str(fval_in) + '\n')

fval_out = c_float(0)
reader(byref(c_int32(fnum_in)), byref(fval_out))
print('Received fval: ' + str(fval_out.value))
os.unlink(fname_in)

С консоли (которая получает stdout), здесь полный вывод:

Writing fort.12
 READER READING FROM          12
 READER DONE   43.2099991    
Received fval: 43.2099990845

Вопрос : необходим ли файловый объект на диске или можно использовать поток, не основанный на файлах, для чтения? Хороший кандидат - io.BytesIO, за исключением того, что он не дает fileno(). И связанный с этим вопрос: возможно ли использовать другое имя файла, отличное от fort.12 в этом примере?

1 Ответ

2 голосов
/ 26 марта 2019

Да, это может быть файл с отображенной памятью, как показано в примере ниже.Остерегайтесь, часть C основана на примерах, которые я нашел в StackOverflow, и может содержать серьезные ошибки (она, безусловно, содержит слишком много типов), а также я не совсем уверен, когда происходит запись на диск.Но вы, безусловно, можете открыть файлы других типов, например /dev/urandom или даже /dev/mem или именованные каналы , как Стив упоминал в комментарии (пример Python ниже).

Обратите внимание, что fd (c) или fileno (Python) не совпадает с файловой единицей Фортрана и не может использоваться в Фортране никоим образом.

read_from_file.f90

      SUBROUTINE READER(IN,FVAL)
        IMPLICIT NONE
        INTEGER IN
        REAL FVAL
        PRINT *,'READER READING FROM',IN
        READ(IN,*) FVAL
        PRINT *,'READER DONE',FVAL
        RETURN
      END

implicit none

integer :: iu

character(256) :: fname
real fval
call get_command_argument(1,value=fname)
open(newunit=iu, file=fname,access="stream", form="formatted")

call READER(iu, fval)
print *,fval

close(iu)
end

mmap.c

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define SIZE 10

void* init(const char *file_path) {
    int fd = open(file_path, O_RDWR);

    if (fd < 0) {
        perror("Could not open file for memory mapping");
        exit(1);
    }

    int result = lseek(fd, SIZE-1, SEEK_SET);
    if (result == -1) {
        close(fd);
        perror("Error calling lseek() to 'stretch' the file");
        exit(EXIT_FAILURE);
    }

    result = write(fd, "", 1);
    if (result != 1) {
        close(fd);
        perror("Error writing last byte of the file");
        exit(EXIT_FAILURE);
    }    

    void *start_ptr = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    unsigned char *mem_buffer = (unsigned char *) start_ptr;

    if (mem_buffer == MAP_FAILED) {        
        exit(1);
    }

    printf("Successfully mapped file.\n");
    return start_ptr;
}

void unmap(void *start_ptr) {


    if (munmap(start_ptr, SIZE) < 0) {
        exit(1);
    }


    printf("Successfully unmapped file.\n");
}

int main(){
  char *ptr;
  ptr = (char *)init("test");
  strcpy(ptr,"42\n\0");
  system("./read_from_file test");
  strcpy(ptr,"258\n\0");
  system("./read_from_file test");
  unmap(ptr);
  return 0;
}

скомпилировать и запустить

gfortran read_from_file.f90 -o read_from_file
gfortran  mmap.c

 ./a.out 
Successfully mapped file.
 READER READING FROM         -10
 READER DONE   42.0000000    
   42.0000000    
 READER READING FROM         -10
 READER DONE   258.000000    
   258.000000    
Successfully unmapped file.

Пример Python-труб:

import os

path = "/tmp/pipe"
os.mkfifo(path)
print(path+"\n")
fifo = open(path, "w")
fifo.write("345\n")
fifo.close()
os.remove(path)

shell 1:

> python fifo.py
/tmp/pipe

shell 2:

>./read_from_file /tmp/pipe
 READER READING FROM         -10
 READER DONE   345.000000    
   345.000000    
...