[英]Converting VBA code to Fortran
I am trying to solve this equation with Gauss elimination with partial pivoting. 我正在尝试通过部分枢轴的高斯消除来解决这个方程。
x-2y-z=2
5x+2y+2z=9
-3x+5y-z=1
so I put 所以我把
1 2 -1
5 2 2
-3 5 -1
to INPUT1.DAT and 到INPUT1.DAT并
2
9
1
to INPUT2.DAT. 到INPUT2.DAT。
This is VBA code running well, 这是VBA代码运行良好,
Option Explicit
Sub GaussElim()
Dim n As Integer, er As Integer, i As Integer
Dim a(10, 10) As Double, b(10) As Double, x(10) As Double
n = 3
a(1, 1) = 1: a(1, 2) = 2: a(1, 3) = -1
a(2, 1) = 5: a(2, 2) = 2: a(2, 3) = 2
a(3, 1) = -3: a(3, 2) = 5: a(3, 3) = -1
b(1) = 2: b(2) = 9: b(3) = 1
Call Gauss(a, b, n, x, er)
If er = 0 Then
For i = 1 To n
MsgBox "x(" & i & ") = " & x(i)
Next i
Else
MsgBox "ill-conditioned system"
End If
End Sub
Sub Gauss(a, b, n, x, er)
Dim i As Integer, j As Integer
Dim s(10) As Double
Const tol As Double = 0.000001
er = 0
For i = 1 To n
s(i) = Abs(a(i, 1))
For j = 2 To n
If Abs(a(i, j)) > s(i) Then s(i) = Abs(a(i, j))
Next j
Next i
Call Eliminate(a, s, n, b, tol, er)
If er <> -1 Then
Call Substitute(a, n, b, x)
End If
End Sub
Sub Pivot(a, b, s, n, k)
Dim p As Integer, ii As Integer, jj As Integer
Dim factor As Double, big As Double, dummy As Double
p = k
big = Abs(a(k, k) / s(k))
For ii = k + 1 To n
dummy = Abs(a(ii, k) / s(ii))
If dummy > big Then
big = dummy
p = ii
End If
Next ii
If p <> k Then
For jj = k To n
dummy = a(p, jj)
a(p, jj) = a(k, jj)
a(k, jj) = dummy
Next jj
dummy = b(p)
b(p) = b(k)
b(k) = dummy
dummy = s(p)
s(p) = s(k)
s(k) = dummy
End If
End Sub
Sub Substitute(a, n, b, x)
Dim i As Integer, j As Integer
Dim sum As Double
x(n) = b(n) / a(n, n)
For i = n - 1 To 1 Step -1
sum = 0
For j = i + 1 To n
sum = sum + a(i, j) * x(j)
Next j
x(i) = (b(i) - sum) / a(i, i)
Next i
End Sub
Sub Eliminate(a, s, n, b, tol, er)
Dim i As Integer, j As Integer, k As Integer
Dim factor As Double
For k = 1 To n - 1
Call Pivot(a, b, s, n, k)
If Abs(a(k, k) / s(k)) < tol Then
er = -1
Exit For
End If
For i = k + 1 To n
factor = a(i, k) / a(k, k)
For j = k + 1 To n
a(i, j) = a(i, j) - factor * a(k, j)
Next j
b(i) = b(i) - factor * b(k)
Next i
Next k
If Abs(a(k, k) / s(k)) < tol Then er = -1
End Sub
and I tried to convert this code to Fortran like below, 我试图将代码转换为如下所示的Fortran,
program Gauss_Emlimination !with partial pivoting
implicit none
INTEGER n, i, j
REAL A(3,3), B(3), X(3), ER, tol
tol = 0.000001
n = 3
OPEN(UNIT=2, FILE='INPUT1.DAT')
OPEN(UNIT=3, FILE='INPUT2.DAT')
OPEN(UNIT=4, FILE='RESULT.DAT')
READ(2,*) ((A(I,J),J=1,3),I=1,3)
READ(3,*) (B(I), I=1,3)
CALL Gauss(a, b, n, x, er)
IF (er .EQ. 0) THEN
DO i =1, N
WRITE(4,*) X(i)
END DO
ELSE
WRITE(4,*) "ill-conditioned system"
END IF
contains
SUBROUTINE Gauss(a, b, n, x, er)
real, intent(inout) :: a(3,3)
real, intent(inout) :: b(3)
integer, intent(in) :: n
real, intent(out) :: x(N)
REAL, intent(out) :: er
real, dimension(10) :: S(10)
INTEGER I, J
ER=0
DO I= 1, N
s(i) = ABS(A(i,1))
DO j = 2, n
IF (ABS(A(i,j)) .GT. s(i)) THEN
s(i) = ABS(A(i,j))
END IF
END DO
END DO
CALL Eliminate(a, s, n, b, tol, er)
IF (er .ne. -1) THEN
CALL Substitute(a, n, b, x)
END IF
END SUBROUTINE Gauss
SUBROUTINE Pivot(a, b, s, n, k)
INTEGER ii, jj
real, intent(inout) :: a(3,3)
real, intent(inout) :: b(3)
integer, intent(in) :: n, K
integer p
real, dimension(10) :: S(10)
DOUBLE PRECISION big, dummy, factor
p = k
big = ABS(A(k,k)/S(k))
DO ii = k+1, n
dummy = ABS(A(ii, k)/S(ii))
IF (dummy .GT. big) THEN
big = dummy
p = ii
END IF
END DO
IF (p .ne. k) THEN
DO jj = k, n
dummy = A(p, jj)
A(p, jj) = A(k, jj)
A(k, jj) = dummy
END DO
dummy = B(p)
B(p) = B(k)
B(k) = dummy
dummy = S(p)
S(p) = S(k)
S(k) = dummy
END IF
END SUBROUTINE Pivot
SUBROUTINE Substitute(a, n, b, x)
INTEGER i, j
real, intent(inout) :: a(3,3)
real, intent(inout) :: b(3)
integer, intent(in) :: n
real, intent(out) :: x(N)
DOUBLE PRECISION sum
X(n) = B(n)/A(n, n)
DO i = n-1, 1, -1
sum = 0
DO j = i+1, n
sum = sum +A(i, j)*X(j)
END DO
X(n) = (B(n)-sum)/A(n,n)
END DO
END SUBROUTINE Substitute
SUBROUTINE Eliminate(a, s, n, b, tol, er)
real, intent(in) :: tol
real, intent(inout) :: a(3,3)
real, intent(inout) :: b(3)
integer, intent(in) :: n
real, dimension(10) :: S(10)
real, intent(INout) :: er
INTEGER i, j, k
DOUBLE PRECISION factor
DO K = 1, N-1
CALL Pivot (a, b, s, n, k)
IF (ABS(A(K,K)/S(K)) .LT. tol) THEN
er=-1
EXIT
END IF
DO i = k+1, n
factor = A(i,k)/A(k,k)
DO j= k+1, n
A(i,j) = A(i,j) - factor*B(k)
END DO
B(i) = B(i) - factor * B(k)
END DO
END DO
IF (ABS(A(n,n)/S(n)) .LT. tol) THEN
er= -1
END IF
END SUBROUTINE Eliminate
end program Gauss_Emlimination
and I got no error for this code. 我没有错误的代码。
but the problem is that I got ' 0.0000000E+00 0.0000000E+00 -7.1424372E-02' as a result. 但是问题是结果是我得到了'0.0000000E + 00 0.0000000E + 00 -7.1424372E-02'。
it supposed to be 'x(1)=1, x(2)=1, x(3)=1'. 它应该是'x(1)= 1,x(2)= 1,x(3)= 1'。
Could anyone help me to find what is wrong in my algorithm please?? 谁能帮我找出我的算法有什么问题吗?
Firstly, you should make sure you put all these subroutines in a module. 首先,您应确保将所有这些子例程放在模块中。 That way you don't need to declare External GaussElim
, for example, in each subroutine as the compiler will know about all the subroutines in the module, and what arguments they expect. 这样,您无需在每个子例程中声明External GaussElim
,因为编译器将知道模块中的所有子例程以及它们期望的参数。 To do that, just put all these subroutines in one file and put them inbetween: 为此,只需将所有这些子例程放在一个文件中,然后将它们放在它们之间:
module gauss_mod
implicit none
contains
! your code here
end module gauss_mod
Then in your main program, you can just put use gauss_mod
at the top, and you will have access to all the subroutines in the module. 然后,在主程序中,只需将use gauss_mod
放在顶部,就可以访问模块中的所有子例程。 The implicit none
tells your compiler that you are going to declare all variables, and it shouldn't guess the type of any you haven't told it about. implicit none
告诉您的编译器您将要声明所有变量,并且它不应该猜测尚未告知的任何变量的类型。 This will catch a lot of errors caused by typos, for example. 例如,这将捕获很多由错别字引起的错误。
Secondly, you need to declare the arguments to your subroutines. 其次,您需要为子例程声明参数。 This is what is causing most of your errors. 这就是导致大多数错误的原因。 Outside of GaussElim
, none of the other subroutines know what variables like A
are. 在GaussElim
之外,其他子例程都不知道像A
这样的变量。 As a result, when your compiler sees 结果,当您的编译器看到
s(i) = ABS(A(i,1))
It thinks A(i,1)
is a function and gives you errors related to that. 它认为A(i,1)
是一个函数,并为您提供与此相关的错误。 You can fix it by simply adding the following line to your subroutines: 您可以通过将以下行添加到子例程中来解决此问题:
double precision, dimension(:,:) :: A
This tells the subroutine that A must have two dimensions, but can be any size. 这告诉子例程A必须具有二维,但可以是任意大小。 You should also add intent(in)
for input arguments, intent(out)
for output arguments and intent(inout)
for arguments which are changed by your subroutines. 您还应该为输入自变量添加intent(in)
为输出自变量添加intent(in)
intent(out)
,为由子例程更改的自变量添加intent(inout)
。
Additionally, instead of using double precision
, use real
and set the kind
parameter: 另外,不要使用double precision
,而是使用real
并设置kind
参数:
module gauss_mod
implicit none
integer, parameter :: dp = selected_real_kind(15)
contains
! your code here
! As an example:
subroutine gauss(a, b, n, x, er)
! dummy arguments
real(kind=dp), dimension(:,:), intent(in) :: a, b
integer, intent(in) :: n
real(kind=dp), dimension(:), intent(out) :: x
real(kind=dp), intent(out) :: er
real(kind=dp), dimension(10) :: S(10)
real(kind=dp), parameter :: tol = 0.000001
! rest of subroutine
end subroutine gauss
end module gauss_mod
Doing these things will get rid of lots of the errors. 做这些事情将消除很多错误。 If you still have errors, you should post the exact error message, and indicate which lines in the code they refer to. 如果仍然有错误,则应发布确切的错误消息,并指出它们所引用的代码行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.