2017-01-17 3 views
1

Я пишу n-мерный численный решатель в Fortran. Я создал модуль для того же самого, который вызывается из основной программы. Я использовал external для вызова неизвестной функции, когда писал для ода первого порядка. Однако на тиражирование результат более чем в одном измерении с помощью external я получаю следующую ошибкуИспользование внешней функции в модуле

Error: EXTERNAL attribute conflicts with DIMENSION attribute in 'f_x' 

Это означает, что мой компилятор не может обрабатывать внешнюю функцию с выходом массива.

Я попытался с помощью блока интерфейса предопределить параметры функции с фиксированными размерами и я в конечном итоге с этой ошибкой

Error: PROCEDURE attribute conflicts with INTENT attribute in 'f_x' 

Есть ли способ в Fortran иметь внешнюю функцию, возвращающую массив инициализируется с помощью фиктивная функция в подпрограмме.

Вот код, который я использовал.

module int_adaptive 
implicit none 
contains 

subroutine RK4nd(f_x, norder, nvar, x_0, t_0, t_f, x_out, t_out) 
    INTERFACE 
     FUNCTION f_x (x_0, t_0) 
      integer, parameter :: dp=kind(0.d0) 
      REAL(dp), INTENT(IN) :: x_0(2, 3), t_0 
      REAL, intent(out) :: f_x(2,3) 
     END FUNCTION f_x 
    END INTERFACE 


    integer, parameter :: dp=kind(0.d0) 
    integer    :: norder, i, nvar 
    external   :: f_x 
    real(dp), intent(in):: x_0(norder, nvar), t_0, t_f 
    real(dp)   :: x_out(norder, nvar), t_out, k1(norder, nvar), y1(norder, nvar), y(norder, nvar) 
    real(dp)   :: k2(norder, nvar), k3(norder, nvar), k4(norder, nvar),& 
          y2(norder, nvar), y3(norder, nvar), y4(norder, nvar)!, f_x(norder, nvar) 
    real(dp)   :: h_min, h_0, h_max, tol, err_est, h, t 

    if (h_0<h) then 
     h = h_0 
    end if 
    if ((t_f - t_0) < 0.0) then 
     h = -h 
    end if 
    t = t_0 
    y = x_0 

    do while (t .NE. t_f) 
     k1 = f_x(y, t) 
     y1 = y + k1*h/2.0 

     k2 = f_x(y1, t+h/2.0) 
     y2 = y + k2*h/2.0 

     k3 = f_x(y2, t+h/2.0) 
     y3 = y + k3*h 

     k4 = f_x(y3, t+h) 
     y4 = y + (k1+ 2*k2 + 2*k3 +k4)*h/6.0 

     t = t + h 
     y = y4 

    end do 

    x_out = y 
    t_out = t 

end subroutine 
end module 

Я мог бы определить стандартный интегратор с входом фиксированной функции внутри модуля и назвать его по имени непосредственно, но так как я имею дело с ОДУ различных заказов будет сложно каждый раз, когда я меняю заказ я необходимо также обновить этот модуль.

ответ

3

У вас здесь две проблемы.

Первое, что ваша спецификация явного интерфейса для функции f_x является incorrect: функция результат f_x не должен иметь атрибут intent(out). Это охватывает второе сообщение об ошибке.

Сообщение первая ошибка связана с последующим использованием

external f_x 

Этот external заявление дает f_x атрибут external. Это нормально, потому что фиктивная процедура является внешней функцией. Тем не менее, вы также указали на эту процедуру явный интерфейс с (теперь исправленным) интерфейсом. Этот блок интерфейса также указывает, что процедура имеет атрибут external.

Выполнение этого, вы нарушили ограничение того, что сущность не должна быть явно указана с одним и тем же атрибутом дважды в одном блоке видимости. Чтобы устранить эту проблему, вы должны удалить одну из спецификаций. Поскольку функция возвращает результат массива, его интерфейс должен быть явным в подпрограмме, на которую он ссылается, поэтому блок интерфейса должен быть сохранен. То есть, удалите оператор external.

Для наглядности вы также должны удалить прокомментированную декларацию real(dp) f_x(norder, nvar). Такая декларация ошибочна для функции.

+0

У меня есть смысл удалить внешний блок, но это была цель, которая сделала трюк. Ошибка компилятора. Необходимо проверить его. Большое спасибо – Astroynamicist

1

Как уже указывал francescalus, функция должна быть явно INTERFACEd или объявлена ​​как EXTERNAL - не обе. Использование EXTERNAL - это старомодный Fortran, поскольку в Fortran 90 была введена функциональная сопряженность, чтобы заменить потребность в EXTERNAL, которая является довольно устаревшей функцией, все еще действительной для обратной совместимости.

Кроме того, результат функции не может иметь атрибут INTENT. Функция предназначена для возврата результата «своим именем», как в y=f_x(...).Поэтому вы должны либо объявить f_x как REAL (без какого-либо атрибута INTENT), или, что еще лучше, использовать атрибут RESULT, как в объявлении функции, так и в ее интерфейсе. Использование атрибута RESULT действительно необходимо в рекурсивных функциях, но это хорошая практика, даже если функция не рекурсивна. Таким образом, интерфейс для функции должны быть записаны в виде

INTERFACE 
    FUNCTION f_x (x_0, t_0) RESULT(res) 
     INTEGER, PARAMETER :: dp=kind(0.d0) 
     REAL(kind=dp), DIMENSION(2,3), INTENT(IN) :: x_0 
     REAL(kind=dp), INTENT(IN) :: t_0 
     REAL, DIMENSION(2,3) :: res 
    END FUNCTION f_x 
END INTERFACE 

В вашей фактической f_x реализации, вы должны использовать res (или что вы хотите назвать РЕЗУЛЬТАТ) в качестве переменной, содержащей результат этой функции должен вернуться.

Обратите внимание, что я также сделал несколько изменений, которые не являются строго необходимыми, но очень рекомендуется: объявить x_0, как вы делаете, REAL(dp), INTENT(IN) :: x_0(2, 3) также старая мода Fortran (атрибут DIMENSION был введен, чтобы сделать вещи понятнее). Также некоторые версии Fortran, особенно F-стандарт, не принимают REAL(dp) и вместо этого нуждаются в REAL(kind=dp).

+0

Я не буду комментировать точку стиля предпочтения 'result', но есть что сказать. Использование «результата» (не атрибута) в блоке интерфейса не требуется для рекурсивной функции. Имя результата функции не является характеристикой (результата функции или функции), и поскольку для ссылки на функцию не является исполняемым оператором, а не результатом функции, нет необходимости иметь отдельное имя. Реализация должна иметь возможность различать их, но имена функции приводят к реализации, а интерфейс не должен совпадать, – francescalus

+0

@francescalus: согласно F-стандарту, используя 'result' (который кажется атрибутом функции для меня, независимо от того, как это официально называется), необходимо даже для нерекурсивных функций. В этом конкретном случае использование «результата» может потребоваться по другой причине: фактическим результатом функции является массив, а некоторые компиляторы не будут принимать «real, dimension (2,3) :: f_x'. Конечно, имена функции приводят к интерфейсу, и фактическая реализация не обязательно должна соответствовать, поэтому в реализации функции можно использовать 'result (res)' в интерфейсе и 'result (foo)'. – Pap

+0

@Pap спасибо за информацию, заглянем в результат с более подробной информацией, но я думал, что определение размерности явно было более ранней практикой, так как вам нужно объявлять скалярные переменные отдельно, поэтому я перешел к давлению измерений после указания переменной. Что-то новое я узнал. – Astroynamicist