Fortran 编程整理

01 Fortran 中的程序单元

  • main program (主程序) —— 只能从操作系统调用的最高级别程序单元;
  • function (函数) —— 从表达式调用的可执行子函数,并且总是返回单个结果;
  • subroutine (子程序)—— 一种可执行的子程序,可以就地修改多个参数,但不能在表达式中使用;
  • module(模块)—— 变量、函数和子例程定义的不可执行集合;
  • submodule(子模块)—— 扩展现有模块,用于定义只有该模块可以访问的变量和过程定义,对于更复杂的应用程序和库非常有用

1-1 submodule(子模块)

在Fortran中,子模块(submodule)是模块系统的一部分,用于帮助组织和管理大型代码库。子模块允许将模块分成多个部分,这些部分可以独立开发和维护,从而提高代码的可读性和可维护性。

下面是子模块的一些关键点和示例:

关键点

  1. 主模块:定义公共接口、类型和公共变量。
  2. 子模块:实现主模块中定义的接口和包含实际的代码逻辑。
  3. 结构:子模块依赖于主模块,子模块中的任何子程序都必须在主模块中声明。
  4. 可见性:子模块可以访问主模块中的所有公共实体。

示例

# 主模块(Module)
module my_module
  implicit none
  ! 在这里声明子模块将实现的接口
  contains
    procedure :: my_subroutine
end module my_module
# 子模块(Submodule)
submodule (my_module) my_submodule
  implicit none
  contains
    subroutine my_subroutine
      ! 子程序实现
      print *, "Hello from submodule!"
    end subroutine my_subroutine
end submodule my_submodule
# 使用子模块
program main
  use my_module
  implicit none
  call my_subroutine
end program main

详细解释:

  1. 主模块 (Module)

    • module my_module 定义了一个名为 my_module 的主模块。
    • contains 部分,声明了一个子程序 my_subroutine,但没有提供实现。这告诉编译器子程序将在子模块中定义。
  2. 子模块 (Submodule)

    • submodule (my_module) my_submodule 声明了一个名为 my_submodule 的子模块,并指定它从属于 my_module
    • contains 部分,实现了 my_subroutine 子程序。
  3. 主程序 (Main Program)

    • use my_module 使主模块 my_module 中的所有公共实体在主程序中可用。
    • call my_subroutine 调用子模块中的 my_subroutine 子程序。

这种结构允许更好的代码组织和模块化,使得大型Fortran项目更容易管理和维护。

02 子程序与函数

2-1 概述

![[Pasted image 20240522135037.png|500]]

图1 子程序与函数概述
  • Fortran中函数可以接收任意数量的输入参数,但是只能返回一个结果。如果有多个结果需要返回可以使用数组或自定义数据类型。
  • 对于没有副作用的计算,应该使用函数来完成。

在Fortran以及其他编程语言中,副作用(side effect)指的是在函数执行过程中对其外部状态(如全局变量、输入参数、I/O操作等)的修改。没有副作用的函数(即纯函数)只依赖于其输入参数,并且除了返回值外不会对程序的其他部分产生影响。这种函数的输出仅由输入决定,不会改变任何外部状态。

在Fortran中,当提到“在没有副作用时使用函数”时,通常指的是:

  1. 函数仅依赖于输入参数:函数的计算结果完全由传递给它的参数决定,没有依赖或修改外部变量。
  2. 没有修改输入参数:函数不会通过传递的参数修改外部变量的值。
  3. 没有I/O操作:函数不进行输入输出操作(如读取文件或打印输出,打印到屏幕上也是一种副作用)。

2-2 函数与子程序特性

(1)为函数结果指定不同名称

可以使用 result 属性为函数结果指定一个不同的名称,而不是直接使用函数名称:

INTEGER FUNCTION SUM(A, B)
	INTEGER, INTENT(IN) :: A, B
	SUM = A + B
END FUNCTION SUM

! 指定不同的函数返回名称
INTEGER FUNCTION SUM(A, B) RESULT(RES)
	INTEGER, INTENT(IN) :: A, B
	RES = A + B
END FUNCTION SUM

一个函数应该只做一件事。

函数适合于不会产生副作用的计算,而子例程则更适合于需要就地修改变量的情况。但要注意这只是最佳实践,而不是硬性规定,Fortran语法是允许函数中就地修改变量的。

03 stop命令后跟说明

if(grid_size <=  0) stop 'grid_size must be > 0'

04 do concurrent

在Fortran中,concurrent 是用在并行编程中的关键字,通常在 DO CONCURRENT 结构中使用。DO CONCURRENT 结构允许程序员明确表示循环迭代之间可以独立并行执行,从而提高计算效率。

4-1 do concurrent 的使用

基本示例:

program concurrent_example
  implicit none
  integer :: i
  integer, dimension(10) :: array

  ! Initialize the array
  array = 0

  ! DO CONCURRENT loop
  do concurrent (i = 1:10)
    array(i) = i * i
  end do

  ! Print the array
  print *, array
end program concurrent_example

在这个示例中,DO CONCURRENT 循环用于对数组 array 的每个元素进行平方计算。由于每次迭代之间没有数据依赖关系,编译器可以将这些迭代并行化,以提高执行效率。

带条件的并行循环

program concurrent_with_conditions
  implicit none
  integer :: i
  integer, dimension(10) :: array

  ! Initialize the array
  array = 0

  ! DO CONCURRENT loop with conditions
  do concurrent (i = 1:10)
    if (mod(i, 2) == 0) then
      array(i) = i * 2
    else
      array(i) = i * 3
    end if
  end do

  ! Print the array
  print *, array
end program concurrent_with_conditions

这个示例展示了带条件语句的 DO CONCURRENT 循环。根据 i 是否为偶数,数组 array 的元素被赋予不同的值。

注意事项:

  1. 独立性DO CONCURRENT 结构的每次迭代必须是独立的,也就是说,每次迭代之间不应有数据依赖性。
  2. 副作用:避免在 DO CONCURRENT 结构中使用具有副作用的操作(如 I/O 操作),因为这可能导致不可预测的行为。
  3. 性能:尽管 DO CONCURRENT 可以显式指示并行性,实际的性能提升仍然依赖于编译器的实现和底层硬件支持。

高级示例:

program concurrent_advanced
  implicit none
  integer :: i, j
  integer, dimension(5, 5) :: matrix

  ! Initialize the matrix
  matrix = 0

  ! DO CONCURRENT nested loop
  do concurrent (i = 1:5, j = 1:5)
    matrix(i, j) = i * j
  end do

  ! Print the matrix
  do i = 1, 5
    print *, matrix(i, :)
  end do
end program concurrent_advanced

这个高级示例展示了嵌套的 DO CONCURRENT 循环,用于初始化一个 5x5 的矩阵。每个元素 matrix(i, j) 的值为 i * j

通过使用 DO CONCURRENT,Fortran 程序可以更好地利用多核处理器的并行计算能力,从而提高计算性能。

05 纯过程(pure procedure)

在Fortran中,纯过程(pure procedure)是一类没有副作用的过程。这意味着纯过程不能更改外部变量、不能执行 I/O 操作,也不能调用任何非纯过程。纯过程的主要优点是它们更容易进行并行化和优化,因为编译器可以更好地预测其行为。

纯过程可以是子程序(subroutine)或函数(function)。要声明一个过程为纯过程,可以使用 pure 关键字。
\

纯过程的规则:

  1. 不能改变通过参数传递的任何变量的值。
  2. 不能更改模块、公共或局部变量的值。
  3. 不能执行输入/输出(I/O)操作。
  4. 不能调用非纯过程。

示例:

纯函数(Pure Function)

module math_module
  implicit none
contains
  pure function add(a, b) result(c)
    integer, intent(in) :: a, b
    integer :: c

    c = a + b
  end function add
end module math_module

在这个示例中,add 是一个纯函数,它接受两个整数参数 ab,并返回它们的和。因为它不修改任何外部状态,也不执行 I/O 操作,所以它是纯的。

纯子程序(Pure Subroutine)

module math_module
  implicit none
contains
  pure subroutine swap(a, b)
    integer, intent(inout) :: a, b
    integer :: temp

    temp = a
    a = b
    b = temp
  end subroutine swap
end module math_module

在这个示例中,swap 是一个纯子程序,它交换两个整数参数 ab 的值。尽管它修改了参数的值,但它不修改任何外部状态或执行 I/O 操作,因此它仍然是纯的。

使用纯过程

program main
  use math_module
  implicit none
  integer :: x, y, z

  x = 3
  y = 4

  z = add(x, y) ! 调用纯函数
  print *, "Sum:", z

  call swap(x, y) ! 调用纯子程序
  print *, "Swapped values:", x, y
end program main

在这个示例中,我们定义了一个主程序 main,它使用 math_module 中的纯过程。它首先调用纯函数 add 来计算两个整数的和,然后调用纯子程序 swap 来交换两个整数的值。

纯过程的优点:

  1. 可并行化:因为纯过程没有副作用,编译器可以更容易地并行化这些过程。
  2. 优化:纯过程更容易被编译器优化,因为它们的行为是确定的。
  3. 可测试性:纯过程由于没有副作用,更容易进行单元测试。

通过使用纯过程,Fortran程序可以提高代码的可靠性、可维护性和性能。

06 Fortran中的elemental 属性

在Fortran中,elemental 属性用于声明可以对数组的每个元素进行操作的过程(函数或子程序)。带有 elemental 属性的过程可以对标量和数组参数进行统一处理,而无需显式编写循环。这使得代码更简洁,并且可以利用编译器的自动向量化和并行化优化。

6-1 elemental 关键字的使用

elemental 函数

module math_module
  implicit none
contains
  elemental function square(x) result(y)
    real, intent(in) :: x
    real :: y

    y = x * x
  end function square
end module math_module

在这个示例中,square 函数使用了 elemental 关键字,可以对标量或数组的每个元素进行平方计算。

elemental 子程序

module math_module
  implicit none
contains
  elemental subroutine add_one(x)
    real, intent(inout) :: x

    x = x + 1.0
  end subroutine add_one
end module math_module

在这个示例中,add_one 子程序使用了 elemental 关键字,可以对标量或数组的每个元素加一。

使用 elemental 子程序

program main
  use math_module
  implicit none
  real :: a, b
  real, dimension(5) :: array, result_array

  ! 标量操作
  a = 3.0
  b = square(a)
  print *, "Square of a:", b

  ! 数组操作
  array = [1.0, 2.0, 3.0, 4.0, 5.0]
  result_array = square(array)
  print *, "Square of array:", result_array

  ! 对数组应用elemental子程序
  call add_one(array)
  print *, "Array after add_one:", array
end program main

在这个示例中:

  1. 对标量 a<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值