如何正确地将MATLAB代码转换为Fortran 95或更高版本?

时间:2015-06-30 19:46:56

标签: arrays matlab vector fortran

我已经为CGR方法编写了一个Matlab代码,它可以正常工作。这是:

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. 我不知道如何找到收敛条件的向量和矩阵的范数? 2008年标准有一个内在声明(nomr2)。 norm2 statement in 2008 standard

  2. 我不知道如何正确更新代码来计算p,mp,alpha2?

  3. alpha2等式:

     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

2 个答案:

答案 0 :(得分:1)

这是&#34;零序&#34;的一个例子。转换代码的版本。因为我对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中区分列和行向量,单引号(&#39;)代表transpose*代表matrix multiplication,{ {3}}似乎与Fortran非常相似。

相反,正如评论中所解释的那样,Fortran中的列向量和行向量之间没有区别。 (关于制作Nx1或1xN 2-dim阵列,请参阅另一个答案。)。此外,*表示逐元素乘法而非矩阵乘法,因此计算内积时需要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

答案 1 :(得分: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中只使用一列并更新该列而不是添加新列。