2015-06-30 3 views
-3

Я написал код Matlab для метода CGR, который отлично работает. Вот оно:Как правильно преобразовать код MATLAB в Fortran 95 или выше?

MATLAB КОД

% Generalized Conjugate Residual Algorithm 
% Solves M*x=b (J*dx=-F) 

function [x,converged]=gcr_Mfree(F,Cnew,Cold,Fold,alpha); 

% define global variables 
global h n M; 


%Change above 

b  = -F    ; 
tol  = 5.E-3   ; 
maxiter = length(b)  ; 
x  = zeros(maxiter,1) ; 
r  = b    ; % we need this when calculating r_new 
normr(1) = norm(r)   ; 

alpha2 = 1e-6    ; % alpha2 is the epsilon in the report! 

for iter=1:maxiter 

%Get preliminary search direction 
p(:,iter) = r ; 

%% Approximate the Jacobian(M) residual product 
%alpha2 = 1.E-6      ; 
Cnew(1:n) = Cnew(1:n)+alpha2*r   ; 
% Cnew(1) = Cold(1)      ; 
F2   = Funct2(Cnew,Cold,Fold,alpha) ; 
Mr   = 1/alpha2*(F2-F)    ;  
Mp(:,iter) = Mr       ; 

%% Orthogonalize search direction 
for j=1:iter-1 
p(:,iter) = p(:,iter) -Mp(:,j)'*Mp(:,iter)*p(:,j) ; 
Mp(:,iter) = Mp(:,iter) -Mp(:,j)'*Mp(:,iter)*Mp(:,j) ; 
end 
%norm(Mp(:,iter)) 

%Normalize search direction 
p(:,iter) = p(:,iter)/(norm(Mp(:,iter))) ; 
Mp(:,iter) = Mp(:,iter)/(norm(Mp(:,iter))) ; 


%Update solution and residual 

alpha2 = r'*(Mp(:,iter))/((Mp(:,iter))'*(Mp(:,iter))) ; 
%alpha2 
x  = x+alpha2*p(:,iter)       ; 
r  = r-alpha2*(Mp(:,iter))       ; 

%Check convergence 
normr=norm(r); 
%fprintf('norm(r) = %g iter = %g\n',normr,iter+1); 
if(normr < tol) 
%fprintf('GCR Converged, iter = %g\n',iter+1); 
converged=1; 
break; 
end 

end %for loop 

if(normr > tol) 
fprintf('SOLUTION DID NOT CONVERGE!!'); 
converged=0; 
end 

А вот моя последняя версия Fortran кода:

SUBROUTINE gcr_Mfree(F2 ,Cnew,Cold,C_Fold,IGG,JGG,x ,converged) 

     !**** FUNCTIONS TO BE SOLVED **** 
     ! Generalized Conjugate Residual Algorithm 
     ! Solves M*x=b (J*dx=-F) 


     ! Use somemodule 
     ! Arguments declarations 
     IMPLICIT REAL*8 (A-H,O-Z) 

     INTEGER :: IGG,JGG 
     real*8, dimension(:), ALLOCATABLE :: x 
     real*8, intent(out)     :: converged !< 
     REAL*8, DIMENSION(:,:), ALLOCATABLE :: F2,F22,Cnew,Cold,C_Fold,p,Mp 

     ! Variable declarations 
     real*8 :: tol 
     real*8, DIMENSION(:), ALLOCATABLE :: r,b,Mr,F2V,F22V,CnewV,ColdV,C_FoldV,alpha2 
     integer :: i,j,maxiter,normr,iter 

     MASK = SIZE(F2) 
     ALLOCATE(F2V(1:MASK)) 
     ALLOCATE(CnewV(1:MASK)) 
     ALLOCATE(ColdV(1:MASK)) 
     ALLOCATE(C_FoldV(1:MASK)) 
     !*********** RESHAPING MATRICES TO VECTORS......... 
     F2V = RESHAPE(F2 ,(/MASK/)) 
     CnewV = RESHAPE(Cnew,(/MASK/)) 
     ColdV = RESHAPE(Cold,(/MASK/)) 
     C_FoldV = RESHAPE(C_Fold,(/MASK/)) 

     b  = -F2V     !(why minus?) 
     tol  = 5.E-2 
     alpha2 = 1e-6 ! alpha2 is the epsilon in the report! 
     maxiter = size(b) 

     DEALLOCATE(x) 
     ALLOCATE(x(1:maxiter)) 
     x  = 0.0D0  !GUIdE: ""m2f: x = zeros(maxiter,1)"" 


     r  = b ! we need this when calculating r_new 
     ! normr(1) = norm2(r)        !!!! Norm 


     DO iter=1,maxiter 

      !Get preliminary search direction 

      DO i=1,iter 
       p(:,i) = r 
      ENDDO 

      ! Approximate the Jacobian(M) residual product 
      CnewV = CnewV + alpha2*r 
      Cnew = RESHAPE(CnewV,(/IGG,JGG/)) 

      F22 = CNF2(Cnew,Cold,Fold,DT) 
      F22V= RESHAPE(F22V,(/MASK/)) 

      Mr = (1/alpha2)*(F22V-F2V)  !GUIDE: (The apporximated Jacobian matrix) 

      DO i=1,iter 
       Mp(:,i) = Mr 
      ENDDO 

      !! Orthogonalize search direction 
      do j=1,iter-1 
      !m2f: p(:,iter) = p(:,iter) - Mp(:,j)'* Mp(:,iter) * p(:,j) 
       p(:,iter) = p(:,iter) - Mp(j,:) * Mp(:,iter) * p(:,j) 

      !m2f: Mp(:,iter) = Mp(:,iter) - Mp(:,j)'* Mp(:,iter) * Mp(:,j) 
       Mp(:, iter) = Mp(:, iter) - sum(Mp(:, j) * Mp(:, iter)) * Mp(:, j) 
      end do 
      !norm(Mp(:,iter)) 

      !Normalize search direction 
      ALLOCATE(p(1:IG,1:JG)) 
      ALLOCATE(Mp(1:IG,1:JG)) 

      p(:,iter) = p(:,iter)/(norm2(Mp(:,iter))) 
      Mp(:,iter) = Mp(:,iter)/(norm2(Mp(:,iter))) 


      !Update solution and residual 


      !call gemv(Mp, r, y [,alpha][,beta] [,trans]) 
      IF(iter.EQ.1) THEN 
      SUM1 = dot_product(Mp(:,iter),r) 
      ELSE 
      DO I=1,MASK 
      SUM1 = SUM (Mp(:,MASK) * r) 
      ENDDO 
      ENDIF 

      IF ((iter.EQ.1) THEN) 
      alpha2 = SUM1/SUM(Mp(1:iter,:) * Mp(:,1:iter)) 

      x = x + alpha2 * p(:,iter) 
      r = r - alpha2 * Mp(:,iter)  ! where is the *(Cnew - C)? 


      !Check convergence 
      normr=norm2(r)  
      norm 
      !fprintf('norm(r) = !g iter = !gNewLine',normr,iter+1); 
      if (normr < tol) then 
       !fprintf('GCR Converged, iter = !gNewLine',iter+1); 
       converged=1 
       exit 
      end if 


      END DO !for loop 

      if (normr > tol) then 
      write(*,*) 'GCR SOLUTION DID NOT CONVERGE!' 
      converged=0 
      end if 

      RETURN 
      END subroutine gcr_Mfree 

Вот основные проблемы преобразования формы MATLAB в FORTRAN:

  1. Я не знаю, как найти норму vecto rs и матрицы для условий сходимости? В стандарте 2008 года есть внутреннее заявление (nomr2).

  2. Я не знаю, как правильно обновить код для расчета p, mp, alpha2?

альфа2 уравнение:

alpha2 = r'*Mp(:,iter))/(Mp(iter,:) * Mp(:,iter)) 

alpha2 definition in MATLAB when iter = 2 
    alpha2 = (Nx1)'*(Nx2)/(Nx2)' * (Nx2) 
       (1x2) / (2x2) 
         (1x2) 

Я нашел gemv функции, и это звено, которое описывает вектор для матричных операций массива.

gemv function

+0

Пожалуйста, более конкретно ... Вектор представляет собой 1D массив, понятие строк и столбцов не применяется здесь! Для 2D-массива вы можете иметь массивы 1xN и Nx1, которые будут соответствовать вектору строки и столбца соответственно. Но тогда вы снова можете использовать 'transpose()'. 'forall' - это просто конструктор/оператор для назначения значений массиву. –

+0

@AlexanderVogt RHS моего уравнения - это скаляр, который является массивом нулевого ранга. LHS состоит из сложного векторного умножения, которое должно давать скаляр в конце. В MATLAB просто я могу использовать ** ** или ** транспонирование **. Но в Фортране это чертовски сложно ... – Vahid

+0

Разве это не только '' dot_product() ') (https://gcc.gnu.org/onlinedocs/gfortran/DOT_005fPRODUCT.html)? –

ответ

0

Если вы хотите использовать векторы строк и столбцов векторов с матричной записи в Fortran, необходимо выделить их в качестве (n,1) и (1,n) массивов. Например,

real :: x(10,1), A(10,10), chisq 
chisq = matmul(transpose(x),matmul(A,x)) 

1-D массив не является ни строкой, ни вектором столбца, это всего лишь массив. Но это, как правило, все, что вам нужно. Приведенный выше пример может быть написано, как это с 1-D массивов:

real :: x(10), A(10,10), chisq 
chisq = sum(x*matmul(A,x)) 

Массивы не растут автоматически, так что проще всего сделать, в общем-то, чтобы выделить его с максимальным размером необходимой, если вы знаете, что это будет. Однако в вашем случае похоже, что вы не ссылаетесь на предыдущие значения iter в своем алгоритме. Если Mp - это просто рабочая переменная и не интересна сама по себе, то вы можете уйти с наличием только одного столбца в Mp и обновлением этого столбца вместо добавления новых столбцов.

1

Ниже приведен пример конвертированного кода «нулевого порядка». Поскольку я не очень хорошо разбираюсь в синтаксисе Matlab, код может содержать некоторые ошибки или не может точно отражать исходный код (я ссылался на this tutorial), но я думаю, что он по крайней мере будет полезен в качестве отправной точки для более точного преобразования. .

subroutine gcr_Mfree(F, Cnew, Cold, Fold, alpha, x, converged) 
    !! Generalized Conjugate Residual Algorithm 
    !! Solves M*x=b (J*dx=-F) 

    !! (M(:,:) does not seem to be used in the code... 
    !! Funct2() may be multiplying M(:,:) inside.) 

    use my_module, only: h, n, M 
    use iso_fortran_env, only: dp => real64 
    implicit none 

    !! Dummy arguments.                
    real(dp), dimension(n) :: F, Cnew, Cold, Fold, x 
    real(dp) :: alpha 
    integer :: converged 

    !! Locals.                  
    integer :: iter, maxiter, j 
    real(dp) :: alpha2, tol, normr 
    real(dp), allocatable :: b(:), r(:), F2(:), Mr(:), p(:,:), Mp(:,:) 

    !! This interface block should be deleted when Funct2() is defined in some module. 
    interface 
     function Funct2(Cnew, Cold, Fold, alpha) result(ret) 
      import dp, n 
      real(dp) :: Cnew(n), Cold(n), Fold(n), alpha, ret(n) 
     endfunction 
    endinterface 

    allocate(b(n), r(n), F2(n), Mr(n), & 
       p(n, maxiter), Mp(n, maxiter)) 

    b(:)  = -F(:) 
    tol  = 5.e-3_dp 
    maxiter = size(b) 
    x(:)  = 0.0_dp 
    r(:)  = b(:) 
    normr = norm2(r) !! or sqrt(sum(r(:)**2)) if norm2() not available  
    alpha2 = 1e-6_dp 

    do iter = 1, maxiter 

     !! Get preliminary search direction           
     p(:,iter) = r(:) 

     !! Approximate the Jacobian(M) residual product        
     Cnew(:) = Cnew(:) + alpha2 * r(:) 
     F2(:)  = Funct2(Cnew, Cold, Fold, alpha)   
     Mr(:)  = 1/alpha2 * (F2(:) - F(:)) 
     Mp(:,iter) = Mr(:) 

     !! Orthogonalize search direction           
     do j = 1, iter-1 
      p(:,iter) = p(:,iter) - sum(Mp(:,j) * Mp(:,iter)) * p(:,j) 
      Mp(:,iter) = Mp(:,iter) - sum(Mp(:,j) * Mp(:,iter)) * Mp(:,j) 
     enddo 

     !! Normalize search direction            
     p(:,iter) = p(:,iter)/norm2(Mp(:,iter)) 
     Mp(:,iter) = Mp(:,iter)/norm2(Mp(:,iter)) 

     !! Update solution and residual            
     alpha2 = sum(r(:) * Mp(:,iter))/sum(Mp(:,iter)**2) 
     x(:) = x(:) + alpha2 * p(:,iter) 
     r(:) = r(:) - alpha2 * Mp(:,iter) 

     !! Check convergence               
     normr = norm2(r) 

     if (normr < tol) then 
      converged = 1 
      return 
     endif 

    enddo !! iter                 

    if(normr > tol) then 
     print *, "SOLUTION DID NOT CONVERGE!!" 
     converged = 0 
    endif 

endsubroutine 

Некоторые подробности:

в соответствии с вышеуказанным Matlab учебник, столбцов и строк векторов различают в Matlab, с одиночной кавычки ('), представляющей transpose, * представляет matrix multiplication и colon notation кажется очень аналогично Fortran.

Напротив, как поясняется в Комментариях, нет различия между векторами столбцов и строк в Fortran. (Что касается создания Nx1 или 1xN 2-мерных массивов, см. Другой ответ.). Кроме того, * представляет собой умножение по элементам, а не умножение матрицы, поэтому для вычисления внутреннего продукта необходимо sum() или dot_product().

Кроме того, матрицы в Fortran не изменяют свой размер динамически (например, увеличивая количество столбцов или строк на лету), поэтому им необходимо заранее назначить заданный размер (где размер выделения, конечно, может быть определен динамически) ,

Призыв Funct2() может потребоваться предостережение. Если он определен внутри некоторого модуля, тогда должен быть предусмотрен блок интерфейса не (поскольку он автоматически обрабатывается компилятором). Если Funct2() определяется внешними модулями, нам нужно написать блок интерфейса вручную, как показано выше. Здесь я предполагаю Funct2() определяется следующим образом:

function Funct2(Cnew, Cold, Fold, alpha) result(ret) 
    use iso_fortran_env, only: dp => real64 
    use my_module, only: n, M 
    implicit none 
    real(dp) :: Cnew(n), Cold(n), Fold(n), alpha 
    real(dp) :: ret(n) 

    !! do some multiplication by M(:,:)... 
endfunction 
Смежные вопросы