LINUX.ORG.RU

Fortran 2003: user-defined io в GCC

 , fortran 2003, ,


1

2

Как известно, в GCC-7.1.0 появилась поддержка «user-defined io» стандарта Fortran 2003. На сайте Intel есть несколько примеров. С большинством из которых всё понятно и они даже собираются gfortran-7.3.0 и работают.

Проблема наблюдается с третьим примером:

! PROGRAM: udio_nml_read_write.f90
!
! This program tests NAMELIST READ and WRITE.  In the WRITE subroutine, there
! are FORMATTED WRITES as well as NAMELIST WRITES.
!
MODULE UDIO
  TYPE MYDT
     INTEGER F1
     INTEGER F2
   CONTAINS
     PROCEDURE :: MYSUBROUTINE
     GENERIC :: READ (FORMATTED) => MYSUBROUTINE
  END TYPE MYDT

  INTERFACE WRITE (FORMATTED)
     MODULE PROCEDURE :: WRITESUBROUTINE
  END INTERFACE
CONTAINS

  SUBROUTINE WRITESUBROUTINE (DTV, UNIT, IOTYPE, V_LIST, IOSTAT, IOMSG)
    CLASS (MYDT),       INTENT(IN)      :: DTV
    INTEGER*4,          INTENT(IN)      :: UNIT
    CHARACTER (LEN=*),  INTENT(IN)      :: IOTYPE
    INTEGER,            INTENT(IN)      :: V_LIST(:)
    INTEGER*4,          INTENT(OUT)     :: IOSTAT
    CHARACTER (LEN=*),  INTENT(INOUT)   :: IOMSG
 
    INTEGER I, J
    NAMELIST /SUBRT_NML/ I, J
 
    I=DTV%F1
    J=DTV%F2
 
    WRITE (UNIT, '(A,2I5.2)', IOSTAT=IOSTAT) IOTYPE, DTV%F1, DTV%F2
    WRITE (UNIT, NML=SUBRT_NML)
  END SUBROUTINE WRITESUBROUTINE
 
SUBROUTINE MYSUBROUTINE (DTV, UNIT, IOTYPE, V_LIST, IOSTAT, IOMSG)
    CLASS (MYDT),       INTENT(INOUT)   :: DTV
    INTEGER*4,          INTENT(IN)      :: UNIT
    CHARACTER (LEN=*),  INTENT(IN)      :: IOTYPE
    INTEGER,            INTENT(IN)      :: V_LIST(:)
    INTEGER*4,          INTENT(OUT)     :: IOSTAT
    CHARACTER (LEN=*),  INTENT(INOUT)   :: IOMSG
 
! X and Y are aliases for DTV%F1 and DTV%F2 since field references
! cannot be referenced in a NAMELIST statement
 
    INTEGER X, Y
    NAMELIST /SUBRT_NML/ X, Y
 
    READ (UNIT, *) DTV%F1, DTV%F2
 
    X = DTV%F1
    Y = DTV%F2
 
    READ (UNIT, NML=SUBRT_NML, IOSTAT=IOSTAT)
  
END SUBROUTINE MYSUBROUTINE

END MODULE UDIO

PROGRAM UDIO_PROGRAM
   USE UDIO
   TYPE (MYDT) :: MYDTV
   INTEGER        :: A, B
   NAMELIST /MAIN_NML/ A, MYDTV, B

   OPEN (10, FILE='udio_nml_read_write.in')
   READ (10, NML=MAIN_NML)
   WRITE (6, NML=MAIN_NML)
   CLOSE (10)
 
END PROGRAM UDIO_PROGRAM

На вход подаётся файл «'udio_nml_read_write.in»:
&MAIN_NML
A=100
MYDTV=20 30
&SUBRT_NML
X=20
Y=30
/
/B=200
/

На выходе должно быть:
&MAIN_NML
 A       =         100,
 MYDTV=NAMELIST   20   30
&SUBRT_NML
 I       =          20,
 J       =          30
/
 /B       =         200
/

Но с gcc-7.3.0 на выходе получается
&MAIN_NML
 A=        100,
 MYDTV=NAMELIST   20   30&SUBRT_NML
 I=         20,
 J=         30,
 /
 B=          0,
 /

, либо вместо «B = 0» может быть произвольное число, то есть словно «B» осталась неинициализированной.

Если во входном файле вместо строки " /B = 200" оставить только «B = 200» и добавить в конце пустую строку, то тогда в выводе будет «B = 200».

Но интересует первоначальный вариант входного файла (если он правильный, конечно).

Есть ли кто, из тех у кого есть intel fortran compiler (2016 и новее, кажется), проверить что выводит пример в результате отработки?
И главное, как этот пример вообще работает? Пока я это не очень понимаю. То есть, например, не совсем понятно как он считывает значения «20» и «30» второй раз и куда? И куда он считывает остальной текст или там во входных данных уже какой-то стандартный «текстовый шаблон» вбит и он его подставляет? Если что-то в «текстовых строках» поменять, то во время исполнения пример упадёт. А если поменять значения «X» и «Y», то они ни на что не влияют (на вывод, то есть словно они и не нужны).

★★★★★

У меня выходит так:

sshestov@somewhere:~/test> ./a.out
 &MAIN_NML
 A       =         100,
 MYDTV= NAMELIST   00   30
&SUBRT_NML
 I       =           0,
 J       =          30
 /B       =         200
 /

версия такая:

ifort (IFORT) 17.0.0 20160721
Copyright (C) 1985-2016 Intel Corporation.  All rights reserved.

в коде не разбирался... тупо скопировал-запустил

sshestov
()
Ответ на: У меня выходит так: от sshestov

при этом содержимое входного файла

sshestov@somewhere:~/test> cat udio_nml_read_write.in
&MAIN_NML
A=100
MYDTV=20 30
&SUBRT_NML
X=20
Y=30
/
/B=200
/
sshestov
()

Оффтоп

А существует более-менее распространенный сейчас софт, написанный на фортране? Аж интересно посмотреть на такое.

xDShot ★★★★★
()
Последнее исправление: xDShot (всего исправлений: 1)
Ответ на: У меня выходит так: от sshestov

Странно, что одну «/» пропустило, но всё равно лучше, хотя бы «B = 200» присвоил и вывел. Из документации нашёл только описание на сайте IBM, но на структуру входного файла это не проливает свет, почему он должен быть именно таким.

Проверю ещё на GCC-8.1 и попробую оформить баг, пусть разбираются, как на самом деле должно быть.


xDShot, конечно! на любой вкус и много где ещё, для чего он разрабатывался и продолжает разрабатываться. Осенью должны опубликовать стандарт 2018, в котором в основном продолжили улучшать части связанные с распараллеливаниеми с взаимодействием с C.

Для совсем фанатов есть Fortran Web Framework. Из совсем уж распространённого есть BLAS и LAPACK.

grem ★★★★★
() автор топика
Ответ на: Оффтоп от xDShot

ну вот в частности для МГД симуляций

широко используют. Софт относительно свежий, проекты стартовали после 2000 года. На фортране веьма просто и быстро, компилируемо и поддержка всяких MPI завезена давно.

Я сам недавно кое-какую дифракцию считал с использованием FFT (fftw). Так на фортране оказалось это сделать быстро и просто (почти как на IDL), комплексные числа из коробки работают, тот же MPI не сложен. А вот со строками (для имени выходного файла) намучался, да, было дело.

sshestov
()
Ответ на: ну вот в частности для МГД симуляций от sshestov

Для строк есть внешние библиотеки
https://github.com/szaghi/StringiFor

и
http://www.megms.com.au/aniso_varying_string.htm
Первая из них чуть богаче возможностями.

grem ★★★★★
() автор топика
Последнее исправление: grem (всего исправлений: 1)
Ответ на: комментарий от grem

Благодарю

Хотя и сомневаюсь, что в ближайшее время мне это понадобится.

При этом был бы благодарен за разжевывание, как подключать MKL библиотеки

sshestov
()
Ответ на: Благодарю от sshestov

Сам пока ими не пользовался, но крайний раз была как раз обработка строк у меня на фортране (нужно было написать модуль к уже написанному коду).

C MKL, к сожалению, не могу подсказать :(

grem ★★★★★
() автор топика
Ответ на: Благодарю от sshestov

В MKL есть примеры, солвер для уравнения Пуассона, например, посмотри там. Вроде бы, ничего сложного, только за порядком линковки надо следить.

anonymous
()
Ответ на: у меня не с Пуассоном проблемы от sshestov

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

grem ★★★★★
() автор топика
Ответ на: комментарий от grem

в той документации на MKL, которую я

нашел было описание только для С-функций (или примеры для С, я не очень чувствую разницу что-где должно быть во время ?линковки?). По идее их использовать можно и необходимы всего-лишь интерфейсы, но у меня не пошло. Да нужно на тот момент мне было всего-лишь fft, который есть в fftw (там с примерами).

А со строками история следующая: так как у них фиксированная длина и все, нельзя сделать что-нибудь типа

char(len=80) :: filename

а потом (условно) filename=«z0»+string(z0)+«_phi0»+string(phi0)+".fits" call write_fits, filename, data

нужно везде делать тримы, правильно объявлять параметры внутри процедуры и т.д.

Как несложно догадаться, такая штука (без проверок и тримов) вполне проходит на компе попроще с gfortran, но когда вроде-бы тот же самый код пускаешь на кластере с intel fortran, оно почему-то крешится :)

sshestov
()
Ответ на: комментарий от grem

Слеш служит признаком конца namelist'a, соответственно в примере namelist заканчивается перед

B = 200
что в итоге и ведет к неинициализированой переменной. Вариант

&MAIN_NML
A=100
MYDTV=20 30
&SUBRT_NML
X=20
Y=30
/
B=200
/

работает без проблем.

anonymous
()
Ответ на: комментарий от anonymous

Спасибо за пояснение, что «/» обозначает конец namelist.

Нашёл документацию об этом для GCC: Extensions to namelist.

Только для работы всё равно нужно, чтобы после последнего «/» была пустая строка, как я писал в шапке темы (иначе runtime error «end of file» имя_входного_файла). И даже в этом случае пример работает только для gnu расширения, то есть, как оказалось, не будет компилироваться с флагом "-std=f2003".

grem ★★★★★
() автор топика
Ответ на: фу блин от sshestov

В 2003 фортране можно делать примерно так:

program test_srt

  character(len=:), allocatable :: filename
  character(len=80) :: buf, z0_str, phi0_str
  real :: z0, phi0

  z0 = 2.4
  phi0 = 1.5

  write(z0_str, '(f3.1)') z0
  write(phi0_str, '(f3.1)') phi0

  buf = "z0" // "_" // trim(z0_str) // "_" // trim(phi0_str) // ".fits"

  filename = trim(buf)
  print *, "filename:[", filename, "]"

end program test_srt
вывод:
 filename:[z0_2.4_1.5.fits]

Не то чтобы сильно отличалось и стало намного удобнее, но зато есть дополнительная переменная filename, у которой длина изменяется динамически при присваивании. Внутрь процедур такая переменная тоже должна без проблем передаваться, емнип. Объявляться внутри процедуры она должна так же. Можно дописать процедуру, в уоторую будет передаваться переменная для записи имени файла (выходная переменная) и числа (входные параметры), которые будут внутри преобразовываться в строки с «нужной точностью».

grem ★★★★★
() автор топика
Последнее исправление: grem (всего исправлений: 1)
Ответ на: комментарий от grem

Как-то так для функции или процедуры

program test_srt

  implicit none

  character(len=:), allocatable :: filename, prefix, ext
  real :: z0, phi0

  z0 = 2.4
  phi0 = 1.5

  prefix = "z0"
  ext = ".fits"

  filename = f_fname(prefix, z0, phi0, ext)
  print *, "filename:[", filename, "]"

  call fname(filename, prefix, 1.9, 5.3, ext)
  print *, "filename:[", filename, "]"

  contains
    function f_fname(prefix, x, y, ext) result(file_name)

    implicit none

        character(len=:), allocatable :: file_name
        character(len=*), intent(in) :: prefix, ext
        real, intent(in) :: x, y
        character(len=80) :: buf, x_str, y_str

        write(x_str, '(f5.2)') x
        write(y_str, '(f5.2)') y

        buf = prefix // "_" // trim(adjustl(x_str)) // "_" // trim(adjustl(y_str)) // ext
        file_name = trim(buf)

    end function f_fname

    subroutine fname(file_name, prefix, x, y, ext)

    implicit none

        character(len=:), allocatable, intent(out) :: file_name
        character(len=*), intent(in) :: prefix, ext
        real, intent(in) :: x, y
        character(len=80) :: buf, x_str, y_str

        write(x_str, '(f5.2)') x
        write(y_str, '(f5.2)') y

        buf = prefix // "_" // trim(adjustl(x_str)) // "_" // trim(adjustl(y_str)) // ext
        file_name = trim(buf)

    end subroutine fname

end program test_srt

grem ★★★★★
() автор топика
Ответ на: комментарий от grem

И даже в этом случае пример работает только для gnu расширения, то есть, как оказалось, не будет компилироваться с флагом "-std=f2003".

А всё потому, что там используется integer*4, которой нет в стандарте и разделитель "::" между «module procedure» и именем процедуры. Но пример всё равно странный у них, X и Y считываются зачем-то, но нигде не выводятся.

grem ★★★★★
() автор топика
Ответ на: комментарий от grem

это понятно, я примерно

так и сделал в результате.

Просто прошло какое-то время, прежде чем допер. Ведь там компилируешь «работающий» код на кластере, ставишь его в очередь (задав процессов побольше, а значит и очередь выждать), потом он работает, и р-а-а-а-з, в самом конце отчего-то падает (не сообщая отчего).

sshestov
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.