├── Makefile编译 ├── Prandtl_EMPTY.dat ├── .DS_Store ├── sample_plot_test1.png ├── test1.dat ├── test2.dat ├── test3.dat ├── test4.dat ├── test5.dat ├── Prandtl.dat ├── Makefile ├── FD_SCHEME.f90 ├── FINITE_DERIVATIVE.f90 ├── convection_diffusion_main.f90 ├── ADVECTION_DIFFUSION.f90 ├── POISSON_SOLVER.f90 └── CONVECTION_DIFFUSION.f90 ├── .DS_Store ├── .gitattributes ├── 1. Hello World └── hello_world.f90 ├── 10. 并行计算 ├── coarray_parallel.f90 └── mpi_parallel.f90 ├── 2. io+声明+运算 ├── C2F.f90 ├── molar.f90 └── Sphere.f90 ├── .gitignore ├── 4. 流程控制 └── traverse.f90 ├── 7. 格式 └── format.f90 ├── 8. 读写文件 └── file.f90 ├── 3. 数组+字符串 └── dict.f90 ├── 5. 子例程+函数+代码结构 ├── mean_std_module.f90 ├── Finite_Derivative.f90 └── main.f90 ├── LICENSE ├── 9. 指针 └── pointer.f90 ├── 6. 面向对象-类(结构体) └── polar.f90 └── README.md /Makefile编译/Prandtl_EMPTY.dat: -------------------------------------------------------------------------------- 1 | &inputs 2 | / 3 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zang-langyan/Fortran-Tutorial-CN/HEAD/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /1. Hello World/hello_world.f90: -------------------------------------------------------------------------------- 1 | program hello_world 2 | print *, "Hello, World!" 3 | end program hello_world -------------------------------------------------------------------------------- /Makefile编译/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zang-langyan/Fortran-Tutorial-CN/HEAD/Makefile编译/.DS_Store -------------------------------------------------------------------------------- /Makefile编译/sample_plot_test1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zang-langyan/Fortran-Tutorial-CN/HEAD/Makefile编译/sample_plot_test1.png -------------------------------------------------------------------------------- /10. 并行计算/coarray_parallel.f90: -------------------------------------------------------------------------------- 1 | program coarray_parrallel 2 | 3 | print *, 'Hello from processor ', this_image(), ' of ', num_images() 4 | 5 | end program coarray_parrallel -------------------------------------------------------------------------------- /Makefile编译/test1.dat: -------------------------------------------------------------------------------- 1 | &inputs 2 | Pr=10 3 | nx=257, ny=65 4 | total_time=0.1 5 | Ra=1.e6 6 | err=1.e-3 7 | a_dif=0.15 8 | a_adv=0.4 9 | Tinit='cosine' 10 | / 11 | -------------------------------------------------------------------------------- /Makefile编译/test2.dat: -------------------------------------------------------------------------------- 1 | &inputs 2 | Pr=1 3 | nx=257, ny=65 4 | total_time=0.1 5 | Ra=1.e6 6 | err=1.e-3 7 | a_dif=0.15 8 | a_adv=0.4 9 | Tinit='cosine' 10 | / 11 | -------------------------------------------------------------------------------- /Makefile编译/test3.dat: -------------------------------------------------------------------------------- 1 | &inputs 2 | Pr=0.1 3 | nx=257, ny=65 4 | total_time=0.1 5 | Ra=1.e6 6 | err=1.e-3 7 | a_dif=0.15 8 | a_adv=0.4 9 | Tinit='cosine' 10 | / 11 | -------------------------------------------------------------------------------- /Makefile编译/test4.dat: -------------------------------------------------------------------------------- 1 | &inputs 2 | Pr=0.01 3 | nx=257, ny=65 4 | total_time=0.1 5 | Ra=1.e6 6 | err=1.e-3 7 | a_dif=0.15 8 | a_adv=0.4 9 | Tinit='cosine' 10 | / 11 | -------------------------------------------------------------------------------- /Makefile编译/test5.dat: -------------------------------------------------------------------------------- 1 | &inputs 2 | Pr=0.001 3 | nx=257, ny=65 4 | total_time=0.1 5 | Ra=1.e6 6 | err=1.e-3 7 | a_dif=0.15 8 | a_adv=0.4 9 | Tinit='cosine' 10 | / 11 | -------------------------------------------------------------------------------- /Makefile编译/Prandtl.dat: -------------------------------------------------------------------------------- 1 | &inputs 2 | Pr=0.0001 3 | nx=257, ny=65 4 | total_time=0.1 5 | Ra=1.e6 6 | err=1.e-3 7 | a_dif=0.15 8 | a_adv=0.4 9 | Tinit='cosine' 10 | / 11 | -------------------------------------------------------------------------------- /2. io+声明+运算/C2F.f90: -------------------------------------------------------------------------------- 1 | program C2F 2 | implicit none 3 | 4 | real :: c, f 5 | 6 | print *, "输入摄氏度:" 7 | read *, c 8 | 9 | f = 1.8 * c + 32.0 10 | 11 | print *, "华氏度:", f 12 | end program C2F -------------------------------------------------------------------------------- /2. io+声明+运算/molar.f90: -------------------------------------------------------------------------------- 1 | program molar_mass 2 | implicit none 3 | 4 | real, parameter :: NaCl = 58.5 ! 氯化钠的摩尔质量为58.5g/mol 5 | real :: mass 6 | real :: mol 7 | 8 | print *, '输入氯化钠质量:' 9 | read(*,*) mass 10 | 11 | mol = mass / NaCl 12 | 13 | print *, 'NaCl' 14 | print *, '摩尔量为: ', mol, ' mol' 15 | 16 | end program molar_mass -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # executables 2 | *.exe 3 | *.out 4 | 5 | # module files 6 | *.mod 7 | 8 | # object files 9 | *.o 10 | 11 | # list of executables 12 | hello_world 13 | c2f 14 | molar 15 | sphere 16 | dict 17 | traverse 18 | main 19 | polar 20 | format 21 | file 22 | *.bin 23 | data.dat 24 | *.csv 25 | pointer 26 | mpi_parallel 27 | coarray_parallel 28 | 29 | .DS_Store -------------------------------------------------------------------------------- /4. 流程控制/traverse.f90: -------------------------------------------------------------------------------- 1 | program traverse 2 | implicit none 3 | 4 | real :: x(5,10) 5 | integer :: i,j 6 | 7 | call random_number(x) 8 | 9 | row: do i = 1,5 10 | col: do j = 1,10 11 | if (x(i,j) > 0.5) then 12 | print *, "位置(", i, j, ") 的值", x(i,j) 13 | cycle row 14 | end if 15 | end do col 16 | end do row 17 | 18 | end program traverse -------------------------------------------------------------------------------- /2. io+声明+运算/Sphere.f90: -------------------------------------------------------------------------------- 1 | program sphere 2 | implicit none 3 | 4 | real, parameter :: pi = 3.1415926535897932384626433832795 5 | real :: radius, surface, volume 6 | 7 | print *, "输入球体半径:" 8 | read *, radius 9 | 10 | surface = 4. * pi * radius**2 11 | volume = 4./3. * pi * radius**3 12 | 13 | print *, "球体表面积:", surface 14 | print *, "球体体积:", volume 15 | 16 | end program sphere -------------------------------------------------------------------------------- /7. 格式/format.f90: -------------------------------------------------------------------------------- 1 | program format 2 | implicit none 3 | 4 | real :: array(10) 5 | character(len=6) :: text = 'String' 6 | 7 | print '(A3)', text ! 只能打印‘Str’ 8 | print '(A6)', text ! 可以打印‘String’ 9 | 10 | call random_number(array) 11 | ! 默认格式 12 | write(*,*) array 13 | 14 | ! 小数格式 15 | write(*,'(10F10.3)') array 16 | 17 | ! 科学计数格式 18 | write(*,'(ES10.3)') array 19 | 20 | end program format -------------------------------------------------------------------------------- /8. 读写文件/file.f90: -------------------------------------------------------------------------------- 1 | program file 2 | implicit none 3 | 4 | real :: a(10), b(10) 5 | 6 | call random_number(a) 7 | 8 | open(99, file='data.csv') 9 | write(99, *) a 10 | close(99) 11 | 12 | open(98, file='data.dat') 13 | read(98, *) b 14 | close(98) 15 | 16 | print *, "b: ", b 17 | 18 | open(100, file='data.bin', access='stream', form='unformatted') 19 | write(100) a 20 | close(100) 21 | 22 | end program file -------------------------------------------------------------------------------- /10. 并行计算/mpi_parallel.f90: -------------------------------------------------------------------------------- 1 | program mpi_parrallel 2 | use mpi 3 | implicit none 4 | 5 | integer :: ierr, num_processors, my_processor_id 6 | 7 | call MPI_INIT(ierr) 8 | call MPI_COMM_SIZE(MPI_COMM_WORLD, num_processors, ierr) 9 | call MPI_COMM_RANK(MPI_COMM_WORLD, my_processor_id, ierr) 10 | 11 | print *, 'Hello from processor ', my_processor_id, ' of ', num_processors 12 | 13 | call MPI_FINALIZE(ierr) 14 | 15 | end program mpi_parrallel -------------------------------------------------------------------------------- /3. 数组+字符串/dict.f90: -------------------------------------------------------------------------------- 1 | program dict 2 | implicit none 3 | 4 | character(len=20), dimension(2) :: keys 5 | character(len=20), dimension(3,2) :: vals 6 | 7 | keys = [character(len=20) :: '姓名', '学号'] 8 | vals(1,:) = [character(len=20) :: '张三', '001'] 9 | vals(2,:) = [character(len=20) :: '李四', '002'] 10 | vals(3,:) = [character(len=20) :: '王五', '003'] 11 | 12 | print *, keys 13 | print *, vals(1,:) 14 | print *, vals(2,:) 15 | print *, vals(3,:) 16 | 17 | end program dict -------------------------------------------------------------------------------- /5. 子例程+函数+代码结构/mean_std_module.f90: -------------------------------------------------------------------------------- 1 | MODULE STATS 2 | real, parameter :: pi = 3.141592653589293 3 | 4 | CONTAINS 5 | 6 | SUBROUTINE mean_std(n,data,mean,std) 7 | integer, intent(in) :: n 8 | real , intent(in) :: data(n) 9 | real , intent(out):: mean, std 10 | integer :: i 11 | real :: sum = 0., sum_of_squared = 0. 12 | 13 | do i = 1,n 14 | sum = sum + data(i) 15 | sum_of_squared = sum_of_squared + data(i) ** 2 16 | end do 17 | 18 | mean = sum / n 19 | std = sqrt(sum_of_squared/n - (mean ** 2)) 20 | END SUBROUTINE mean_std 21 | 22 | END MODULE STATS -------------------------------------------------------------------------------- /5. 子例程+函数+代码结构/Finite_Derivative.f90: -------------------------------------------------------------------------------- 1 | SUBROUTINE derivative (data, ndt, h, deriv) 2 | integer, intent(in) :: ndt 3 | real , intent(in) :: data(ndt), h 4 | real , intent(out):: deriv(ndt) 5 | integer :: i 6 | 7 | do i = 1,ndt-1 8 | deriv(i) = (data(i+1)-data(i))/h 9 | end do 10 | deriv(ndt) = 0. 11 | END SUBROUTINE derivative 12 | 13 | SUBROUTINE derivative2 (data, ndt, h, deriv) 14 | integer, intent(in) :: ndt 15 | real , intent(in) :: data(ndt), h 16 | real , intent(out):: deriv(ndt) 17 | integer :: i 18 | 19 | deriv(1) = 0. 20 | do i = 2,ndt-1 21 | deriv(i) = (data(i+1)-2.0*data(i)+data(i-1))/(h**2.0) 22 | end do 23 | deriv(ndt) = 0. 24 | END SUBROUTINE derivative2 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 臧朗彦 Langyan Zang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile编译/Makefile: -------------------------------------------------------------------------------- 1 | # Disable all of make's built-in rules (similar to Fortran's implicit none) 2 | MAKEFLAGS += --no-builtin-rules --no-builtin-variables 3 | 4 | # configuration 5 | FC := gfortran 6 | LD := $(FC) 7 | AR := ar -r 8 | # RM := rm -f 9 | 10 | # list of all source files 11 | SRCS := FINITE_DERIVATIVE.f90 FD_SCHEME.f90 POISSON_SOLVER.f90 ADVECTION_DIFFUSION.f90 CONVECTION_DIFFUSION.f90 12 | OBJS := FINITE_DERIVATIVE.o FD_SCHEME.o POISSON_SOLVER.o ADVECTION_DIFFUSION.o CONVECTION_DIFFUSION.o 13 | LIBR := libCODF.a 14 | 15 | PROG := convection_diffusion_main.f90 16 | EXEC := co_df 17 | 18 | # command line arguments 19 | CMLA := test 20 | 21 | .PHONY: all test run clean 22 | all: $(EXEC) 23 | 24 | $(EXEC): $(LIBR) 25 | $(LD) -o $@ $(PROG) $^ 26 | 27 | $(LIBR): $(OBJS) 28 | $(AR) $@ $^ 29 | 30 | $(OBJS): 31 | $(FC) -c $(SRCS) 32 | 33 | # define dependencies between object files 34 | ADVECTION_DIFFUSION.o: FINITE_DERIVATIVE.o FD_SCHEME.o 35 | 36 | # rebuild all object files in case this Makefile changes 37 | # $(OBJS): $(MAKEFILE_LIST) 38 | 39 | test: 40 | for idx in 1 2 3 4 5; do \ 41 | ./$(EXEC) $(addsuffix .dat, $(addsuffix $$idx, $(CMLA))); \ 42 | done 43 | 44 | run: 45 | ./$(EXEC) Prandtl.dat 46 | # ./$(EXEC) Prandtl_EMPTY.dat 47 | 48 | clean: 49 | $(RM) $(filter %.o, $(OBJS)) $(wildcard *.mod) $(EXEC) $(LIBR) $(wildcard *.bin) -------------------------------------------------------------------------------- /Makefile编译/FD_SCHEME.f90: -------------------------------------------------------------------------------- 1 | MODULE FD_SCHEME 2 | USE iso_fortran_env 3 | CONTAINS 4 | 5 | SUBROUTINE UPWIND_FD(a,u,h,deriv) 6 | real(real64), intent(in) :: u(:), h 7 | real(real64), intent(in) :: a(size(u)) 8 | real(real64), intent(out):: deriv(size(u)) 9 | integer :: n 10 | 11 | n = size(u) 12 | deriv = 0. 13 | 14 | if (a(1) < 0.) then 15 | deriv(1) = a(1) * (u(2)-u(1))/h 16 | end if 17 | 18 | do i = 2,n-1 19 | if (a(i) < 0.) then 20 | deriv(i) = a(i) * (u(i+1)-u(i))/h 21 | else if (a(i) > 0.) then 22 | deriv(i) = a(i) * (u(i)-u(i-1))/h 23 | end if 24 | end do 25 | 26 | if (a(n) > 0.) then 27 | deriv(n) = a(n) * (u(n)-u(n-1))/h 28 | end if 29 | END SUBROUTINE UPWIND_FD 30 | 31 | SUBROUTINE DOWNWIND_FD(a,u,h,deriv) 32 | real(real64), intent(in) :: u(:), h 33 | real(real64), intent(in) :: a(size(u)) 34 | real(real64), intent(out):: deriv(size(u)) 35 | integer :: n 36 | 37 | n = size(u) 38 | deriv = 0. 39 | 40 | if (a(1) > 0.) then 41 | deriv(1) = a(1) * (u(2)-u(1))/h 42 | end if 43 | 44 | do i = 2,n-1 45 | if (a(i) > 0.) then 46 | deriv(i) = a(i) * (u(i+1)-u(i))/h 47 | else if (a(i) < 0.) then 48 | deriv(i) = a(i) * (u(i)-u(i-1))/h 49 | end if 50 | end do 51 | 52 | if (a(n) < 0.) then 53 | deriv(n) = a(n) * (u(n)-u(n-1))/h 54 | end if 55 | END SUBROUTINE DOWNWIND_FD 56 | 57 | END MODULE FD_SCHEME -------------------------------------------------------------------------------- /9. 指针/pointer.f90: -------------------------------------------------------------------------------- 1 | program pointerdemo 2 | implicit none 3 | 4 | integer, pointer :: p0 5 | integer, target :: i0 = 99, i1 = 1000 6 | 7 | real, pointer :: p1(:) ! 数组指针直接声明为可分配(变长)数组即可 8 | real, target :: r0(5) = (/ 1,2,3,4,5 /) 9 | 10 | procedure(f), pointer :: pf 11 | 12 | real, pointer :: p_r, pp_r 13 | real, target :: r1 = 1.5, r2 = 3.14 14 | 15 | ! 1. 简单的单一变量指针 16 | print *, "1. 简单的单一变量指针" 17 | print *, 'i0 = ', i0 18 | print *, 'i1 = ', i1 19 | p0 => i0 20 | p0 = 999 ! p0指向i0,修改p0的值,i0的值也会被修改 21 | print *, 'i0 = ', i0 22 | print *, 'i1 = ', i1 23 | print *, 'p0 = ', p0 24 | p0 => i1 25 | p0 = 10000 ! 此时p0指向i1,修改p0的值,i1的值也会被修改 26 | print *, 'i0 = ', i0 27 | print *, 'i1 = ', i1 28 | print *, 'p0 = ', p0 29 | print * 30 | 31 | ! 2. 数组指针 32 | print *, "2. 数组指针" 33 | print *, 'r0 = ', r0 34 | p1 => r0 35 | p1(1:3) = 0 36 | print *, 'r0 = ', r0 ! r0的前3个元素被赋值为0 37 | print * 38 | 39 | ! 3. 函数指针 40 | print *, "3. 函数指针" 41 | pf => f 42 | print *, 'pf(2) = ', pf(2.) ! 调用f(2.) 43 | pf => g 44 | print *, 'pf(2) = ', pf(2.) ! 调用g(2.) 45 | print * 46 | 47 | ! 4. 指针的指针 48 | print *, "4. 指针的指针" 49 | print *, 'r1 = ', r1 50 | print *, 'r2 = ', r2 51 | p_r => r1 52 | pp_r => p_r 53 | print *, 'p_r = ', p_r ! p_r指向r1 54 | print *, 'pp_r = ', pp_r ! pp_r指向r1 55 | p_r => r2 56 | print *, 'p_r = ', p_r ! p_r指向r2 57 | print *, 'pp_r = ', pp_r ! pp_r仍然指向r1 58 | print * 59 | 60 | contains 61 | 62 | real function f(x) 63 | real, intent(in) :: x 64 | f = x**2 65 | end function f 66 | 67 | real function g(x) 68 | real, intent(in) :: x 69 | g = x**3 70 | end function g 71 | 72 | end program pointerdemo -------------------------------------------------------------------------------- /6. 面向对象-类(结构体)/polar.f90: -------------------------------------------------------------------------------- 1 | module coordinates 2 | implicit none 3 | private ! 将整体设为private, 只在后续有必要的地方将方法或类型设为public 4 | 5 | public :: polar_coord ! 将polar_coord类设为public,供其他程序调用 6 | 7 | real, public :: pi = 3.141592653589793238462643383279502884197169399375105820974944592307816406286 8 | 9 | type polar_coord 10 | real :: rho, theta 11 | contains 12 | procedure, public, pass(this) :: to_euler 13 | procedure, public :: to_euler_func 14 | end type polar_coord 15 | 16 | contains 17 | 18 | subroutine to_euler(euler_coord, this) 19 | class(polar_coord), intent(in) :: this 20 | real, intent(out) :: euler_coord(2) 21 | 22 | euler_coord(1) = this%rho * cos(this%theta) 23 | euler_coord(2) = this%rho * sin(this%theta) 24 | end subroutine to_euler 25 | 26 | function to_euler_func(this) result(euler_coord) 27 | class(polar_coord), intent(in) :: this 28 | real :: euler_coord(2) 29 | 30 | euler_coord(1) = this%rho * cos(this%theta) 31 | euler_coord(2) = this%rho * sin(this%theta) 32 | end function to_euler_func 33 | 34 | 35 | end module coordinates 36 | 37 | program typedemo 38 | use coordinates 39 | implicit none 40 | 41 | type(polar_coord) :: p(2) ! 定义一个polar_coord类型的数组p,长度为2 42 | real :: euler_coord(2), euler_coord_func(2) 43 | 44 | p(1)%rho = sqrt(2.0) 45 | p(1)%theta = pi / 4.0 46 | 47 | print *, "---------------------" 48 | print *, "p(1)" 49 | call p(1)%to_euler(euler_coord) 50 | print *, "call member subroutine:" 51 | print *, euler_coord 52 | 53 | euler_coord_func = p(1)%to_euler_func() 54 | print *, "call member function:" 55 | print *, euler_coord_func 56 | 57 | 58 | p(2)%rho = 2. 59 | p(2)%theta = pi / 3.0 60 | 61 | print *, "---------------------" 62 | print *, "p(2)" 63 | call p(2)%to_euler(euler_coord) 64 | print *, "call member subroutine:" 65 | print *, euler_coord 66 | 67 | euler_coord_func = p(2)%to_euler_func() 68 | print *, "call member function:" 69 | print *, euler_coord_func 70 | end program typedemo -------------------------------------------------------------------------------- /5. 子例程+函数+代码结构/main.f90: -------------------------------------------------------------------------------- 1 | program structure 2 | ! ****************** 模块 ****************** 3 | use STATS, only: pi, mean_std 4 | implicit none 5 | 6 | integer :: n_x 7 | real :: x(3), y(3), dot_product, element_wise_product(3) 8 | real :: deriv(3) 9 | 10 | real, external :: norm1 ! 外部函数 11 | 12 | real :: data_array(5), data_mean, data_std 13 | 14 | x = [1,2,3] 15 | y = [3,2,1] 16 | n_x = size(x) 17 | 18 | ! ****************** 内部 ****************** 19 | print *, '内部函数:' 20 | call vector_product(n_x, x, y, dot_product, element_wise_product) 21 | 22 | print *, 'x · y = ', dot_product 23 | print *, 'element wise product = ', element_wise_product 24 | 25 | print *, '||x||_2 = ', vector_norm(n_x, x) 26 | print *, '' 27 | 28 | ! ****************** 外部 ****************** 29 | print *, '外部函数:' 30 | print *, '||x|| = ', norm1(n_x, x) 31 | print *, '' 32 | 33 | ! ****************** 单独文件存放 ****************** 34 | print *, '其他文件(derivative):' 35 | call derivative(x, n_x, 0.5, deriv) 36 | print *, deriv 37 | print *, '' 38 | 39 | ! ****************** 模块 ****************** 40 | print *, '模块(STATS):' 41 | data_array = [pi, 2*pi, 3*pi, 4*pi, 5*pi] 42 | call mean_std(5, data_array, data_mean, data_std) 43 | print *, '均值:', data_mean 44 | print *, '标准差:', data_std 45 | 46 | contains 47 | 48 | subroutine vector_product(n,a,b,dot_pro,element_wise_pro) 49 | integer, intent(in) :: n 50 | real, intent(in) :: a(n), b(n) 51 | real, intent(out) :: dot_pro, element_wise_pro(n) 52 | 53 | integer :: i 54 | 55 | dot_pro = 0. 56 | do i = 1,n 57 | dot_pro = dot_pro + a(i) * b(i) 58 | element_wise_pro(i) = a(i) * b(i) 59 | end do 60 | 61 | end subroutine vector_product 62 | 63 | function vector_norm(n,vec) 64 | integer, intent(in) :: n 65 | real, intent(in) :: vec(n) 66 | real :: vector_norm 67 | 68 | vector_norm = sqrt(sum(vec**2)) 69 | 70 | end function vector_norm 71 | 72 | end program structure 73 | 74 | real function norm1(n,vec) 75 | integer, intent(in) :: n 76 | real, intent(in) :: vec(n) 77 | 78 | norm1 = sum(abs(vec)) 79 | 80 | end function norm1 81 | -------------------------------------------------------------------------------- /Makefile编译/FINITE_DERIVATIVE.f90: -------------------------------------------------------------------------------- 1 | MODULE FINITE_DERIVATIVE 2 | USE iso_fortran_env 3 | CONTAINS 4 | 5 | SUBROUTINE FORWARD_FD(data,h,deriv) 6 | real(real64), intent(in) :: data(:),h 7 | real(real64), intent(out):: deriv(size(data)) 8 | integer :: n,i 9 | 10 | n = size(data) 11 | do i=1,n-1 12 | deriv(i) = (data(i+1)-data(i))/h 13 | end do 14 | deriv(n) = 0. 15 | END SUBROUTINE FORWARD_FD 16 | 17 | SUBROUTINE BACKWARD_FD(data,h,deriv) 18 | real(real64), intent(in) :: data(:),h 19 | real(real64), intent(out):: deriv(size(data)) 20 | integer :: n,i 21 | 22 | n = size(data) 23 | deriv(1) = 0. 24 | do i=2,n 25 | deriv(i) = (data(i)-data(i-1))/h 26 | end do 27 | END SUBROUTINE BACKWARD_FD 28 | 29 | SUBROUTINE CENTERED_FD(data,h,deriv) 30 | real(real64), intent(in) :: data(:),h 31 | real(real64), intent(out):: deriv(size(data)) 32 | integer :: n,i 33 | 34 | n = size(data) 35 | deriv(1) = 0. 36 | do i=2,n-1 37 | deriv(i) = (data(i+1)-data(i-1))/(2*h) 38 | end do 39 | deriv(n) = 0. 40 | END SUBROUTINE CENTERED_FD 41 | 42 | SUBROUTINE SECOND_FORWARD_FD(data,h,deriv) 43 | real(real64), intent(in) :: data(:),h 44 | real(real64), intent(out):: deriv(size(data)) 45 | integer :: n,i 46 | 47 | n = size(data) 48 | deriv = 0. 49 | do i=1,n-2 50 | deriv(i) = (data(i+2)-2.0*data(i+1)+data(i))/(h**2.0) 51 | end do 52 | END SUBROUTINE SECOND_FORWARD_FD 53 | 54 | SUBROUTINE SECOND_BACKWARD_FD(data,h,deriv) 55 | real(real64), intent(in) :: data(:),h 56 | real(real64), intent(out):: deriv(size(data)) 57 | integer :: n,i 58 | 59 | n = size(data) 60 | deriv = 0. 61 | do i=3,n 62 | deriv(i) = (data(i)-2.0*data(i-1)+data(i-2))/(h**2.0) 63 | end do 64 | END SUBROUTINE SECOND_BACKWARD_FD 65 | 66 | SUBROUTINE SECOND_CENTERED_FD(data,h,deriv) 67 | real(real64), intent(in) :: data(:),h 68 | real(real64), intent(out):: deriv(size(data)) 69 | integer :: n,i 70 | 71 | n = size(data) 72 | deriv = 0. 73 | do i=2,n-1 74 | deriv(i) = (data(i+1)-2.0*data(i)+data(i-1))/(h**2.0) 75 | end do 76 | END SUBROUTINE SECOND_CENTERED_FD 77 | 78 | END MODULE FINITE_DERIVATIVE -------------------------------------------------------------------------------- /Makefile编译/convection_diffusion_main.f90: -------------------------------------------------------------------------------- 1 | PROGRAM CO_DF_2D_MAIN 2 | USE CONVECTION_DIFFUSION 3 | IMPLICIT NONE 4 | 5 | integer :: nx = 257, ny = 65 6 | real(real64) :: Lx = 4., Ly = 1. 7 | real(real64) :: k = 1., Ra = 1.e6, Pr = 0.0001 8 | real(real64) :: err = 1.e-3 9 | real(real64) :: total_time = 0.1 10 | real(real64) :: a_dif = 0.28, a_adv = 0.4 11 | real(real64), allocatable :: T_init(:,:) 12 | character(len = 10) :: Tinit 13 | 14 | real(real64), allocatable :: T(:,:), W(:,:), Psi(:,:) 15 | 16 | character(len=50) :: input_file = 'Prandtl.dat', suffix 17 | character(len=50) :: T_fname, W_fname, Psi_fname 18 | namelist /inputs/ nx,ny,Lx,Ly,k,Ra,Pr,err,total_time,a_dif,a_adv,Tinit 19 | 20 | if (command_argument_count()>0) & 21 | call get_command_argument(1,input_file) 22 | open(10,file=input_file,status='old') 23 | read(10,inputs) 24 | close(10) 25 | 26 | Lx = Ly/(ny-1)*(nx-1) 27 | 28 | allocate(T_init(ny,nx),T(ny,nx),W(ny,nx),Psi(ny,nx)) 29 | 30 | ! Initialize T 31 | if (Tinit == 'cosine') then 32 | call COSINE_T(Lx,Ly,nx,ny,T_init) 33 | else 34 | call RANDOM_T(T_init) 35 | end if 36 | 37 | if (input_file(1:4) == 'test') then 38 | suffix = input_file(1:5) 39 | T_fname = 'T_' // TRIM(suffix) //'.bin' 40 | W_fname = 'W_' // TRIM(suffix) //'.bin' 41 | Psi_fname = 'Psi_' // TRIM(suffix) //'.bin' 42 | else 43 | T_fname = 'T.bin' 44 | W_fname = 'W.bin' 45 | Psi_fname = 'Psi.bin' 46 | end if 47 | 48 | ! ------------------------------------------------------------------- 49 | ! Calling Convection-Diffsion Subroutine 50 | ! ------------------------------------------------------------------- 51 | call CONVECTION_DIFFUSION_2D(Lx,Ly,nx,ny,k,Ra,Pr,a_dif,a_adv, & 52 | err,total_time,T_init,T,W,Psi, & 53 | record_all=.False., & ! record all 54 | fname_suffix=suffix & 55 | ) 56 | ! ------------------------------------------------------------------- 57 | open(20,file=T_fname,form='unformatted',access='stream') 58 | open(21,file=W_fname,form='unformatted',access='stream') 59 | open(22,file=Psi_fname,form='unformatted',access='stream') 60 | write(20) T 61 | write(21) W 62 | write(22) Psi 63 | close(20) 64 | close(21) 65 | close(22) 66 | 67 | deallocate(T,T_init,Psi,W) 68 | 69 | CONTAINS 70 | 71 | SUBROUTINE RANDOM_T(T_out) 72 | real(real64), intent(out) :: T_out(:,:) 73 | integer :: npx,npy 74 | 75 | npy = size(T_out,1) 76 | npx = size(T_out,2) 77 | 78 | call random_number(T_out) 79 | T_out(1,:) = 1. 80 | T_out(npy,:) = 0. 81 | T_out(:,1) = T_out(:,2) 82 | T_out(:,npx) = T_out(:,npx-1) 83 | END SUBROUTINE RANDOM_T 84 | 85 | SUBROUTINE COSINE_T(xmax,ymax,npx,npy,T_out) 86 | real(real64), intent(in) :: xmax,ymax 87 | integer, intent(in) :: npx,npy 88 | real(real64), intent(out) :: T_out(npy,npx) 89 | real(real64) :: dx,dy,x 90 | integer :: x_idx,y_idx 91 | real(real64), parameter :: PI = 16*ATAN(1./5.) - 4*ATAN(1./239.) 92 | 93 | dx = xmax/(npx-1) 94 | dy = ymax/(npy-1) 95 | 96 | T_out = 0. 97 | 98 | do x_idx=2,npx-1 99 | do y_idx=2,npy-1 100 | x = (x_idx-1)*dx/xmax 101 | T_out(y_idx,x_idx) = 0.5*(1.+cos(3.*PI*x)) 102 | end do 103 | end do 104 | T_out(1,:) = 1. 105 | T_out(:,1) = T_out(:,2) 106 | T_out(:,npx) = T_out(:,npx-1) 107 | 108 | END SUBROUTINE COSINE_T 109 | 110 | END PROGRAM CO_DF_2D_MAIN -------------------------------------------------------------------------------- /Makefile编译/ADVECTION_DIFFUSION.f90: -------------------------------------------------------------------------------- 1 | MODULE ADVECTION_DIFFUSION 2 | USE iso_fortran_env 3 | USE FINITE_DERIVATIVE 4 | USE FD_SCHEME 5 | CONTAINS 6 | 7 | ! ****************************************************************** 8 | SUBROUTINE VELOCITY(Stream,dx,dy,Vx,Vy) 9 | ! INPUTS: 10 | ! Stream(ny,nx) - stream function at grid points 11 | ! dx,dy - spacing at x,y direction 12 | ! OUTPUTS: 13 | ! Vx(ny,nx) - velocity at x direction 14 | ! Vy(ny,nx) - velocity at y direction 15 | 16 | real(real64),intent(in)::Stream(:,:),dx,dy 17 | real(real64),intent(out),dimension(size(Stream,1),size(Stream,2))::Vx,Vy 18 | integer :: nx,ny,i,j 19 | 20 | nx = size(Stream,2) 21 | ny = size(Stream,1) 22 | 23 | do i=1,nx 24 | call CENTERED_FD(Stream(:,i),dy,Vx(:,i)) 25 | end do 26 | 27 | do j=1,ny 28 | call CENTERED_FD(Stream(j,:),dx,Vy(j,:)) 29 | end do 30 | Vy = -Vy 31 | 32 | END SUBROUTINE VELOCITY 33 | ! ****************************************************************** 34 | 35 | ! ****************************************************************** 36 | SUBROUTINE ADVECTION(T,dx,dy,Vx,Vy,AD_out) 37 | ! INPUTS: 38 | ! T(ny,nx) - temperature state 39 | ! Vx(ny,nx) - velocity at x direction 40 | ! Vy(ny,nx) - velocity at y direction 41 | ! dx,dy - spacing at x,y direction 42 | ! OUTPUTS: 43 | ! AD_out(ny,nx) - velocity * gradient_T 44 | 45 | real(real64),intent(in) ::T(:,:),dx,dy 46 | real(real64),intent(in),dimension(size(T,1),size(T,2))::Vx,Vy 47 | real(real64),intent(out)::AD_out(size(T,1),size(T,2)) 48 | integer :: nx,ny,i,j 49 | real(real64),dimension(size(T,1),size(T,2)):: VT_x, VT_y 50 | 51 | nx = size(T,2) 52 | ny = size(T,1) 53 | 54 | do j=1,ny 55 | call UPWIND_FD(Vx(j,:),T(j,:),dx,VT_x(j,:)) 56 | end do 57 | 58 | do i=1,nx 59 | call UPWIND_FD(Vy(:,i),T(:,i),dy,VT_y(:,i)) 60 | end do 61 | 62 | AD_out = VT_x+VT_y 63 | 64 | END SUBROUTINE ADVECTION 65 | ! ****************************************************************** 66 | 67 | ! ****************************************************************** 68 | SUBROUTINE DIFFUSION(T,dx,dy,DF_out) 69 | ! INPUTS: 70 | ! T(ny,nx) - temperature state 71 | ! dx,dy - spacing at x,y direction 72 | ! OUTPUTS: 73 | ! DF_out(ny,nx) - second derivative of T 74 | 75 | real(real64),intent(in) ::T(:,:),dx,dy 76 | real(real64),intent(out)::DF_out(size(T,1),size(T,2)) 77 | integer :: nx,ny,i,j 78 | real(real64),dimension(size(T,1),size(T,2))::T_x,T_y 79 | 80 | nx = size(T,2) 81 | ny = size(T,1) 82 | 83 | do j=1,ny 84 | call SECOND_CENTERED_FD(T(j,:),dx,T_x(j,:)) 85 | end do 86 | 87 | do i=1,nx 88 | call SECOND_CENTERED_FD(T(:,i),dy,T_y(:,i)) 89 | end do 90 | 91 | DF_out = T_x+T_y 92 | 93 | END SUBROUTINE DIFFUSION 94 | ! ****************************************************************** 95 | 96 | ! ****************************************************************** 97 | SUBROUTINE ADVECTION_DIFFUSION_2D( & 98 | Lx,Ly,nx,ny,k,end_time,a_diff,a_advect, & 99 | T_init,Stream, & ! initial state and stream 100 | T & ! output 101 | ) 102 | ! INPUTS: 103 | ! Lx,Ly - x & y domain 104 | ! nx,ny - grid points 105 | ! k - kappa 106 | ! end_time - end time of the whole process 107 | ! a_diff,a_advect - parameters for time step 108 | ! T_init(ny,nx) - initial temperature state 109 | ! Stream(ny,nx) - stream function at grid points 110 | ! OUTPUTS: 111 | ! T(ny,nx) - temparature state at end_time 112 | 113 | integer, intent(in) :: nx,ny 114 | real(real64), intent(in) :: Lx,Ly,k,end_time,a_diff,a_advect 115 | real(real64), intent(in) :: T_init(ny,nx), Stream(ny,nx) 116 | real(real64), intent(out):: T(ny,nx) 117 | real(real64) :: dx,dy,dt,dt_diff,dt_advect 118 | integer :: total_time_steps 119 | integer :: t_idx 120 | real(real64) :: Vx(ny,nx),Vy(ny,nx) 121 | real(real64) :: T_temp(ny,nx) 122 | real(real64) :: AD(ny,nx), DF(ny,nx) 123 | 124 | dx = Lx/(nx-1) 125 | dy = Ly/(ny-1) 126 | 127 | call VELOCITY(Stream,dx,dy,Vx,Vy) 128 | 129 | dt_diff = a_diff*min(dx,dy)**2/k 130 | dt_advect = a_advect*min(dx/maxval(Vx),dy/maxval(Vy)) 131 | dt = min(dt_diff,dt_advect) 132 | 133 | total_time_steps = floor(end_time/dt) 134 | 135 | T_temp = T_init 136 | do t_idx=1,total_time_steps 137 | call ADVECTION(T_temp,dx,dy,Vx,Vy,AD) 138 | call DIFFUSION(T_temp,dx,dy,DF) 139 | T = T_temp + dt*(k*DF-AD) 140 | ! Boundary Conditions: 141 | ! dT/dx = 0, T(1,:) = T(ny,:) = 0 142 | T(1,:) = 1. 143 | T(ny,:) = 0. 144 | T(:,1) = T(:,2) 145 | T(:,nx) = T(:,nx-1) 146 | 147 | T_temp = T 148 | end do 149 | 150 | END SUBROUTINE ADVECTION_DIFFUSION_2D 151 | ! ****************************************************************** 152 | END MODULE ADVECTION_DIFFUSION -------------------------------------------------------------------------------- /Makefile编译/POISSON_SOLVER.f90: -------------------------------------------------------------------------------- 1 | MODULE POISSON_SOLVER 2 | USE iso_fortran_env 3 | CONTAINS 4 | 5 | ! ********************************************************************* 6 | function iteration_2DPoisson(u,f,h,alpha) result (res_rms) 7 | implicit none 8 | real(real64) :: res_rms 9 | real(real64), intent(inout) :: u(:,:) 10 | real(real64), intent(in) :: f(:,:), h, alpha 11 | integer :: i,j,nx,ny 12 | real(real64) :: res 13 | 14 | nx=size(u,1); ny=size(u,2) 15 | res_rms = 0. 16 | 17 | do concurrent (i=2:nx-1,j=2:ny-1) 18 | res = (u(i,j+1)+u(i,j-1)+u(i+1,j)+u(i-1,j)-4*u(i,j))/(h**2.0) - f(i,j) 19 | u(i,j) = u(i,j)+alpha*res*(h**2)/4.0 20 | res_rms = res_rms + res**2 21 | end do 22 | 23 | res_rms = sqrt(res_rms/(nx*ny)) 24 | end function iteration_2DPoisson 25 | ! ********************************************************************* 26 | 27 | ! ********************************************************************* 28 | subroutine residue_2DPoisson(u,f,h,res) 29 | real(real64), intent(in) :: u(:,:),f(:,:),h 30 | real(real64), intent(out):: res(:,:) 31 | integer :: i,j,nx,ny 32 | 33 | nx=size(u,1); ny=size(u,2) 34 | res = 0. 35 | 36 | do concurrent (i=2:nx-1,j=2:ny-1) 37 | res(i,j)=(u(i,j+1)+u(i,j-1)+u(i+1,j)+u(i-1,j)-4*u(i,j))/(h**2.0)-f(i,j) 38 | end do 39 | 40 | end subroutine residue_2DPoisson 41 | ! ********************************************************************* 42 | 43 | ! ********************************************************************* 44 | subroutine restrict(fine,coarse) 45 | real(real64), intent(in) :: fine(:,:) 46 | real(real64), intent(out):: coarse(:,:) 47 | integer :: nxf,nyf,nxc,nyc,i,j 48 | 49 | nxf=size(fine,1);nyf=size(fine,2) 50 | nxc=size(coarse,1);nyc=size(coarse,2) 51 | if (nxf+1/=2*nxc .or. nyf+1/=2*nyc) & 52 | stop 'ERROR: grid mismatch when coarsening!' 53 | 54 | do concurrent (i=1:nxc,j=1:nyc) 55 | coarse(i,j) = fine(2*i-1,2*j-1) 56 | end do 57 | 58 | end subroutine restrict 59 | ! ********************************************************************* 60 | 61 | ! ********************************************************************* 62 | subroutine prolongate(coarse,fine) 63 | real(real64), intent(in) :: coarse(:,:) 64 | real(real64), intent(out):: fine(:,:) 65 | integer :: nxf,nyf,nxc,nyc,i,j 66 | 67 | nxf=size(fine,1);nyf=size(fine,2) 68 | nxc=size(coarse,1);nyc=size(coarse,2) 69 | if (nxf+1/=2*nxc .or. nyf+1/=2*nyc) & 70 | stop 'ERROR: grid mismatch when prolongating!' 71 | 72 | do concurrent (i=1:nxc,j=1:nyc) 73 | fine(2*i-1,2*j-1) = coarse(i,j) 74 | end do 75 | 76 | do i=1,nxc 77 | do j=1,nyc-1 78 | fine(2*i-1,2*j) = (coarse(i,j)+coarse(i,j+1))/2.0 79 | end do 80 | end do 81 | 82 | do i=1,nxc-1 83 | fine(2*i,:) = (fine(2*i-1,:)+fine(2*i+1,:))/2.0 84 | end do 85 | 86 | end subroutine prolongate 87 | ! ********************************************************************* 88 | 89 | ! ********************************************************************* 90 | recursive function Vcycle_2DPoisson(u_f,rhs,h) result (resV) 91 | implicit none 92 | real(real64) resV 93 | real(real64),intent(inout):: u_f(:,:) ! arguments 94 | real(real64),intent(in) :: rhs(:,:),h 95 | integer :: nx,ny,nxc,nyc, i ! local variables 96 | real(real64),allocatable:: res_c(:,:),corr_c(:,:),res_f(:,:),corr_f(:,:) 97 | real(real64) :: alpha=0.7, res_rms 98 | 99 | nx=size(u_f,1); ny=size(u_f,2) ! must be power of 2 plus 1 100 | if( nx-1/=2*((nx-1)/2) .or. ny-1/=2*((ny-1)/2) ) & 101 | stop 'ERROR:not a power of 2' 102 | nxc=1+(nx-1)/2; nyc=1+(ny-1)/2 ! coarse grid size 103 | 104 | if (min(nx,ny)>5) then ! not the coarsest level 105 | 106 | allocate(res_f(nx,ny),corr_f(nx,ny), & 107 | corr_c(nxc,nyc),res_c(nxc,nyc)) 108 | 109 | !---------- take 2 iterations on the fine grid-------------- 110 | res_rms = iteration_2DPoisson(u_f,rhs,h,alpha) 111 | res_rms = iteration_2DPoisson(u_f,rhs,h,alpha) 112 | 113 | !---------- restrict the residue to the coarse grid -------- 114 | call residue_2DPoisson(u_f,rhs,h,res_f) 115 | call restrict(res_f,res_c) 116 | 117 | !---------- solve for the coarse grid correction ----------- 118 | corr_c = 0. 119 | res_rms = Vcycle_2DPoisson(corr_c,res_c,h*2) ! *RECURSIVE CALL* 120 | 121 | !---- prolongate (interpolate) the correction to the fine grid 122 | call prolongate(corr_c,corr_f) 123 | 124 | !---------- correct the fine-grid solution ----------------- 125 | u_f = u_f - corr_f 126 | 127 | !---------- two more smoothing iterations on the fine grid--- 128 | res_rms = iteration_2DPoisson(u_f,rhs,h,alpha) 129 | res_rms = iteration_2DPoisson(u_f,rhs,h,alpha) 130 | 131 | deallocate(res_f,corr_f,res_c,corr_c) 132 | 133 | else 134 | 135 | !----- coarsest level (ny=5): iterate to get 'exact' solution 136 | 137 | do i = 1,100 138 | res_rms = iteration_2DPoisson(u_f,rhs,h,alpha) 139 | end do 140 | 141 | end if 142 | 143 | resV = res_rms ! returns the rms. residue 144 | 145 | end function Vcycle_2DPoisson 146 | ! ********************************************************************* 147 | END MODULE POISSON_SOLVER -------------------------------------------------------------------------------- /Makefile编译/CONVECTION_DIFFUSION.f90: -------------------------------------------------------------------------------- 1 | MODULE CONVECTION_DIFFUSION 2 | USE iso_fortran_env 3 | USE FINITE_DERIVATIVE 4 | USE POISSON_SOLVER 5 | USE ADVECTION_DIFFUSION 6 | IMPLICIT NONE 7 | 8 | CONTAINS 9 | 10 | ! *************************************************************************** 11 | SUBROUTINE CONVECTION_DIFFUSION_2D( & 12 | Lx,Ly,nx,ny,k,Ra,Pr,a_dif,a_adv,tol,end_time, & 13 | T_init, & 14 | T, W, Psi, & 15 | record_all, fname_suffix & 16 | ) 17 | ! INPUTS: 18 | ! Lx,Ly - x & y domain 19 | ! nx,ny - grid points (nx-1 and ny-1 must be a power of 2) 20 | ! e.g. nx = 257, ny = 65 => nx-1=2^8, ny-1=2^6 21 | ! k - kappa 22 | ! Ra - Rayleigh number 23 | ! Pr - Prandtl number 24 | ! a_diff,a_advect - parameters for time step 25 | ! tol - tolerence for Poisson solver 26 | ! end_time - end time of the whole process 27 | ! T_init(ny,nx) - initial temperature state 28 | ! OUTPUTS: 29 | ! T(ny,nx) - temparature state at end_time 30 | ! W(ny,nx) - vorticity at grid points 31 | ! Psi(ny,nx) - stream function at grid points 32 | ! OPTIONAL ARGUMENTS: 33 | ! record_all - .True. then write T and Psi to file through time 34 | ! fname_suffix - file name suffix for all data through time record 35 | ! if not specified, 'T_all.bin' and 'Psi_all.bin' 36 | ! will be used 37 | real(real64), intent(in) :: Lx,Ly,k,Ra,Pr,end_time 38 | integer, intent(in) :: nx,ny 39 | real(real64), intent(in) :: a_dif,a_adv,tol 40 | real(real64), intent(in) :: T_init(ny,nx) 41 | real(real64), intent(out) :: T(ny,nx),W(ny,nx),Psi(ny,nx) 42 | logical, optional :: record_all 43 | logical :: record_all_flag 44 | character(len=50), optional :: fname_suffix 45 | character(len=50) :: Tfname, Wfname, Psifname 46 | 47 | real(real64) :: T_temp(ny,nx) 48 | 49 | real(real64) :: dx,dy 50 | real(real64) :: dt,dt_dif,dt_adv 51 | real(real64) :: current_time 52 | 53 | real(real64) :: dT_dx(ny,nx) 54 | real(real64) :: res(ny,nx),f_rms,res_rms 55 | real(real64) :: Vx(ny,nx), Vy(ny,nx), max_v 56 | 57 | real(real64) :: AD(ny,nx), DF(ny,nx), AD_W(ny,nx), DF_W(ny,nx) 58 | 59 | integer :: j,t_idx=0 60 | 61 | T_temp = T_init 62 | T = T_init 63 | dx = Lx/(nx-1) 64 | dy = Ly/(ny-1) 65 | 66 | dt_dif = a_dif*min(dx,dy)**2/max(Pr,k) 67 | dt = dt_dif 68 | 69 | ! check if record T through time 70 | if (.Not. present(record_all)) then 71 | record_all_flag = .False. 72 | else 73 | record_all_flag = record_all 74 | end if 75 | 76 | ! -------------- Iterate until end time -------------- 77 | W = 0. 78 | Psi = 0. 79 | 80 | ! ------------ File Configuration (Start) -------------- 81 | if (record_all_flag) then 82 | if (.Not. present(fname_suffix)) then 83 | Tfname = 'T_all.bin' 84 | Wfname = 'W_all.bin' 85 | Psifname = 'Psi_all.bin' 86 | else 87 | Tfname = 'T_all_'// TRIM(fname_suffix) //'.bin' 88 | Wfname = 'W_all_'// TRIM(fname_suffix) //'.bin' 89 | Psifname = 'Psi_all_'// TRIM(fname_suffix) //'.bin' 90 | end if 91 | open(97,file=Wfname,form='unformatted',access='stream') 92 | open(98,file=Tfname,form='unformatted',access='stream') 93 | open(99,file=Psifname,form='unformatted',access='stream') 94 | write(97) W 95 | write(98) T 96 | write(99) Psi 97 | end if 98 | 99 | ! Open a file for velocity record 100 | if ((.Not. present(fname_suffix)) .Or. (fname_suffix(1:4)/='test')) then 101 | open(100,file='maxv.bin',form='unformatted',access='stream') 102 | else 103 | open(100,file='maxv_'// TRIM(fname_suffix) //'.bin',& 104 | form='unformatted',access='stream') 105 | end if 106 | ! ------------ File Configuration (End) -------------- 107 | 108 | current_time = 0. 109 | do while (current_time < end_time) 110 | 111 | do j=1,ny 112 | call CENTERED_FD(T(j,:),dx,dT_dx(j,:)) 113 | end do 114 | 115 | ! W_t = W_(t-1) + dt*(Pr*d2W - advection(W) - Ra*Pr*dTdx) 116 | call ADVECTION(W,dx,dy,Vx,Vy,AD_W) 117 | call DIFFUSION(W,dx,dy,DF_W) 118 | W = W + dt*(Pr*DF_W - AD_W - Ra*Pr*dT_dx) 119 | 120 | ! W -> Psi 121 | call residue_2DPoisson(Psi,W,dy,res) 122 | res_rms = sqrt(sum(res**2)/(nx*ny)) 123 | f_rms = sqrt(sum(W**2)/(nx*ny)) 124 | do while(res_rms/f_rms >= tol) 125 | res_rms = Vcycle_2DPoisson(Psi,W,dy) 126 | end do 127 | 128 | ! Psi -> velocity 129 | call VELOCITY(Psi,dx,dy,Vx,Vy) 130 | ! record maximum velocity through time 131 | max_v = max(maxval(Vx),maxval(Vy)) 132 | write(100) current_time,max_v 133 | 134 | ! dt 135 | if (maxval(Vx) == 0. .Or. maxval(Vy) == 0.) then 136 | dt = dt_dif 137 | else 138 | dt_adv = a_adv*min(dx/maxval(Vx),dy/maxval(Vy)) 139 | dt = min(dt_dif,dt_adv) 140 | end if 141 | 142 | ! Advection_Diffusion 143 | call ADVECTION(T_temp,dx,dy,Vx,Vy,AD) 144 | call DIFFUSION(T_temp,dx,dy,DF) 145 | T = T_temp + dt*(k*DF - AD) 146 | ! Boundary Conditions: 147 | ! dT/dx = 0, T(1,:) = T(ny,:) = 0 148 | T(1,:) = 1. 149 | T(ny,:) = 0. 150 | T(:,1) = T(:,2) 151 | T(:,nx) = T(:,nx-1) 152 | 153 | ! Update T 154 | T_temp = T 155 | 156 | ! record T through time 157 | if (record_all_flag) then 158 | write(97) W 159 | write(98) T 160 | write(99) Psi 161 | end if 162 | 163 | current_time = current_time + dt 164 | t_idx = t_idx + 1 165 | 166 | ! Progress 167 | write(*,*) 'Current Iteration: ', t_idx, 'Current Time: ', current_time 168 | end do 169 | 170 | if (record_all) then 171 | close(97) 172 | close(98) 173 | close(99) 174 | end if 175 | 176 | ! close velocity record 177 | close(100) 178 | 179 | END SUBROUTINE CONVECTION_DIFFUSION_2D 180 | ! *************************************************************************** 181 | 182 | ! *************************************************************************** 183 | END MODULE CONVECTION_DIFFUSION -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fortran 基础 2 | 3 | 本教程参考了包括但不限于以下网站的内容 4 | 1. https://fortran-lang.org/ 5 | 2. https://www.fortran90.org/ 6 | 3. https://www.nsc.liu.se/~boein/f77to90/f77to90.html#index 7 | 4. https://gcc.gnu.org/onlinedocs/gcc-4.2.4/gfortran/index.html#Top 8 | 9 | - [Fortran 基础](#fortran-基础) 10 | - [一. 编译器](#一-编译器) 11 | - [二. Hello World](#二-hello-world) 12 | - [三. 标准输入和输出 (io)](#三-标准输入和输出-io) 13 | - [四. 基本数据类型](#四-基本数据类型) 14 | - [浮点数精度](#浮点数精度) 15 | - [五. 运算符](#五-运算符) 16 | - [六. 数组和字符串](#六-数组和字符串) 17 | - [数组](#数组) 18 | - [字符串](#字符串) 19 | - [字符串数组](#字符串数组) 20 | - [七. 流程控制](#七-流程控制) 21 | - [逻辑运算](#逻辑运算) 22 | - [条件语句](#条件语句) 23 | - [循环语句](#循环语句) 24 | - [序数循环](#序数循环) 25 | - [条件循环](#条件循环) 26 | - [循环控制](#循环控制) 27 | - [八. 函数与代码结构](#八-函数与代码结构) 28 | - [子例程和函数](#子例程和函数) 29 | - [代码结构](#代码结构) 30 | - [九. 面向对象 -- 类或结构体](#九-面向对象----类或结构体) 31 | - [十. 格式](#十-格式) 32 | - [十一. 文件读写](#十一-文件读写) 33 | - [十二. 指针](#十二-指针) 34 | - [十三. 并行计算](#十三-并行计算) 35 | - [MPI](#mpi) 36 | - [Coarray](#coarray) 37 | - [十四. 内置函数](#十四-内置函数) 38 | - [数值](#数值) 39 | - [数学](#数学) 40 | - [数组](#数组-1) 41 | - [十五. Makefile管理项目](#十五-makefile管理项目) 42 | 43 | 44 | ## 一. 编译器 45 | 46 | 在此列出一些常见的Fortran编译器(参考[fortran-lang.org](https://fortran-lang.org/zh_CN/compilers/)的列表) 47 | 本中文文档将使用 ***gfortran*** 作为其中案例的编译器。 48 | 关于 ***gfortran*** 的安装方法请参考[安装GFortran](https://fortran-lang.org/zh_CN/learn/os_setup/install_gfortran/) (https://fortran-lang.org/learn/os_setup/install_gfortran/) 或 [GNU GCC](https://gcc.gnu.org/wiki/GFortranBinaries) (https://gcc.gnu.org/wiki/GFortranBinaries) 页面的指导,选择符合你操作系统的安装方法。 49 | 50 | **开源编译器:** 51 | 52 | * [gfortran](https://gcc.gnu.org/fortran/): GNU Fortran 编译器,隶属于GNU Compiler Collection。[OpenCoarrays](http://www.opencoarrays.org/)是一个围绕gfortran的库和编译器包装器,以此可进行gfortran的并行编程功能 53 | * [LLVM Flang](https://github.com/llvm/llvm-project/tree/main/flang): LLVM 的 Fortran 2018 新前端. (它是用现代 C++ 实现的,并使用面向 Fortran 的 MLIR 方言来降低到 LLVM IR。该项目正在积极开发中) 54 | * [Current Flang](https://github.com/flang-compiler/flang): NVIDIA/PGI 商业编译器的开源编译器 55 | * [LFortran](https://lfortran.org/) 是一种现代的、交互式的、基于 LLVM 的 Fortran 编译器 56 | 57 | **商业编译器:** 58 | 59 | * [Intel oneAPI](https://www.intel.com/content/www/us/en/developer/tools/oneapi/toolkits.html#gs.zhoout): 是英特尔针对 Fortran、C、 C++ 和 Python。 Intel oneAPI HPC Toolkit 提供两个 Fortran 编译器:ifx(可在Intel GPUs上运行`do concurrent`) 和 ifort(仅支持CPU) (目前版本的Intel oneAPI是免费的,可购买支持) 60 | * [NAG](https://www.nag.com/content/nag-fortran-compiler): 最新的 NAG Fortran 编译器 版本 (7.0) 广泛支持传统和现代 Fortran 功能,包括使用 coarray 进行并行编程,以及对使用 OpenMP 进行编程的额外支持 61 | * [NVIDIA HPC SDK](https://developer.nvidia.com/hpc-sdk): C、C++ 和 Fortran 编译器,以前的 PGI 编译器,支持使用标准 C++ 和 Fortran、OpenACC® 指令和 CUDA® 对 HPC 建模和仿真应用程序进行 GPU 加速。 GPU 加速的数学库最大限度地提高了常见 HPC 算法的性能,优化的通信库支持基于标准的多 GPU 和可扩展系统编程。 62 | NVHPC 编译器是被免费提供的,目前编译器支持 Linux 平台和 x86_64、ppc64le 和 aarch64 架构, HPC 编译器论坛 提供社区支持。 63 | * [AMD Optimizing C/C++ Compiler (AOCC)](https://www.amd.com/en/developer/aocc.html): 编译器系统是一个高性能、生产质量的代码生成工具。在构建和优化面向 32 位和 64 位 Linux® 平台的 C、C++ 和 Fortran 应用程序时,AOCC 环境为开发人员提供了各种选项。 AOCC 编译器系统提供高级优化、多线程和处理器支持,包括全局优化、矢量化、过程间分析、循环转换和代码生成。 AMD 还提供高度优化的库,可在使用时从每个 x86 处理器内核中提取最佳性能。 AOCC 编译器套件简化并加速了 x86 应用程序的开发和调整。 64 | AOCC 编译器被免费提供,支持 x86_64 架构的 Linux 平台。 65 | 66 | 检查安装是否完成 67 | ```shell 68 | $ gfortran --version 69 | ``` 70 | 71 | --- 72 | ## 二. Hello World 73 | 74 | 创建一个Fortran脚本`hello_world.f90`,用你最喜欢的编辑器打开并写入下面的代码 75 | ```fortran 76 | program hello 77 | ! 这是一行注释 78 | print *, 'Hello, World!' 79 | end program hello 80 | ``` 81 | 82 | 保存之后,在命令行中对其进行编译 83 | ```shell 84 | $ gfortran hello_world.f90 -o hello_world 85 | ``` 86 | 87 | 编译完成后,运行 88 | ```shell 89 | $ ./hello_world 90 | ``` 91 | 92 | --------- 93 | 94 | > ℹ️ Fortran 不区分大小写,所有大写字符都会在编译之前转为小写。 95 | > 如:`real :: VAR, var, VaR` 将导致错误,编译器会认为同一变量声明了三次 96 | 97 | ## 三. 标准输入和输出 (io) 98 | 99 | 此处介绍最基本的用法,详细的用法将在[十. 格式](#十-格式)和读写文件章节中介绍 100 | 101 | ```fortran 102 | program read_value 103 | implicit none ! 添加之后所有变量必须明确声明 (可以不添加,但强烈建议添加以避免bug) - 强类型语言 104 | integer :: age 105 | 106 | print *, '请输入你的年龄: ' 107 | read(*,*) age ! 或使用 read *, age 108 | 109 | write(*,*) '年龄: ', age 110 | 111 | end program read_value 112 | ``` 113 | 114 | --- 115 | ## 四. 基本数据类型 116 | 117 | * integer 整数 118 | * real 实数 119 | * complex 复数 120 | * character 字符 121 | * logical 逻辑值 122 | 123 | > ⚠️ **_注意:_** 在使用浮点数时,即使没有小数部分,也要记住加上小数点;如:`real :: x = 2.`; 124 | > 如果没有小数点可能会导致编译器识别错误 125 | 126 | 每一个`program` 或 `subroutine` 或 `function` 中必须在开头先声明所有需要使用的变量,不可以在主体运算部分另起声明 127 | 128 | ```fortran 129 | program variables 130 | implicit none 131 | 132 | integer :: count 133 | real :: pi 134 | complex :: z 135 | character :: char 136 | logical :: isOkay 137 | 138 | count = 10 139 | pi = 3.1415927 140 | z = (1.0, -0.5) ! 1.0 - 0.5i 141 | char = 'A' ! 或使用双引号 "A" 142 | isOkay = .true. ! 否为 .false. 143 | 144 | end program variables 145 | ``` 146 | 147 | ### 浮点数精度 148 | 149 | 32位和64位,没有声明类型时一般为32位 150 | 151 | ```fortran 152 | program float_precision 153 | use, intrinsic :: iso_fortran_env, only: sp=>real32, dp=>real64 154 | implicit none 155 | 156 | real(kind=sp) :: x_32 157 | real(kind=dp) :: x_64 158 | 159 | x_32 = 1.0_sp ! 建议总是将类型后缀加在常量后 160 | x_64 = 1.0_dp 161 | 162 | end program float_precision 163 | ``` 164 | 165 | --- 166 | ## 五. 运算符 167 | 168 | * `+` 加 169 | * `-` 减 170 | * `*` 乘 171 | * `/` 除 172 | * `**` 幂 173 | 174 | ```fortran 175 | program sphere 176 | implicit none 177 | 178 | real :: pi = 3.141592653589793238 179 | real :: radius 180 | real :: surface 181 | real :: volume 182 | 183 | print *, '输入球体半径:' 184 | read(*,*) radius 185 | 186 | surface = 4. * pi * radius**2 187 | volume = 4./3. * pi * radius**3 188 | 189 | print *, '球' 190 | print *, '表面积为: ', surface 191 | print *, '体积为: ', volume 192 | 193 | end program sphere 194 | ``` 195 | 196 | `parameter` 代表常量,在运行过程中无法修改,类似于C/C++中的`const`,所以需要在声明时初始化 197 | ```fortran 198 | program molar_mass 199 | implicit none 200 | 201 | real, parameter :: NaCl = 58.5 ! 氯化钠的摩尔质量为58.5g/mol 202 | real :: mass 203 | real :: mol 204 | 205 | print *, '输入氯化钠质量:' 206 | read(*,*) mass 207 | 208 | mol = mass / NaCl 209 | 210 | print *, 'NaCl' 211 | print *, '摩尔量为: ', mol, ' mol' 212 | 213 | end program molar_mass 214 | ``` 215 | 216 | --- 217 | ## 六. 数组和字符串 218 | 219 | ### 数组 220 | 221 | > **注:** 222 | > - 数组默认从1开始序数 223 | > - 也可以自定义从任意位置开始序数 224 | > - 多维数组由前向后(维度)记录 (column major) 225 | 226 | ```fortran 227 | program arrays 228 | implicit none 229 | 230 | ! ******************** 数组声明 ******************** 231 | ! 1维整数数组 232 | integer, dimension(10) :: array1 233 | 234 | ! 也可以这样声明 235 | integer :: array2(10) 236 | 237 | ! 2维浮点数数组 238 | real, dimension(10, 10) :: array3 239 | 240 | ! 自定义序数 241 | real :: array4(0:9) ! 长度为10 242 | real :: array5(-5:5) ! 长度为11 243 | 244 | ! 可分配(动态)数组 - 变长数组 245 | real, allocatable :: array6(:,:) 246 | 247 | 248 | ! ******************** 数组初始化 ******************** 249 | array1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ! Array constructor 250 | array1 = [(i, i = 1, 10)] ! Implied do loop constructor 251 | 252 | ! ******************** 数组索引与切片 ******************** 253 | array1(:) = 0 ! 将所有元素设为0 254 | array1(1:5) = 1 ! 将前5个元素设为1 255 | array2(6:) = 1 ! 将第5个元素向后的所有元素设为1 256 | array3(1,2:3) = 0 ! 将array3的第1行第2,3列的元素设为0 257 | array4(0) = 1 ! 将array4的第1个元素设为1 258 | 259 | ! ******************** 动态数组 ******************** 260 | ! 分配大小 261 | allocate(array6(16,32)) ! 16 x 32 的数组 262 | deallocate(array6) ! 释放数组空间 263 | end program arrays 264 | ``` 265 | 266 | ### 字符串 267 | ```fortran 268 | program string 269 | implicit none 270 | 271 | ! ******************** 字符串声明 ******************** 272 | ! 定长字符串 273 | character(len=4) :: xing ! 姓 274 | character(len=5) :: ming ! 名 275 | character(10) :: name ! 姓名 276 | ! 变长字符串 (可分配字符串) 277 | character(:), allocatable :: first_name 278 | character(:), allocatable :: last_name 279 | 280 | ! ******************** 定长字符串 ******************** 281 | xing = '屈' 282 | ming = '原' 283 | 284 | ! 字符串串联 285 | name = xing//ming 286 | 287 | print *, name 288 | 289 | ! ******************** 变长字符串 ******************** 290 | allocate(character(4) :: first_name) 291 | first_name = 'yyyy' 292 | 293 | last_name = 'xxxxx' 294 | print *, first_name//' '//last_name 295 | end program string 296 | ``` 297 | 298 | ### 字符串数组 299 | 顾名思义,即存储字符串的数组 300 | ```fortran 301 | program string_array 302 | implicit none 303 | character(len=10), dimension(2) :: keys 304 | character(len=10), dimension(3,2) :: vals 305 | 306 | keys = [character(len=10) :: "姓名", "学号"] 307 | vals(1,:) = [character(len=10) :: "张三", "001"] 308 | vals(2,:) = [character(len=10) :: "李四", "002"] 309 | vals(3,:) = [character(len=10) :: "王五", "003"] 310 | 311 | call show(keys, vals) 312 | 313 | contains 314 | 315 | subroutine show(akeys, avals) 316 | character(len=*), intent(in) :: akeys(:), avals(:,:) 317 | integer :: i 318 | 319 | print *, akeys 320 | 321 | do i = 1, size(avals,1) 322 | print *, avals(i,:) 323 | end do 324 | end subroutine show 325 | 326 | end program string_array 327 | ``` 328 | 329 | --- 330 | ## 七. 流程控制 331 | 332 | ### 逻辑运算 333 | 334 | |关系运算符|(或)|描述| 335 | |:---:|:---:|:---:| 336 | |`==`|`.eq.`|是否相等| 337 | |`/=`|`.ne.`|是否不相等| 338 | |`>`|`.gt.`|是否大于| 339 | |`<`|`.lt.`|是否小于| 340 | |`>=`|`.ge.`|是否大于等于| 341 | |`<=`|`.le.`|是否小于等于| 342 | 343 | |逻辑运算符|描述| 344 | |:---:|:---:| 345 | |`.and.`|与| 346 | |`.or.`|或| 347 | |`.not.`|非| 348 | |`.eqv.`|同或(前后逻辑值**相同**返回**真**)| 349 | |`.neqv.`|异或(前后逻辑值**不同**返回**真**)| 350 | 351 | ### 条件语句 352 | 353 | ```fortran 354 | program if_statement 355 | implicit none 356 | 357 | real :: angle 358 | 359 | read *, angle 360 | 361 | if (angle < 90.0) then 362 | print *, '锐角' 363 | else if (angle < 180.0) then 364 | print *, '钝角' 365 | else 366 | print *, '什么角?' 367 | end if 368 | 369 | end program if_statement 370 | ``` 371 | 372 | ### 循环语句 373 | 374 | #### 序数循环 375 | `do` 后加上序数。如:`do i = 1,100,3`,其中`1`为起始值,`100`为结束值,`3`为步长。 376 | 377 | ```fortran 378 | program do_statement 379 | implicit none 380 | 381 | integer :: i 382 | 383 | do i = 1, 10, 2 384 | print *, i ! Print odd numbers 385 | end do 386 | 387 | end program do_statement 388 | ``` 389 | 390 | #### 条件循环 391 | `do while (condition)` 392 | 393 | ```fortran 394 | program do_while_statement 395 | implicit none 396 | 397 | integer :: i = 1 398 | 399 | do while (i < 11) 400 | print *, i 401 | i = i + 1 402 | end do 403 | 404 | print *, i ! i此时为11 405 | 406 | end program do_while_statement 407 | ``` 408 | 409 | #### 循环控制 410 | * `cycle` - (最近的)循环进入下一个取值 411 | * `exit` - 跳出(最近的)循环 412 | 413 | ```fortran 414 | program cycle_prog 415 | implicit none 416 | 417 | integer :: i 418 | 419 | do i = 1, 10 420 | if (mod(i, 2) == 0) then 421 | cycle ! 不再运行后面的程序,直接进入下一个i取值 422 | end if 423 | print *, i 424 | end do 425 | 426 | end program cycle_prog 427 | ``` 428 | 429 | ```fortran 430 | program exit_prog 431 | implicit none 432 | 433 | integer :: i 434 | 435 | do i = 1, 100 436 | if (i > 10) then 437 | exit ! 立即停止循环 438 | end if 439 | print *, i 440 | end do 441 | 442 | end program exit_prog 443 | ``` 444 | 445 | 在嵌套循环时,还可以给每个循环添加标签,在使用`cycle` 和 `exit` 后加上标签名即可对相应的循环产生作用 446 | ```fortran 447 | program tag_do 448 | implicit none 449 | 450 | integer :: i, j 451 | 452 | outer_loop: do i = 1, 10 453 | inner_loop: do j = 1, 10 454 | if (i > 7) then 455 | exit outer_loop ! 退出外部循环(outer_loop) 456 | end if 457 | 458 | if ((j + i) > 10) then 459 | cycle outer_loop ! 如果j+i的值大于10,则跳过剩下的内部循环(inner_loop),进入下一个i的取值(outer_loop) 460 | end if 461 | print *, 'I=', i, ' J=', j, ' Sum=', j + i 462 | end do inner_loop 463 | end do outer_loop 464 | 465 | end program tag_do 466 | ``` 467 | 468 | 469 | --- 470 | ## 八. 函数与代码结构 471 | 472 | ### 子例程和函数 473 | Fortran里有两种子程序,一种是`subroutine` (子例程),一种是`function` (函数)。 474 | 475 | 传入的参数有三种形式,`intent(in)` 只读,`intent(out)` 只写,`intent(inout)` 读写 476 | 477 | 一般来说,`function` (函数)只能有一个返回值,而`subroutine` (子例程)可以通过写入多个`intent(out)` 只写或`intent(inout)` 读写参数来拥有多个返回值 478 | 479 | * ___子例程___ ___subroutine___ 480 | ```fortran 481 | subroutine vector_product(n,a,b,dot_pro,element_wise_pro) 482 | integer, intent(in) :: n 483 | real, intent(in) :: a(n), b(n) 484 | real, intent(out) :: dot_pro, element_wise_pro(n) 485 | 486 | integer :: i 487 | 488 | dot_pro = 0. 489 | do i = 1,n 490 | dot_pro = dot_pro + a(i) * b(i) 491 | element_wise_pro(i) = a(i) * b(i) 492 | end do 493 | 494 | end subroutine vector_product 495 | 496 | ``` 497 | 498 | * ___函数___ ___function___ 499 | ```fortran 500 | function vector_norm(n,vec) result(norm) 501 | integer, intent(in) :: n 502 | real, intent(in) :: vec(n) 503 | real :: norm 504 | 505 | norm = sqrt(sum(vec**2)) 506 | 507 | end function vector_norm 508 | ``` 509 | 510 | 上面这个函数还可以将`result(norm)`省去,直接用函数名代替返回变量 511 | ```fortran 512 | function vector_norm(n,vec) 513 | integer, intent(in) :: n 514 | real, intent(in) :: vec(n) 515 | real :: vector_norm 516 | 517 | vector_norm = sqrt(sum(vec**2)) 518 | 519 | end function vector_norm 520 | ``` 521 | 522 | 或者直接在`function`关键词前添加类型,对函数名进行声明 523 | ```fortran 524 | real function vector_norm(n,vec) 525 | integer, intent(in) :: n 526 | real, intent(in) :: vec(n) 527 | 528 | vector_norm = sqrt(sum(vec**2)) 529 | 530 | end function vector_norm 531 | ``` 532 | 533 | ### 代码结构 534 | Fortran 里有四种方式存放子例程(`subroutine`)和函数(`function`) 535 | 536 | __1.__ 内部(internal)函数;在`program`中使用`contains`存放函数 537 | __2.__ 外部(external)函数;存放在`program`之外 538 | __3.__ 单独文件存放(编译时将涉及的文件全部编译) 539 | __4.__ 使用模块(module)管理;在`implicit none`前使用`use`加上模块名来导入模块,使用`only: ...`来导入特定的函数或变量 540 | 541 | 下面的例子在[5. 子例程+函数+代码结构](./5.%20%E5%AD%90%E4%BE%8B%E7%A8%8B%2B%E5%87%BD%E6%95%B0%2B%E4%BB%A3%E7%A0%81%E7%BB%93%E6%9E%84/)文件夹中 542 | 543 | ```fortran 544 | ! main.f90 545 | program structure 546 | ! ****************** 模块 ****************** 547 | use STATS, only: pi, mean_std 548 | implicit none 549 | 550 | integer :: n_x 551 | real :: x(3), y(3), dot_product, element_wise_product(3) 552 | real :: deriv(3) 553 | 554 | real, external :: norm1 ! 外部函数 555 | 556 | real :: data_array(5), data_mean, data_std 557 | 558 | x = [1,2,3] 559 | y = [3,2,1] 560 | n_x = size(x) 561 | 562 | ! ****************** 内部 ****************** 563 | print *, '内部函数:' 564 | call vector_product(n_x, x, y, dot_product, element_wise_product) 565 | 566 | print *, 'x · y = ', dot_product 567 | print *, 'element wise product = ', element_wise_product 568 | 569 | print *, '||x||_2 = ', vector_norm(n_x, x) 570 | print *, '' 571 | 572 | ! ****************** 外部 ****************** 573 | print *, '外部函数:' 574 | print *, '||x|| = ', norm1(n_x, x) 575 | print *, '' 576 | 577 | ! ****************** 单独文件存放 ****************** 578 | print *, '其他文件(derivative):' 579 | call derivative(x, n_x, 0.5, deriv) 580 | print *, deriv 581 | print *, '' 582 | 583 | ! ****************** 模块 ****************** 584 | print *, '模块(STATS):' 585 | data_array = [pi, 2*pi, 3*pi, 4*pi, 5*pi] 586 | call mean_std(5, data_array, data_mean, data_std) 587 | print *, '均值:', data_mean 588 | print *, '标准差:', data_std 589 | 590 | contains 591 | 592 | subroutine vector_product(n,a,b,dot_pro,element_wise_pro) 593 | integer, intent(in) :: n 594 | real, intent(in) :: a(n), b(n) 595 | real, intent(out) :: dot_pro, element_wise_pro(n) 596 | 597 | integer :: i 598 | 599 | dot_pro = 0. 600 | do i = 1,n 601 | dot_pro = dot_pro + a(i) * b(i) 602 | element_wise_pro(i) = a(i) * b(i) 603 | end do 604 | 605 | end subroutine vector_product 606 | 607 | function vector_norm(n,vec) 608 | integer, intent(in) :: n 609 | real, intent(in) :: vec(n) 610 | real :: vector_norm 611 | 612 | vector_norm = sqrt(sum(vec**2)) 613 | 614 | end function vector_norm 615 | 616 | end program structure 617 | 618 | real function norm1(n,vec) 619 | integer, intent(in) :: n 620 | real, intent(in) :: vec(n) 621 | 622 | norm1 = sum(abs(vec)) 623 | 624 | end function norm1 625 | ``` 626 | 627 | ```fortran 628 | ! Finite_Derivative.f90 629 | SUBROUTINE derivative (data, ndt, h, deriv) 630 | integer, intent(in) :: ndt 631 | real , intent(in) :: data(ndt), h 632 | real , intent(out):: deriv(ndt) 633 | integer :: i 634 | 635 | do i = 1,ndt-1 636 | deriv(i) = (data(i+1)-data(i))/h 637 | end do 638 | deriv(ndt) = 0. 639 | END SUBROUTINE derivative 640 | ``` 641 | 642 | ```fortran 643 | ! mean_std_module.f90 644 | module STATS 645 | real, parameter :: pi = 3.141592653589293 646 | 647 | contains 648 | 649 | SUBROUTINE mean_std(n,data,mean,std) 650 | integer, intent(in) :: n 651 | real , intent(in) :: data(n) 652 | real , intent(out):: mean, std 653 | integer :: i 654 | real :: sum = 0., sum_of_squared = 0. 655 | 656 | do i = 1,n 657 | sum = sum + data(i) 658 | sum_of_squared = sum_of_squared + data(i) ** 2 659 | end do 660 | 661 | mean = sum / n 662 | std = sqrt(sum_of_squared/n - (mean ** 2)) 663 | END SUBROUTINE mean_std 664 | 665 | end module STATS 666 | ``` 667 | 668 | --- 669 | ## 九. 面向对象 -- 类或结构体 670 | 671 | Fortran中的类或结构体(对应C/C++中的`struct`/`class`)是以`type`关键词实现的。`type`中包含了新定义类的属性(成员变量)和方法(成员函数)。要访问/读取/使用成员变量或函数,使用`%`符号。 672 | 673 | 在类中创建成员变量时和一般的声明一样,但是创建方法时要注意,不能直接在`type`中的`contains`后定义方法,我们只能声明方法,然后在`module`的`contains`后定义方法(这类似于C/C++的头文件的目的,只是C/C++的头文件允许你直接定义)。 674 | 675 | ```fortran 676 | module coordinates 677 | implicit none 678 | private ! 将整体设为private, 只在后续有必要的地方将方法或类型设为public 679 | 680 | public :: polar_coord ! 将polar_coord类设为public,供其他程序调用 681 | 682 | real, public :: pi = 3.141592653589793238462643383279502884197169399375105820974944592307816406286 683 | 684 | type polar_coord 685 | real :: rho, theta 686 | contains 687 | procedure, public, pass(this) :: to_euler 688 | procedure, public :: to_euler_func 689 | end type polar_coord 690 | 691 | contains 692 | 693 | subroutine to_euler(euler_coord, this) 694 | class(polar_coord), intent(in) :: this 695 | real, intent(out) :: euler_coord(2) 696 | 697 | euler_coord(1) = this%rho * cos(this%theta) 698 | euler_coord(2) = this%rho * sin(this%theta) 699 | end subroutine to_euler 700 | 701 | function to_euler_func(this) result(euler_coord) 702 | class(polar_coord), intent(in) :: this 703 | real :: euler_coord(2) 704 | 705 | euler_coord(1) = this%rho * cos(this%theta) 706 | euler_coord(2) = this%rho * sin(this%theta) 707 | end function to_euler_func 708 | 709 | 710 | end module coordinates 711 | 712 | program typedemo 713 | use coordinates 714 | implicit none 715 | 716 | type(polar_coord) :: p(2) ! 定义一个polar_coord类型的数组p,长度为2 717 | real :: euler_coord(2), euler_coord_func(2) 718 | 719 | p(1)%rho = sqrt(2.0) 720 | p(1)%theta = pi / 4.0 721 | 722 | print *, "---------------------" 723 | print *, "p(1)" 724 | call p(1)%to_euler(euler_coord) 725 | print *, "call member subroutine:" 726 | print *, euler_coord 727 | 728 | euler_coord_func = p(1)%to_euler_func() 729 | print *, "call member function:" 730 | print *, euler_coord_func 731 | 732 | 733 | p(2)%rho = 2. 734 | p(2)%theta = pi / 3.0 735 | 736 | print *, "---------------------" 737 | print *, "p(2)" 738 | call p(2)%to_euler(euler_coord) 739 | print *, "call member subroutine:" 740 | print *, euler_coord 741 | 742 | euler_coord_func = p(2)%to_euler_func() 743 | print *, "call member function:" 744 | print *, euler_coord_func 745 | end program typedemo 746 | ``` 747 | 748 | --- 749 | ## 十. 格式 750 | 751 | 在打印或读取数据的时候,我们常常需要指定打印或读取数据的格式。例如,我们想要打印`real :: pi = 3.141592653589293`,但只想保留2位小数,我们就可以写`write(*,'(F4.2)')`。所以,之前在[标准输入和输出 (io)](#三-标准输入和输出-io)章节中见过的`read(*,*)`和`write(*,*)`中的第二个`*`就代表默认的格式,编译器会根据数据类型进行选择格式;第一个`*`代表输入或输出位置,我们将在下一章[文件读写](#十一-文件读写)中看到。(同样,`read *`和`write *`以及`print *`中的`*`也代表默认格式,如果需要明确格式则将其替换。) 752 | 753 | 格式是以字符串定义的,字符串中的格式必须以小括号包裹,如`'(A)'`。下面的表列出了不同类型的数据格式可以如何明确。 754 | 755 |
| 目的 | 758 |格式 | 759 |||
| 读/写 整数 INTEGERs | 762 |Iw | 763 |Iw.m | 764 ||
| 读/写 浮点数 REALs | 767 |小数格式 | 768 |Fw.d | 769 ||
| 指数格式 | 772 |Ew.d | 773 |Ew.dEe | 774 ||
| 科学计数格式 | 777 |ESw.d | 778 |ESw.dEe | 779 ||
| 工程计数格式 | 782 |ENw.d | 783 |ENw.dEe | 784 ||
| 读/写 逻辑值 LOGICALs | 787 |Lw | 788 |||
| 读/写 字符/字符串 CHARACTERs | 791 |A | 792 |Aw | 793 ||
| 对齐 | 796 |水平 | 797 |nX | 798 ||
| Tabbing | 801 |Tc | 802 |TLc and TRc | 803 ||
| 垂直 | 806 |/ | 807 |||
| 其他 | 810 |Grouping | 811 |r(....) | 812 ||
| Format Scanning Control | 815 |: | 816 |||
| Sign Control | 819 |S, SP and SS | 820 |||
| Blank Control | 823 |BN and BZ | 824 |||
| 注 | 827 |w - 宽度, m - 最小宽度 , d - 小数位数 , e - 指数位数 | 828 |||