我的sinc子函数定义有什么问题?

时间:2017-02-14 06:26:32

标签: fortran

  implicit     none
  character*20 fflname,oflname
  integer      length_sgnl
  real*8       pi, dt, m, n, theta
  parameter    ( length_sgnl=11900, dt=0.01d0, m=1, n=1, pi=3.1416
 &            ,theta=0.2 )
  integer      i     
  complex*16   cj, coeff ,sgnl(1 : length_sgnl)
  real*8       t(1 : length_sgnl)
  parameter    ( cj = dcmplx(0, 1) )
  real*8       time, real_sgnl, imag_sgnl



  oflname="filtered.data"
  fflname="artificial"
  open(11, file = oflname)
  do i=1, length_sgnl
     read(11, *) time, real_sgnl, imag_sgnl
     sgnl(i) = dcmplx(real_sgnl, imag_sgnl)
     t(i) = (i*dt - m) / (2**n)
  enddo


  coeff = 0
  do i=1, length_sgnl
     coeff = coeff 
 &        + sgnl(i) * sinc (t(i)) * exp (-cj*2*pi*t(i))
  enddo

  do i=1, length_sgnl
     sgnl(i) = sgnl(i) 
 &         - coeff * sinc (t(i)) * exp (-cj*2*pi*t(i))
 &         + coeff * sinc (t(i)) * exp (-cj*2*pi*t(i))
 &         * exp (cj*theta)
  enddo


  open(12, file = fflname)
  do i=1, length_sgnl
    write(12, *) i*dt, sgnl(i)
  enddo
  close(12)


  real*8     function sinc (a)
             real*8 :: sinc, a
             if (abs(a) < 1.0d-6) then
                sinc = 1
             else
                sinc = sin(pi*a) / (pi*a)
             end if
  end function           

  stop
  end

在子定义函数sinc的最后一部分,我假设问题存在,但我不确定它究竟是什么。 gfortran注意到我没有定义sinc和a,而“end function”应该是“end program”?

1 个答案:

答案 0 :(得分:1)

我尝试将您的程序更新为符合标准的现代Fortran:

program sinctest
  use :: iso_fortran_env
  implicit none

  ! Declare parameters
  integer,         parameter :: length_sgnl=11900
  real(real64),    parameter :: pi=3.1416, dt=0.01, m=1, n=1, theta=0.2
  complex(real64), parameter :: cj = cmplx(0, 1)

  ! Declare variables
  character(len=20) :: fflname, oflname
  complex(real64)   :: coeff, sgnl(length_sgnl)
  real(real64)      :: time, real_sgnl, imag_sgnl, t(length_sgnl)
  integer           :: i, ofl, ffl

  ! Define filenames
  oflname="filtered.data"
  fflname="artificial"

  ! Read the input file
  open(newunit = ofl, file = oflname)
  do i=1, length_sgnl
     read(ofl, *) time, real_sgnl, imag_sgnl
     sgnl(i) = cmplx(real_sgnl, imag_sgnl, kind=real64)
     t(i) = (i*dt - m) / (2**n)
  end do
  close(ofl)

  ! Process the input signal
  coeff = 0
  do i=1, length_sgnl
     coeff = coeff &
           + sgnl(i) * sinc(t(i)) * exp(-cj*2*pi*t(i))
  end do

  do i=1, length_sgnl
     sgnl(i) = sgnl(i)                              &
          - coeff * sinc(t(i)) * exp(-cj*2*pi*t(i)) &
          + coeff * sinc(t(i)) * exp(-cj*2*pi*t(i)) &
          * exp(cj*theta)
  end do

  ! Save the output file
  open(newunit = ffl, file = fflname)
  do i=1, length_sgnl
    write(ffl, *) i*dt, sgnl(i)
  enddo
  close(ffl)

contains
  pure function sinc(a) result(r)
    ! This function calculates sinc(a)=sin(pi*a)/(pi*a).
    real(real64), intent(in) :: a
    real(real64)             :: r

    if (abs(a) < 1.0e-6) then
      r = 1
    else
      r = sin(pi*a) / (pi*a)
    end if
  end function           
end program

使用例如编译它GFortran:

gfortran -std=f2008 -ffree-form sinctest.f

这些是我修复的语法错误:

  • 在定义sinc-function之前添加了contains部分;
  • 将续行字符(&)从续行的开头移到上一行的末尾;

这些不是必需的更改,仅仅是样式建议:

  • 使用内在模块iso_fortran_env获取real64变量,这样您就可以将变量定义为real(real64)而不是real*8,因为前者是可移植的,而后者是不;
  • 将变量类型(例如real)和parameter的规范合并为一行;
  • 将Fortran2008 newunit参数用于open而不是单位数字中的硬编码,因为如果您编写大型程序并拥有现代编译器,这会让您感到头疼;
  • 确保您也关闭输入文件;
  • 声明你的sinc-function为pure,因为它没有副作用;
  • 对您的sinc函数使用result表示法,这样您就不必在函数名前面指定类型real*8;
  • program...end program而非...stop end
  • 的形式重写了该计划

修改

我还想注意,使用现代Fortran,数学本身可以使用&#39;数组符号来更加简洁地编写。和&#39;元素功能&#39;。例如,如果你定义了sinc-function:

   elemental function sinc(a) result(r)
     ! This function calculates sinc(a)=sin(pi*a)/(pi*a).
     real(real64), intent(in) :: a
     real(real64)             :: r

     if (abs(a) < 1.0e-6) then
       r = 1
     else
       r = sin(pi*a) / (pi*a)
     end if
   end function

然后elemental关键字表示如果将sinc函数应用于数组,它应返回一个新数组,其中已为每个元素计算sinc函数。所以这段代码:

   coeff = 0
   do i=1, length_sgnl
      coeff = coeff &
            + sgnl(i) * sinc(t(i)) * exp(-cj*2*pi*t(i))
   end do

然后可以实际写成一行:

coeff = sum(sgnl * sinc(t) * exp(-2*pi*cj*t))

所以我强烈建议您查看现代数组符号:)。

编辑2: 试图强调哪些变化与修正错误有关,哪些变化只是风格建议(感谢Vladimir F)。