Why is my Lagrange interpolation algorithm not working? - fortran

For some reason it never interpolates, but it gives 0 as an answer. The code is:
PROGRAM LAGRANGE
REAL X(0:100), Y(0:100), INTERP
REAL TEMP = 1.0
REAL POLINOM = 0.0
N=10
OPEN(1,FILE="datos.txt")
DO I=0,100 !We 'clean' the arrays: all positions are 0
X(I)=0.0
Y(I)=0.0
END DO
DO I=0,10 !We read the data file and we save the info
READ(1,*) X(I), Y(I)
END DO
CLOSE(1)
WRITE(*,*) "Data table:"
DO I=0,10
WRITE(*,*) X(I), Y(I)
END DO
WRITE(*,*) "Which value of X do you want to interpolate?"
READ(*,*) INTERP
DO I=0,N
DO J=0,N
IF(J.NE.I) THEN !Condition: J and I can't be equal
TEMP=TEMP*(INTERP-X(J))/(X(I)-X(J))
ELSE IF(J==I) THEN
TEMP=TEMP*1.0
ELSE
END IF
END DO
POLINOM=POLINOM+TEMP
END DO
WRITE(*,*) "Value: ",POLINOM
STOP
END PROGRAM
Where did I fail? I basically need to implement this:
Lagrange interpolation method
Thanks a lot in advance.

In addition to the "symbol-concatenation" problem (explained in the other answer), it seems that TEMP needs to be reset to 1.0 for every I (to calculate the Lagrange polynomial for each grid point), plus we need to multiply it by the functional value on that point (Y(I)). After fixing these
PROGRAM LAGRANGE
implicit none !<-- always recommended
REAL :: X(0:100), Y(0:100), INTERP, TEMP, POLINOM
integer :: I, J, K, N
N = 10
X = 0.0
Y = 0.0
!! Test data (sin(x) over [0,2*pi]).
DO I = 0, N
X(I) = real(I) / real(N) * 3.14159 * 2.0
Y(I) = sin( X(I) )
END DO
WRITE(*,*) "Data table:"
DO I = 0, N
WRITE(*,*) X(I), Y(I)
END DO
interp = 0.5 !! test value
POLINOM = 0.0
DO I = 0, N
TEMP = 1.0 !<-- TEMP should be reset to 1.0 for every I
DO J = 0, N
IF( J /= I ) THEN
TEMP = TEMP * (interp - X(J)) / (X(I) - X(J))
END IF
END DO
TEMP = TEMP * Y(I) !<-- also needs this
POLINOM = POLINOM + TEMP
END DO
print *, "approx : ", POLINOM
print *, "exact : ", sin( interp )
end
we get a pretty good agreement between the approximate (= interpolated) and exact results:
Data table:
0.00000000 0.00000000
0.628318012 0.587784827
1.25663602 0.951056182
1.88495409 0.951056957
2.51327205 0.587786913
3.14159012 2.53518169E-06
3.76990819 -0.587782800
4.39822626 -0.951055467
5.02654409 -0.951057792
5.65486193 -0.587789178
6.28318024 -5.07036339E-06
approx : 0.479412317
exact : 0.479425550

Consider the (complete) program
real x = 1.
end
What does this do?
If this is free-form source then it is an invalid program. If it is fixed-form source then it is a valid program.
In fixed-form source, spaces after column 6 largely have no effect. The program above is exactly like
realx=1.
end
and we can see that we're just setting an implicitly declared real variable called realx to have value 1..
implicit none
real x = 1.
end
will show a problem.
In free-form source, initialization in a declaration statement requires ::, like so:
real :: x = 1.
end
And: use implicit none.

Related

if statement to determine steady-state

My code below correctly solves a 1D heat equation for a function u(x,t). I now want to find the steady-state solution, the solution that no longer changes in time so it should satisfy u(t+1)-u(t) = 0. What is the most efficient way to find the steady-state solution? I show three different attempts below, but I'm not sure if either are actually doing what I want. The first and third have correct syntax, the second method has a syntax error due to the if statement. Each method is different due to the change in the if structure.
Method 1 :
program parabolic1
integer, parameter :: n = 10, m = 20
real, parameter :: h = 0.1, k = 0.005 !step sizes
real, dimension (0:n) :: u,v
integer:: i,j
real::pi,pi2
u(0) = 0.0; v(0) = 0.0; u(n) = 0.0; v(n) =0.0
pi = 4.0*atan(1.0)
pi2 = pi*pi
do i=1, n-1
u(i) = sin( pi*real(i)*h)
end do
do j = 1,m
do i = 1, n-1
v(i) = 0.5*(u(i-1)+u(i+1))
end do
t = real(j)*k !increment in time, now check for steady-state
!steady-state check: this checks the solutions at every space point which I don't think is correct.
do i = 1,n-1
if ( v(i) - u(i) .LT. 1.0e-7 ) then
print*, 'steady-state condition reached'
exit
end if
end do
do i = 1, n-1 !updating solution
u(i) = v(i)
end do
end do
end program parabolic1
Method 2 :
program parabolic1
integer, parameter :: n = 10, m = 20
real, parameter :: h = 0.1, k = 0.005 !step sizes
real, dimension (0:n) :: u,v
integer:: i,j
real::pi,pi2
u(0) = 0.0; v(0) = 0.0; u(n) = 0.0; v(n) =0.0
pi = 4.0*atan(1.0)
pi2 = pi*pi
do i=1, n-1
u(i) = sin( pi*real(i)*h)
end do
do j = 1,m
do i = 1, n-1
v(i) = 0.5*(u(i-1)+u(i+1))
end do
t = real(j)*k !increment in time, now check for steady-state
!steady-state check: (This gives an error message since the if statement doesn't have a logical scalar expression, but I want to compare the full arrays v and u as shown.
if ( v - u .LT. 1.0e-7 ) then
print*, 'steady-state condition reached'
exit
end if
do i = 1, n-1 !updating solution
u(i) = v(i)
end do
end do
end program parabolic1
Method 3 :
program parabolic1
integer, parameter :: n = 10, m = 20
real, parameter :: h = 0.1, k = 0.005 !step sizes
real, dimension (0:n) :: u,v
integer:: i,j
real::pi,pi2
u(0) = 0.0; v(0) = 0.0; u(n) = 0.0; v(n) =0.0
pi = 4.0*atan(1.0)
pi2 = pi*pi
do i=1, n-1
u(i) = sin( pi*real(i)*h)
end do
do j = 1,m
do i = 1, n-1
v(i) = 0.5*(u(i-1)+u(i+1))
end do
t = real(j)*k !increment in time, now check for steady-state
!steady-state check: Perhaps this is the correct expression I want to use
if( norm2(v) - norm2(u) .LT. 1.0e-7 ) then
print*, 'steady-state condition reached'
exit
end if
do i = 1, n-1 !updating solution
u(i) = v(i)
end do
end do
end program parabolic1
Without discussing which method to determine "closeness" is best or correct (not really being a programming problem) we can focus on what the Fortran parts of the methods are doing.
Method 1 and Method 2 are similar ideas (but broken in their execution), while Method 3 is different (and broken in another way).
Note also that in general one wants to compare the magnitude of the difference abs(v-u) rather than the (signed) difference v-u. With non-monotonic changes over iterations these are quite different.
Method 3 uses norm2(v) - norm2(u) to test whether the arrays u and v are similar. This isn't correct. Consider
norm2([1.,0.])-norm2([0.,1.])
instead of the more correct
norm2([1.,0.]-[0.,1.])
Method 2's
if ( v - u .LT. 1.0e-7 ) then
has the problem of being an invalid array expression, but the "are all points close?" can be written appropriately as
if ( ALL( v - u .LT. 1.0e-7 )) then
(You'll find other questions around here about such array reductions).
Method 1 tries something similar, but incorrectly:
do i = 1,n-1
if ( v(i) - u(i) .LT. 1.0e-7 ) then
print*, 'steady-state condition reached'
exit
end if
end do
This is incorrect in one big way, and one subtle way.
First, the loop is exited when the condition tests true the first time, with a message saying the steady state is reached. This is incorrect: you need all values close, while this is testing for any value close.
Second, when the condition is met, you exit. But you don't exit the time iteration loop, you exit the closeness testing loop. (exit without a construct name leaves the innermost do construct). You'll be in exactly the same situation, running again immediately after this innermost construct whether the tested condition is ever or never met (if ever met you'll get the message also). You will need to use a construct name on the time loop.
I won't show how to do that (again there are other questions here about that), because you also need to fix the test condition, by which point you'll be better off using if(all(... (corrected Method 2) without that additional do construct.
For Methods 1 and 2 you'll have something like:
if (all(v-u .lt 1e-7)) then
print *, "Converged"
exit
end if
And for Method 3:
if (norm2(v-u) .lt. 1e-7) then
print *, "Converged"
exit
end if

Fortran Error: 'y' argument of 'datan2' intrinsic at (1) must be REAL

I want to calculate z value as the coordinate in range of x:-50~50 and y:-50~50 like below code.
program test
implicit none
! --- [local entities]
real*8 :: rrr,th,U0,amp,alp,Ndiv
real*8 :: pi,alpR,NR,Rmin,Rmax,z
integer :: ir, i, j
do i=0, 50
do j=0, 50
th=datan2(i,j)
pi=datan(1.d0)*4.d0
!
Ndiv= 24.d0 !! Number of circumferential division
alp = 90.d0/180.d0*pi !! phase [rad]
U0 = 11.4d0 !! average velocity
amp = 0.5d0 !! amplitude of velocity
Rmin = 10 !! [m]
Rmax = 50 !! [m]
NR = 6.d0 !! Number of radial division
!
rrr=dsqrt(i**2+j**2)
ir=int((rrr-Rmin)/(Rmax-Rmin)*NR)
alpR=2.d0*pi/dble(Ndiv)*dble(mod(ir,2))
z=U0*(1.d0+amp*dsin(0.5d0*Ndiv*th+alp+alpR))
write(*,*) 'i, j, z'
write(*,*) i, j, z
end do
end do
stop
end program test
But I couldn't make it work like below error. I think because i, j are in datan(i,j). How should I change these code?
test.f90:10.16:
th=datan2(i,j)
1
Error: 'y' argument of 'datan2' intrinsic at (1) must be REAL
test.f90:21.16:
rrr=dsqrt(i**2+j**2)
1
Error: 'x' argument of 'dsqrt' intrinsic at (1) must be REAL
Inspired by the comments of #Rodrigo Rodrigues, #Ian Bush, and #Richard, here is a suggested rewrite of the code segment from #SW. Kim
program test
use, intrinsic :: iso_fortran_env, only : real64
implicit none
! --- [local entities]
! Determine the kind of your real variables (select one):
! for specifying a given numerical precision
integer, parameter :: wp = selected_real_kind(15, 307) !15 digits, 10**307 range
! for specifying a given number of bits
! integer, parameter :: wp = real64
real(kind=wp), parameter :: pi = atan(1._wp)*4._wp
real(kind=wp) :: rrr, th, U0, amp, alp, Ndiv
real(kind=wp) :: alpR, NR, Rmin, Rmax, z
integer :: ir, i, j
do i = 0, 50
do j = 0, 50
th = atan2(real(i, kind=wp), real(j, kind=wp))
!
Ndiv= 24._wp !! Number of circumferential division
alp = 90._wp/180._wp*pi !! phase [rad]
U0 = 11.4_wp !! average velocity
amp = 0.5_wp !! amplitude of velocity
Rmin = 10 !! [m]
Rmax = 50 !! [m]
NR = 6._wp !! Number of radial division
!
rrr = sqrt(real(i, kind=wp)**2 + real(j, kind=wp)**2)
ir = int((rrr - Rmin) / (Rmax - Rmin) * NR)
alpR = 2._wp * pi / Ndiv * mod(ir, 2)
z = U0 * (1._wp + amp * sin(0.5_wp * Ndiv * th + alp + alpR))
!
write(*,*) 'i, j, z'
write(*,*) i, j, z
end do
end do
stop
end program test
Specifically, the following changes were made with respect to the original code posted:
Minimum change to answer the question: casting integer variables i and j to real values for using them in the real valued functions datan and dsqrt.
Using generic names for intrinsic procedures, i.e sqrt instead of dsqrt, atan instead of datan, and sin instead of dsin. One benefit of this approach, is that the kind of working precision wp can be changed in one place, without requiring explicit changes elsewhere in the code.
Defining the kind of real variables and calling it wp. Extended discussion of this topic, its implications and consequences can be found on this site, for example here and here. Also #Steve Lionel has an in depth post on his blog, where his general advice is to use selected_real_kind.
Defining pi as a parameter calculating its value once, instead of calculating the same value repeatedly within the nested for loops.

How to calculate Pi using Monte Carlo Simulation?

I'm attempting to write a FORTRAN 90 program that calculates Pi using random numbers. These are the steps I know I need to undertake:
Create a randomly placed point on a 2D surface within the range [−1, 1] for x and y, using call random_number(x).
calculate how far away the point is from the origin, i'll need to do both of these steps for N points.
for each N value work out the total amount of points that are less than 1 away from origin. Calculate pi with A=4pir^2
use a do loop to calculate pi as a function of N and output it to a data file. then plot it in a graphing package.
This is what I have:
program pi
implicit none
integer :: count, n, i
real :: r, x, y
count = 0
CALL RANDOM_SEED
DO i = 1, n
CALL RANDOM_NUMBER(x)
CALL RANDOM_NUMBER(y)
IF (x*x + y*Y <1.0) count = count + 1
END DO
r = 4 * REAL(count)/n
print *, r
end program pi
I know i've missed out printing the results to the data file, i'm not sure on how to implement this.
This program gives me a nice value for pi (3.149..), but how can I implement step 4, so that it outputs values for pi as a function of N?
Thanks.
Here is an attempt to further #meowgoesthedog effort...
Program pi
implicit none
integer :: count, n, i
real :: r, x, y
count = 0
Integer, parameter :: Slice_o_Pie = 8
Integer :: Don_McLean
Logical :: Purr = .FALSE.
OPEN(NEWUNIT=Don_McLean, FILE='American.Pie')
CALL RANDOM_SEED
DO i = 1, n
CALL RANDOM_NUMBER(x)
CALL RANDOM_NUMBER(y)
IF (x*x + y*Y <1.0) count = count + 1
Purr = .FALSE.
IF(MODULO(I, Slice_o_Pie) == 0) Purr = .TRUE.
IF (Purr) THEN
r = 4 * REAL(count)/i
print *, i, r
WRITE(LUN,*) 'I=',I,'Pi=',Pi
END IF
END DO
CLOSE(Don_McLean)
end program pi
Simply put the final calculation step inside the outer loop, and replace n with i. Also maybe add a condition to limit the number of results printed, e.g. i % 100 = 0 to print every 100 iterations.
program pi
implicit none
integer :: count, n, i
real :: r, x, y
count = 0
CALL RANDOM_SEED
DO i = 1, n
CALL RANDOM_NUMBER(x)
CALL RANDOM_NUMBER(y)
IF (x*x + y*Y <1.0) count = count + 1
IF ([condition])
r = 4 * REAL(count)/i
print *, i, r
END IF
END DO
end program pi

Inaccurate results from Lagrange Interpolation in Fortran

I have written a Fortran program to compute the Lagrange interpolation of two data sets: x,G. I am not able to evaluate the defined function correctly. Please see what I did wrong as while my program runs, the numbers are not at all accurate for the fxn see first two programs (Matlab code) to see actual result). They are provided by the author and are what I am trying to emulate on Fortran:
%% example 1.1 langrange interpolation %(Matlab)
% X : interpolation points
% Y : value of f(X)
% x : points where we want an evaluation of P(x),
% where P is the interpolator polynomial
x = [-1:0.01:1];
X = [-1:0.20:1];
y = 1./(1+25*x.^2);
Y = 1./(1+25*X.^2);
pol = lagrange_interp(X,Y,x)
%plot(x,pol,'k',x,y,'k--',X,Y,'k.');
legend('Lagrange Polynomial','Expected behavior','Data Points');
function polynomial = lagrange_interp(X,Y,x) %(Matlab)
n = length(X);
phi = ones(n,length(x));
polynomial = zeros(1,length(x));
i = 0;
j = 0;
for i = [1:n]
for j = [1:n]
if not(i==j)
phi(i,:) = phi(i,:).*(x-X(j))./(X(i)-X(j));
end;
end;
end;
for i = [1:n]
polynomial = polynomial + Y(i)*phi(i,:);
end;
!Lagrange Interpolation example !(Fortran)
program Lagrange
implicit none
integer:: i
integer, parameter:: n=10
integer, parameter:: z=201
integer, parameter:: z1=11
real, parameter:: delta=.01
real,parameter:: delta2=.20
real, dimension(1:z):: x,G,y,H
real*8, dimension(1:n):: M
real*8, dimension(1:n):: linterp(n)
x(1)=-1
G(1)=-1
do i=2,z
x(i)=x(i-1)+delta
y(i)=1/(1+25*(x(i)**2))
end do
print*, "The one-dimensional array x is:", x(1:z)
print*, "The one dimensional array y is", y(1:z)
do i=2,z1
G(i)=G(i-1)+delta2
H(i)=1/(1+25*(G(i)**2))
end do
print*,"The other one-dimensional array G is:", G(1:z1)
print*, "Then the one dimensional array H is", H(1:z1)
M=linterp(1:n)
print*, M(1:n)
end program
!Lagrange interpolation polynomial function !(Fortran)
real*8 function linterp(n)
implicit none
integer,parameter:: n=10
integer, dimension(1:n):: poly,pol
integer:: i, j
i=0
j=0
do i=1,n
do j=1,n
if (i/=j)then
poly(i,j)=poly(i,j)*(x(i)-G(j))/(y(i)-G(j))
end if
end do
end do
print*, poly(i,j)
do i=1,n
pol(i)=pol(i)+H(i)*poly(i,j)
end do
print*, pol(1:n)
end function

FFTW: Inverse transform of forward transform of 1/cosh function is wrong

I'm attempting to take the inverse transform of a complex 1D arrays forward transform in Fortran 90 and fftw. However, the output I receive from the inverse transform is at times completely different from the original input, whereas some values possess an incorrect real section but a correct imaginary part and a few match the original values perfectly.
I've noticed that this issue disappears if dx (the spacing between x values) is reduced to 0.01. Increasing n to compensate for this reduction in x's range then results in the issue resurfacing.
At this point, I believe the issue lies in the 1/cosh segment of the input array as I've been able to replace this with other complex inputs with no issues.
This code is adapted from a MATLAB file in which the form of the input only differs due to MATLAB using sech instead of 1/cosh.
Fortran isn't my 'go to' language so I'm wondering if I've made some normally obvious mistake due to my familiarity with python/matlab .
As for more specifics on the outputs,
The matlab version of this code produces the same values for the in array but the operation of the forward transform and the inverse transform produce different results,
Matlab
out2(2) = 5.5511e-17 + 6.9389e-18i
out2(3) = 5.5511e-17 - 1.3878e-17i
out2(4) = 5.5511e-17 + 2.7756e-17i
out2(1024) = 0.9938 + 0.0994i
out2(2048) = 0 - 1.3878e-17i
Fortran
out2(2) = -5.5511151231257827E-017 - 6.9388939039072284E-018i
out2(3) = 0.0000000000000000 + 1.3877787807814457E-017i
out2(4) = 0.0000000000000000 + 0.0000000000000000i
out(1024) = 0.99380163159683255 + 9.9410098890158616E-002i
out2(2048) = -5.5511151231257827E-017 - 6.9388939039072284E-018i
PROGRAM FFTEXAMPLE
implicit none
include 'fftw3.f'
INTEGER :: n, j, nindex, i
REAL :: dx
DOUBLE COMPLEX, ALLOCATABLE :: in(:), out(:), in2(:), out2(:)
REAL(kind = 8), ALLOCATABLE :: x(:)
INTEGER*8 :: plan, plan2
nindex = 11
n = 2 ** nindex
dx = 0.05 ! Spacing between x array values
allocate( in(n), out(n), x(n), in2(n), out2(n) )
CALL dfftw_plan_dft_1d( plan, n, in, out, FFTW_FORWARD, FFTW_ESTIMATE )
CALL dfftw_plan_dft_1d( plan2, n, in2, out2, FFTW_BACKWARD, FFTW_ESTIMATE )
x = (/ (-dx*n/2 + (i-1)*dx, i=1, n) /) ! Seeds x array from -51.2 to 51.15
! Create values for the input array
DO j = 1, n, 1
in(j) = 1/cosh ( x(j)/1.0040 ) * exp( (0.0, -1.0) * 1.9940 * x(j) )
END DO
CALL dfftw_execute_dft( plan, in, out ) ! FWD transform
!DO j = 1, n, 1
! in2(j) = cmplx(REAL(out(j)), AIMAG(out(j)))
!END DO
in2 = out
CALL dfftw_execute_dft( plan2, in2, out2 ) ! Inverse transform
out2 = out2/n ! Divide output by n to normalise
CALL dfftw_destroy_plan( plan )
CALL dfftw_destroy_plan( plan2 )
END PROGRAM