├── 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 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 |
目的 格式
读/写 整数 INTEGERs Iw Iw.m
读/写 浮点数 REALs 小数格式 Fw.d
指数格式 Ew.d Ew.dEe
科学计数格式 ESw.d ESw.dEe
工程计数格式 ENw.d ENw.dEe
读/写 逻辑值 LOGICALs Lw
读/写 字符/字符串 CHARACTERs A Aw
对齐 水平 nX
Tabbing Tc TLc and TRc
垂直 /
其他 Grouping r(....)
Format Scanning Control :
Sign Control S, SP and SS
Blank Control BN and BZ
w - 宽度, m - 最小宽度 , d - 小数位数 , e - 指数位数
830 | 831 | ```fortran 832 | program format 833 | implicit none 834 | 835 | real :: array(10) 836 | character(len=6) :: text = 'String' 837 | 838 | print '(A3)', text ! 只能打印‘Str’ 839 | print '(A6)', text ! 可以打印‘String’ 840 | 841 | call random_number(array) 842 | ! 默认格式 843 | write(*,*) array 844 | 845 | ! 小数格式 846 | write(*,'(F5.3)') array 847 | 848 | ! 科学计数格式 849 | write(*,'(ES10.3)') array 850 | 851 | end program format 852 | ``` 853 | 854 | --- 855 | ## 十一. 文件读写 856 | 857 | 使用`open()`和`close()`函数来打开和关闭一个文件。首先,我们要给打开的文件指定一个编号,如`open(2,file='test.dat')`,这样我们就成功将文件绑定到编号2上。(程序运行时如果`test.dat`不存在,则程序会创建一个`test.dat`文件)。当我们完成读取或写入操作后,则使用`close(2)`来关闭程序对`test.dat`文件的读写权限。在读取文件数据时,我们用`read(2,*)`来依次读取数据(将`read(*,*)`的第一个`*`替换为文件编号)。同样的,在写入时我们用`write(2,*)`来依次写入数据。 858 | 859 | > ⚠️ **_注意:_** 文件编号可以选择除了`5`和`6`之外的任何正整数,因为`5`和`6`分别指代了标准输出和输入 860 | 861 | 除了读写文本文件之外,我们还可以对二进制文件进行读写,只是在打开时要加上`form = unformatted`参数。 862 | 863 | ```fortran 864 | program file 865 | implicit none 866 | 867 | real :: a(10), b(10) 868 | 869 | call random_number(a) 870 | 871 | open(99, file='data.dat') 872 | write(99, *) a 873 | close(99) 874 | 875 | open(99, file='data.dat') 876 | read(99, *) b 877 | close(99) 878 | 879 | print *, b 880 | 881 | open(100, file='data.bin', access='stream', form='unformatted') 882 | write(100) a 883 | close(100) 884 | 885 | end program file 886 | ``` 887 | 888 | --- 889 | ## 十二. 指针 890 | Fortran中的指针可以指向变量和函数。在声明指针时,我们要加上`pointer`关键词,如`real, pointer :: p`。在声明被指向变量时,要加上`target`关键词,如`real, target :: a = 3.14`。在声明完成后,我们可以在主程序中用`=>`运算符对指针赋值。 891 | 892 | 指针也可以指向函数,如`procedure(functionName), pointer :: p_func`,这样`p_func`就创建了一个和`functionName`具有同样输入参数的函数指针。如果我们有多个函数且它们具有同样的输入参数(类型和数量),则`p_func`就可以在主程序中指向任意一个函数。 893 | 894 | 指针还可以指向同一类型的指针,当指向一个指针时,当前指针会指向被指向指针所指向的目标。因此,当被指向指针改变之后,当前指针并不会跟随被指向指针改变其指向目标的地址。我们可以在下面的例子中具体看到。 895 | 896 | ```fortran 897 | program pointerdemo 898 | implicit none 899 | 900 | integer, pointer :: p0 901 | integer, target :: i0 = 99, i1 = 1000 902 | 903 | real, pointer :: p1(:) ! 数组指针直接声明为可分配(变长)数组即可 904 | real, target :: r0(5) = (/ 1,2,3,4,5 /) 905 | 906 | procedure(f), pointer :: pf 907 | 908 | real, pointer :: p_r, pp_r 909 | real, target :: r1 = 1.5, r2 = 3.14 910 | 911 | ! 1. 简单的单一变量指针 912 | print *, "1. 简单的单一变量指针" 913 | print *, 'i0 = ', i0 914 | print *, 'i1 = ', i1 915 | p0 => i0 916 | p0 = 999 ! p0指向i0,修改p0的值,i0的值也会被修改 917 | print *, 'i0 = ', i0 918 | print *, 'i1 = ', i1 919 | print *, 'p0 = ', p0 920 | p0 => i1 921 | p0 = 10000 ! 此时p0指向i1,修改p0的值,i1的值也会被修改 922 | print *, 'i0 = ', i0 923 | print *, 'i1 = ', i1 924 | print *, 'p0 = ', p0 925 | print * 926 | 927 | ! 2. 数组指针 928 | print *, "2. 数组指针" 929 | print *, 'r0 = ', r0 930 | p1 => r0 931 | p1(1:3) = 0 932 | print *, 'r0 = ', r0 ! r0的前3个元素被赋值为0 933 | print * 934 | 935 | ! 3. 函数指针 936 | print *, "3. 函数指针" 937 | pf => f 938 | print *, 'pf(2) = ', pf(2.) ! 调用f(2.) 939 | pf => g 940 | print *, 'pf(2) = ', pf(2.) ! 调用g(2.) 941 | print * 942 | 943 | ! 4. 指针的指针 944 | print *, "4. 指针的指针" 945 | print *, 'r1 = ', r1 946 | print *, 'r2 = ', r2 947 | p_r => r1 948 | pp_r => p_r 949 | print *, 'p_r = ', p_r ! p_r指向r1 950 | print *, 'pp_r = ', pp_r ! pp_r指向r1 951 | p_r => r2 952 | print *, 'p_r = ', p_r ! p_r指向r2 953 | print *, 'pp_r = ', pp_r ! pp_r仍然指向r1 954 | print * 955 | 956 | contains 957 | 958 | real function f(x) 959 | real, intent(in) :: x 960 | f = x**2 961 | end function f 962 | 963 | real function g(x) 964 | real, intent(in) :: x 965 | g = x**3 966 | end function g 967 | 968 | end program pointerdemo 969 | ``` 970 | 971 | --- 972 | ## 十三. 并行计算 973 | 974 | Fortran中的并行计算/分布式计算有多种方式实现 975 | 976 | 1. MPI (Message-Passing-Interface) ([mpich.org](www.mpich.org), [open-mpi.org](www.open-mpi.org)) 977 | 2. Coarray Fortran (GNU Fortran) ([opencoarrays.org/](http://www.opencoarrays.org/)) 978 | 3. CUDA Fortran (GPUs parallel) 979 | 980 | 我们这里简单介绍一下MPI和Coarray的用法 981 | 982 | ### MPI 983 | 首先要能使用MPI来进行并行计算,需要先下载MPI - [open-mpi.org](https://www.open-mpi.org/) 984 | 985 | 创建一个名为`mpi_parallel.f90`的文件 986 | ```fortran 987 | ! mpi_parallel.f90 988 | program mpi_parrallel 989 | use mpi ! 调用MPI模块 990 | implicit none 991 | 992 | integer :: ierr, num_processors, my_processor_id 993 | 994 | call MPI_INIT(ierr) ! 启动MPI 995 | call MPI_COMM_SIZE(MPI_COMM_WORLD, num_processors, ierr) ! 获取CPUs的数量 996 | call MPI_COMM_RANK(MPI_COMM_WORLD, my_processor_id, ierr) ! 获取当前工作的CPU编号 997 | 998 | print *, 'Hello from processor ', my_processor_id, ' of ', num_processors 999 | 1000 | call MPI_FINALIZE(ierr) ! 终止MPI 1001 | 1002 | end program mpi_parrallel 1003 | ``` 1004 | 1005 | 在编译时使用`mpifort`/`mpif90`/`mpif77`进行编译,其中,`mpifort`可以编译任意版本的fortran文件,`mpif90`和`mpif77`分别可以编译`.f90`和`.f77`的fortran文件。不过,`mpif90`和`mpif77`在以后的版本可能不再支持。 1006 | ```shell 1007 | $ mpifort mpi_parallel.f90 -o mpi_parallel 1008 | ``` 1009 | 1010 | 在运行,必须使用`mpirun -n [number of cores]`来调用CPU。 1011 | ```shell 1012 | $ mpirun -n 4 mpi_parallel 1013 | Hello from processor 0 of 4 1014 | Hello from processor 1 of 4 1015 | Hello from processor 2 of 4 1016 | Hello from processor 3 of 4 1017 | ``` 1018 | 1019 | ### Coarray 1020 | 使用Coarray首先要去[opencoarrays.org](http://www.opencoarrays.org/)下载Coarrays. 1021 | 1022 | 创建一个名为`coarray_parallel.f90`的文件 1023 | ```fortran 1024 | program coarray_parrallel 1025 | 1026 | print *, 'Hello from processor ', this_image(), ' of ', num_images() 1027 | 1028 | end program coarray_parrallel 1029 | ``` 1030 | 1031 | 和MPI类似,编译时使用`caf`,运行时使用`cafrun -n [number of cores]` 1032 | 1033 | ```shell 1034 | $ caf coarray_parallel.f90 -o coarray_parallel 1035 | $ cafrun -n 4 coarray_parallel 1036 | Hello from processor 2 of 4 1037 | Hello from processor 3 of 4 1038 | Hello from processor 4 of 4 1039 | Hello from processor 1 of 4 1040 | ``` 1041 | 1042 | --- 1043 | ## 十四. 内置函数 1044 | 1045 | Fortran中有相当多的内置函数,我在此列出一部分常用的函数供大家参考。 1046 | 更加详细的清单请见[fortran-lang.org/zh_CN/learn/intrinsics/index](https://fortran-lang.org/zh_CN/learn/intrinsics/index)和[nsc.liu.se/~boein/f77to90/a5.html#section21](https://www.nsc.liu.se/~boein/f77to90/a5.html) 1047 | 1048 | 下面列表中的函数除非特别备注为子例程,否则都为函数类型。 1049 | 1050 | ### 数值 1051 | 1052 | |函数名|说明|用法| 1053 | |:---:|:--:|:--:| 1054 | |`abs`|绝对值|`abs(-1.5)` $\rightarrow$ 1.5| 1055 | |`aint`|向0取整|`aint(-2.999)` $\rightarrow$ -2; `aint(2.999)` $\rightarrow$ 2| 1056 | |`anint`|四舍五入取整|`anint(-2.999)` $\rightarrow$ -3; `anint(2.4)` $\rightarrow$ 2| 1057 | |`ceiling`|向 $\infty$ 取整|`ceiling(-2.99)` $\rightarrow$ -2; `ceiling(2.4)` $\rightarrow$ 3| 1058 | |`floor`|向 $-\infty$ 取整|`floor(-2.99)` $\rightarrow$ -3; `floor(2.4)` $\rightarrow$ 2| 1059 | |`conjg`|取复数共轭|`conjg((1,2))` $\rightarrow$ (1,-2)| 1060 | |`max`|最大值|`max(1,2,3,4,5,6)` $\rightarrow$ 6| 1061 | |`min`|最小值|`min(1.0,2.0,3.0,4.0,5.0,6.0)` $\rightarrow$ 1.0| 1062 | |`mod`|取余|`mod(19,6)` $\rightarrow$ 1| 1063 | |`dot_product`|向量点乘|`dot_product([1,2],[3,3])` $\rightarrow$ 9| 1064 | |`matmul`|矩阵相乘|`C = matmul(A,B)`| 1065 | 1066 | ### 数学 1067 | |函数名|说明|用法| 1068 | |:---:|:--:|:--:| 1069 | |`sqrt`|平方根|`sqrt(2)` $\rightarrow$ $\sqrt{2}$| 1070 | |`exp`|指数|`exp(2)` $\rightarrow$ $e^{2}$| 1071 | |`log`|自然对数|`log(2)` $\rightarrow$ $ln(2)$| 1072 | |`log10`|底数为10的对数|`log10(2)` $\rightarrow$ $log_{10}(2)$| 1073 | |`sin`|正弦 sine|`sin(0)` $\rightarrow$ 0| 1074 | |`cos`|余弦 cosine|`cos(0)` $\rightarrow$ 1| 1075 | |`tan`|正切 tangent|`tan(0)` $\rightarrow$ 0| 1076 | |`asin`|反正弦 arcsin|`asin(theta)` $\rightarrow$ $arcsin(\theta) = sin^{-1}(\theta)$| 1077 | |`acos`|反余弦 arccos|`acos(theta)` $\rightarrow$ $arccos(\theta) = cos^{-1}(\theta)$| 1078 | |`atan`|反正切 arctan|`atan(theta)` $\rightarrow$ $arctan(\theta) = tan^{-1}(\theta)$| 1079 | |`sinh`|双曲正弦|`sinh(x)` $\rightarrow$ $sinh(x) = \dfrac{e^x - e^{-x}}{2}$| 1080 | |`cosh`|双曲余弦|`cosh(x)` $\rightarrow$ $cosh(x) = \dfrac{e^x + e^{-x}}{2}$| 1081 | |`tanh`|双曲正切|`tanh(x)` $\rightarrow$ $tanh(x) = \dfrac{e^x - e^{-x}}{e^x + e^{-x}}$| 1082 | |`random_number`(子例程)|生成 $U(0,1)$ 的随机数|`call random_number(harvest)` $\rightarrow$ harvest $\sim U(0,1)$| 1083 | |`bessel_j0`|第一类0阶贝塞尔函数 $J_0(x)$ (Bessel function of the first kind of order 0)|`bessel_j0(x)` $\rightarrow$ $J_0(x) = \sum_{m = 0}^{\infty} \dfrac{(-1)^m}{m!\Gamma(m+1)}\left( \dfrac{x}{2} \right)^{2m}$| 1084 | |`bessel_j1`|第一类1阶贝塞尔函数 $J_1(x)$ (Bessel function of the first kind of order 1)|`bessel_j1(x)` $\rightarrow$ $J_1(x) = \sum_{m = 0}^{\infty} \dfrac{(-1)^m}{m!\Gamma(m+2)}\left( \dfrac{x}{2} \right)^{2m+1}$| 1085 | |`bessel_jn`|第一类n阶贝塞尔函数 $J_n(x)$ (Bessel function of the first kind of order n)|`bessel_jn(n,x)` $\rightarrow$ $J_n(x) = \sum_{m = 0}^{\infty} \dfrac{(-1)^m}{m!\Gamma(m+n+1)}\left( \dfrac{x}{2} \right)^{2m+n}$ (注: `integer :: n`, n 为正整数)| 1086 | |`bessel_y0`|第二类0阶贝塞尔函数 $Y_0(x)$ (Bessel function of the second kind of order 0)|`bessel_y0(x)` $\rightarrow$ $Y_0(x) = \dfrac{J_0(x) - J_{-0}(x)}{sin(0)}$| 1087 | |`bessel_y1`|第二类1阶贝塞尔函数 $Y_1(x)$ (Bessel function of the second kind of order 1)|`bessel_y1(x)` $\rightarrow$ $Y_1(x) = \dfrac{J_1(x)cos(\pi) - J_{-1}(x)}{sin(\pi)}$ | 1088 | |`bessel_yn`|第二类n阶贝塞尔函数 $Y_n(x)$ (Bessel function of the second kind of order n)|`bessel_yn(n,x)` $\rightarrow$ $Y_n(x) = \dfrac{J_n(x)cos(n\pi) - J_{-n}(x)}{sin(n\pi)}$ (注: `integer :: n`, n 为整数)| 1089 | |`erf`|误差函数|`erf(x)` $\rightarrow$ $erf(x) = \dfrac{2}{\sqrt{\pi}} \int_{0}^{x}e^{-t^2}dt$ | 1090 | |`erfc`|互补误差函数| $erfc(x) = 1 - erf(x) = \dfrac{2}{\sqrt{\pi}} \int_{x}^{\infty}e^{-t^2}dt$ | 1091 | |`erfc_scaled`|指数放缩误差函数|`erfc_scaled(x)` $\rightarrow$ $erfcx(x) = e^{x^2}erfc(x) = e^{x^2} \dfrac{2}{\sqrt{\pi}} \int_{x}^{\infty}e^{-t^2}dt$| 1092 | |`gamma`| $\Gamma$ 函数|`gamma(x)` $\rightarrow$ $\Gamma(x) = (x-1)!$| 1093 | |`norm2`|2-范数|`norm2(x)` $\rightarrow$ `sqrt(sum(x**2))` $\rightarrow$ $\lVert x \rVert _2$| 1094 | 1095 | ### 数组 1096 | |函数名|说明| 1097 | |:---:|:--:| 1098 | |`ALL(MASK, [dim])`| 逻辑数组MASK在dim维度与门| 1099 | |`ANY(MASK, [dim])`| 逻辑数组MASK在dim维度或门| 1100 | |`COUNT(MASK, [dim])`| 逻辑数组MASK在dim维度上真的个数| 1101 | |`PRODUCT(ARRAY, [[dim], [mask]])`|数组元素积| 1102 | |`SUM(ARRAY, [[dim], [mask]])`|数组元素和| 1103 | |`SIZE(ARRAY, dim)`| 数组dim维度元素个数| 1104 | |`RESHAPE(SOURCE, SHAPE)`|数组重构| 1105 | 1106 | 1107 | --- 1108 | ## 十五. Makefile管理项目 1109 | 在使用`make`之前,我们需要去[https://www.gnu.org/software/make/](https://www.gnu.org/software/make/)网站下载make。下载好之后,我们就可以通过编写`Makefile`文件来管理项目了。 1110 | 1111 | 在[Makefile编译](./Makefile%E7%BC%96%E8%AF%91/)文件夹中是我写好的一个计算**对流-扩散方程**的小型项目。项目结构如下所示 1112 | 1113 | ```bash 1114 | Makefile编译 1115 | ├── FD_SCHEME.f90 1116 | ├── FINITE_DERIVATIVE.f90 1117 | ├── ADVECTION_DIFFUSION.f90 1118 | ├── POISSON_SOLVER.f90 1119 | ├── CONVECTION_DIFFUSION.f90 1120 | ├── convection_diffusion_main.f90 1121 | └── Makefile 1122 | ``` 1123 | 1124 | 接着我们创建一个`Makefile`文件,在其中写入如下所示的编译规则。 1125 | 1126 | ```Makefile 1127 | # Disable all of make's built-in rules (similar to Fortran's implicit none) 1128 | MAKEFLAGS += --no-builtin-rules --no-builtin-variables 1129 | 1130 | # configuration 1131 | FC := gfortran 1132 | LD := $(FC) 1133 | AR := ar -r 1134 | # RM := rm -f 1135 | 1136 | # list of all source files 1137 | SRCS := FINITE_DERIVATIVE.f90 FD_SCHEME.f90 POISSON_SOLVER.f90 ADVECTION_DIFFUSION.f90 CONVECTION_DIFFUSION.f90 1138 | OBJS := FINITE_DERIVATIVE.o FD_SCHEME.o POISSON_SOLVER.o ADVECTION_DIFFUSION.o CONVECTION_DIFFUSION.o 1139 | LIBR := libCODF.a 1140 | 1141 | PROG := convection_diffusion_main.f90 1142 | EXEC := co_df 1143 | 1144 | # command line arguments 1145 | CMLA := test 1146 | 1147 | .PHONY: all test run clean 1148 | all: $(EXEC) 1149 | 1150 | $(EXEC): $(LIBR) 1151 | $(LD) -o $@ $(PROG) $^ 1152 | 1153 | $(LIBR): $(OBJS) 1154 | $(AR) $@ $^ 1155 | 1156 | $(OBJS): 1157 | $(FC) -c $(SRCS) 1158 | 1159 | # define dependencies between object files 1160 | ADVECTION_DIFFUSION.o: FINITE_DERIVATIVE.o FD_SCHEME.o 1161 | 1162 | # rebuild all object files in case this Makefile changes 1163 | # $(OBJS): $(MAKEFILE_LIST) 1164 | 1165 | test: 1166 | for idx in 1 2 3 4 5; do \ 1167 | ./$(EXEC) $(addsuffix .dat, $(addsuffix $$idx, $(CMLA))); \ 1168 | done 1169 | 1170 | run: 1171 | ./$(EXEC) Prandtl.dat 1172 | # ./$(EXEC) Prandtl_EMPTY.dat 1173 | 1174 | clean: 1175 | $(RM) $(filter %.o, $(OBJS)) $(wildcard *.mod) $(EXEC) $(LIBR) $(wildcard *.bin) 1176 | ``` 1177 | 1178 | 要运行定义好的分支的话,就在命令行中输入`make`后面加上分支名。 1179 | 1180 | - `all`分支 1181 | ```bash 1182 | make 1183 | ``` 1184 | 或 1185 | ```bash 1186 | make all 1187 | ``` 1188 | > **注:** 在命令行中输入`make`时,make可执行文件会自动搜寻当前文件夹下的`Makefile`文件,并运行其中的`all`分支。 1189 | 1190 | - `run`分支 1191 | ```bash 1192 | make run 1193 | ``` 1194 | 1195 | - `test`分支 1196 | ```bash 1197 | make test 1198 | ``` 1199 | 1200 | - `clean`分支 1201 | ```bash 1202 | make clean 1203 | ``` --------------------------------------------------------------------------------