---
# System prepended metadata

title: Group hackathon on interpolation code (Fortran)

---

# Group hackathon on interpolation code (Fortran)


## Compilation 
- Code compiled and run on laptop with gfortran on Ubuntu. 
- But not easy for everyone to compile locally.
- Tried on cocal but did not work very well
- Im not so familiar with cocalc
- so he problem is not cocalc 


## Share the code & development in Github

### Github username

- annefou
- maryjanebopape
- pmulovhedzi
- leeseetja
- NohlahlaDlamini

Create a github repository under github.com/annefou

https://github.com/annefou/hackathon-south-africa


## Compile and run the code

- Open cocalc.com and create a terminal
    - let us know when you are done.
        - annefou done.
        - leeseetja done
        - NohlahlaDlamini done
        - maryjanebopape done

- console.cloud.google.com seems to be free and we can run 
    - open console and clone repository
        - annefou done.
        - leeseetja done.
        - NohlahlaDlamini done

Try out to update the console:

```
sudo apt-get update
sudo apt-get install -y gcc gfortran

```

Get the code:

```
git clone https://github.com/annefou/hackathon-south-africa
cd hackathon-south-africa
```
Then compile and run the code:


```
gfortran interpolate_sub.f90 -o interpolate.x

```

And to run

```
./interpolate.x
```

## Installation of pFUnit (Fortran unit testing)

You may need to install additional packages (on ubuntu):

```
sudo apt-get install cmake make python3 m4
```

And then 

```
cd $HOME
git clone https://github.com/Goddard-Fortran-Ecosystem/pFUnit.git

cd pFUnit
mkdir build
cd build
cmake ..
make
make tests
make install
```

## Test pFUnit with "custom" code

- Take the test from CodeRefinery test lesson: https://coderefinery.github.io/testing/

Let's try an existing Fortran test with pfUnit:

https://coderefinery.github.io/testing/test-design/

We create test-cr.f90 (use any of your favourite editor vi, nano, etc.) with:

```
module factorial_mod
contains
   ! computes the factorial of n
   integer function factorial(n)
      implicit none
      integer, intent(in) :: n
      integer r
      integer i
      if(n < 0) then
         write(*,*) 'Received negative input'
         stop
      end if
      r = 1
      do i = 1,n
         r = r*i
      end do
      factorial=r
   end function factorial
end module factorial_mod
```

Compile:

```
gfortran -c test.cr.f90
```

Action: Anne to create a makefile to test the code (and that can be reused for interpolate_sub.f90)

## Adding test for interpolate_sub.f90

What kind of tests should we do?

- unrealistic temperatures


Plan:
- implement one simple test
- Add github action to automate testing
- Create and implement more tests

## Hackathon day

- Create mybinder environment & badge snippet: 
    - go to https://mybinder.org/
    - and fill information as shown below e.g. fill the **Github repository URL**: https://github.com/annefou/hackathon-south-africa
    
    ![](https://i.imgur.com/YNHwK2g.png)

    
- test mybinder to create test environment
    - Does it work for you?

- Open your web browser and go to https://github.com/annefou/hackathon-south-africa

![](https://i.imgur.com/KTck5Hs.png)

- mybinder documentation: https://mybinder.readthedocs.io/en/latest/introduction.html

When compiling with mybinder terminal/console, we use `$FC` environment variable to compile (because the compiler name is weird/difficult to remember `/srv/conda/envs/notebook/bin/x86_64-conda-linux-gnu-gfortran`)

```
$FC interpolate_sub.f90 -o interpolate.x
```

## Testing/Using pFUnit

There is a very nice repository with examples on how to run/use pFUNIT:
https://github.com/Goddard-Fortran-Ecosystem/pFUnit_demos

### Clone the pFUNIT demo repository

```
git clone https://github.com/Goddard-Fortran-Ecosystem/pFUnit_demos.git
```

We will now test one example in Trivial folder:

```
cd pFUnit_demos/Trivial
```

Edit the Makefile and add `-lgomp` in `my_tests_OTHER_LIBRARIES`:

```
SRCS := square.F90
OBJS := $(SRCS:%.F90=%.o)

all: my_tests

libsut.a: $(OBJS)
	$(AR) -r $@ $?

ifeq (nagfor,$(findstring nagfor,$(FC)))
  FFLAGS += -fpp
endif

%.o : %.F90
	$(FC) -c $(FFLAGS) $<


LATEST_PFUNIT_DIR := $(lastword $(shell echo $(wildcard $(PFUNIT_DIR)/PFUNIT-4.*) | xargs -n1 | sort -V))
include $(LATEST_PFUNIT_DIR)/include/PFUNIT.mk

FFLAGS += $(PFUNIT_EXTRA_FFLAGS)

test_square.o: square.mod
square.mod: square.o

my_tests: libsut.a

my_tests_TESTS := test_square.pf
my_tests_REGISTRY :=
my_tests_OTHER_SOURCES :=
my_tests_OTHER_LIBRARIES := -L. -lsut -lgomp
my_tests_OTHER_INCS :=

$(eval $(call make_pfunit_test,my_tests))


clean:
	$(RM) *.o *.mod *.a *.inc test_square.F90

```

And then:

First we need to set pFUNIT environment variable:

```
 export PFUNIT_DIR=$HOME/packages
```

Then we can compile test and run it:

```
make clean
make all
./my_tests
```

It should return:

```
Time:         0.001 seconds

 OK
 (1 test)
```

### Exercice: make the test fail

For instance, change square.F90:

```
module Square_mod
contains
   
   pure real function square(x)
      real, intent(in) :: x
      square = x**3
   end function square
end module Square_mod
```

Then without changing the test, I check if my changes are correct:

```
make clean
make
./my_tests
```

And it returns:

```
 ./my_tests
.F
Time:         0.001 seconds

Failure
 in:
test_square_suite.test_square
  Location:
[test_square.pf:6]
square(3)
AssertEqual failure:
      Expected: <9.00000000>
        Actual: <27.0000000>
    Difference: <18.0000000> (greater than tolerance of 0.00000000)

 FAILURES!!!
Tests run: 1, Failures: 1, Errors: 0
, Disabled: 0
ERROR STOP *** Encountered 1 or more failures/errors during testing. ***

Error termination. Backtrace:
#0  0x55b0ec9d4b03 in __funit_MOD_finalize
        at /home/jovyan/pFUnit/src/funit/FUnit.F90:171
#1  0x55b0ec9e4def in funit_main_
        at /home/jovyan/pFUnit/src/funit/funit_main.F90:17
#2  0x55b0ec98a10f in ???
#3  0x7f66166e2bf6 in ???
#4  0x55b0ec98a160 in ???
```

The expected value is still 9.0 but now I got 27.0. so the changes I implemented in `square.F90` is not correct.

### Testing interpolate_sub.F90

1. Which test to add: we will check the subroutine INTERP and check that the values are "correct"


BREAK: 10mn --> 11:45 SA Time

Register your changes with git:

git add interpolate_sub.f90
git add inter_mod.f90

Then we need to setup username and email address for git:

configure git:
```
git config --global user.name "Your name here"
git config --global user.email "your_email@example.com"
```

Sor for me:
```
git config --global user.name "Anne Fouilloux"
git config --global user.email "annefou@geo.uio.no"

```

Then we "commit":

```
git commit -m "create a new Fortran module for interp subroutine"
```

On github: https://github.com/annefou/hackathon-south-africa

we still do not have this change yet so we need to "push" the changes to github:

```
git push 
```

Ask you for username (maryjanebopape) and password.

Then it is updated on github.

For everyone else, we need to update our local copy:

``` 
git pull
```

Do you get Mary-Jane's changes on your local "copy"?

And we can now check our local copy works:

Compile:

```
$FC Inter_mod.f90 interpolate_sub.f90  -o interpolate.x
```

And run:

```
./interpolate.x
```

### test for interp
Values at 4 points: 29, 30, 31 and 32
Answer: 30.5
Latitude: south point: -33, north point: -31
Longitude: west point: 20, east point: 22
Station point: 21; -32

## Hackathon cont. 30/04/2021

- start software environment with mybinder (takes a few minutes)
- Open a terminal and then clone pFUnit_demos:

```
git clone https://github.com/Goddard-Fortran-Ecosystem/pFUnit_demos
```

- modify Inter_mod.f90 and replace subroutine by a function.

```
 SUBROUTINE INTERP(I,J,K,TEMP,LONS,LATS,LONSTAT,LATSTAT,NEW)
     IMPLICIT NONE
     INTEGER, INTENT(IN) :: I
     INTEGER, INTENT(IN) :: J
     INTEGER, INTENT(IN) :: K
     REAL, INTENT(IN) :: TEMP(I-1:I,J-1:J)
     REAL, INTENT(IN) :: LONS(I-1:I)
     REAL, INTENT(IN) :: LATS(J-1:J)
     REAL, INTENT(IN) :: LONSTAT(K:K)
     REAL, INTENT(IN) :: LATSTAT(K:K)
     REAL, INTENT(OUT) :: NEW(K:K)
!    REAL, INTENT(INOUT) :: LATSSTAT
     REAL :: VAR1
     REAL :: VAR2
       
       VAR1=(LONS(I)-LONSTAT(K))/(LONS(I)-LONS(I-1))*TEMP(I-1,J-1)+&
            (LONSTAT(K)-LONS(I-1))/(LONS(I)-LONS(I-1))*TEMP(I,J-1)
       VAR2=(LONS(I)-LONSTAT(K))/(LONS(I)-LONS(I-1))*TEMP(I-1,J)+&
            (LONSTAT(K)-LONS(I-1))/(LONS(I)-LONS(I-1))*TEMP(I-1,J)
       NEW(K)=(LATS(J)-LATSTAT(K))/(LATS(J)-LATS(J-1))*VAR1+&
            (LATSTAT(K)-LATS(J-1))/(LATS(J)-LATS(J-1))*VAR2

    END SUBROUTINE INTERP
```

replaced by 

```
 pure real function INTERP(I,J,K,TEMP,LONS,LATS,LONSTAT,LATSTAT)
     IMPLICIT NONE
     INTEGER, INTENT(IN) :: I
     INTEGER, INTENT(IN) :: J
     INTEGER, INTENT(IN) :: K
     REAL, INTENT(IN) :: TEMP(I-1:I,J-1:J)
     REAL, INTENT(IN) :: LONS(I-1:I)
     REAL, INTENT(IN) :: LATS(J-1:J)
     REAL, INTENT(IN) :: LONSTAT(K:K)
     REAL, INTENT(IN) :: LATSTAT(K:K)
!    REAL, INTENT(INOUT) :: LATSSTAT
     REAL :: VAR1
     REAL :: VAR2
       
       VAR1=(LONS(I)-LONSTAT(K))/(LONS(I)-LONS(I-1))*TEMP(I-1,J-1)+&
            (LONSTAT(K)-LONS(I-1))/(LONS(I)-LONS(I-1))*TEMP(I,J-1)
       VAR2=(LONS(I)-LONSTAT(K))/(LONS(I)-LONS(I-1))*TEMP(I-1,J)+&
            (LONSTAT(K)-LONS(I-1))/(LONS(I)-LONS(I-1))*TEMP(I-1,J)
       INTERP=(LATS(J)-LATSTAT(K))/(LATS(J)-LATS(J-1))*VAR1+&
            (LATSTAT(K)-LATS(J-1))/(LATS(J)-LATS(J-1))*VAR2

    END Function INTERP
```

update test_Inter_mod.pf

```
@test
subroutine test_Inter_mod()
   use Inter_mod
   use funit
  Implicit None
  Integer:: I=2
  Integer:: J=2
  Integer:: K=1
  REAL :: TEMP(2,2)
  REAL :: LON(2)
  REAL :: LAT(2)
  REAL :: LONSTAT
  REAL :: LATSTAT

  TEMP(1,2)=29.
  TEMP(2,2)=30.
  TEMP(1,1)=31.
  TEMP(2,1)=32.
  LAT(2) = -31.
  LAT(1) = -33.
  LON(2) = 22.
  LON(1) = 20.
  LONSTAT = 21.
  LATSTAT = -32.

  
	!Values at 4 points: 29, 30, 31 and 32
	!Answer: 30.5
	!Latitude: south point: -33, north point: -31
	!Longitude: west point: 20, east point: 22
	!Station point: 21; -32

   @assertEqual(30.5, INTERP(I,J,K,TEMP,LON,LAT,LONSTAT,LATSTAT), 'INTERP(I,J,K,TEMP,LON,LAT,LONSTAT,LATSTAT)')

end subroutine test_Inter_mod

```

- copy and existing [makefile](https://raw.githubusercontent.com/Goddard-Fortran-Ecosystem/pFUnit_demos/main/Trivial/Makefile) from pFUNIT_demos

```
SRCS := Inter_mod.F90
OBJS := $(SRCS:%.F90=%.o)

all: my_tests

libsut.a: $(OBJS)
        $(AR) -r $@ $?

ifeq (nagfor,$(findstring nagfor,$(FC)))
  FFLAGS += -fpp
endif

%.o : %.F90
        $(FC) -c $(FFLAGS) $<


LATEST_PFUNIT_DIR := $(lastword $(shell echo $(wildcard $(PFUNIT_DIR)/PFUNIT-4.*) | xargs -n1 | sort -V))
include $(LATEST_PFUNIT_DIR)/include/PFUNIT.mk

FFLAGS += $(PFUNIT_EXTRA_FFLAGS)

test_Inter_mod.o: Inter_mod.mod
Inter_mod.mod: Inter_mod.o

my_tests: libsut.a

my_tests_TESTS := test_Inter_mod.pf
my_tests_REGISTRY :=
my_tests_OTHER_SOURCES :=
my_tests_OTHER_LIBRARIES := -L. -lsut
my_tests_OTHER_INCS :=

$(eval $(call make_pfunit_test,my_tests))

clean:
        $(RM) *.o *.mod *.a *.inc test_Inter_mod.F90
```

- Try to compile

```
make
```

but we got an error because we need to define `PFUNIT_DIR`

```
export PFUNIT_DIR=/home/jovyan/packages/
```

Failed with an error with lonstat because we did not define it as an array of 1. So let's update it:


update test_Inter_mod.pf

```
@test
subroutine test_Inter_mod()
   use Inter_mod
   use funit
  Implicit None
  Integer:: I=2
  Integer:: J=2
  Integer:: K=1
  REAL :: TEMP(2,2)
  REAL :: LON(2)
  REAL :: LAT(2)
  REAL :: LONSTAT(1)
  REAL :: LATSTAT(1)

  TEMP(1,2)=29.
  TEMP(2,2)=30.
  TEMP(1,1)=31.
  TEMP(2,1)=32.
  LAT(2) = -31.
  LAT(1) = -33.
  LON(2) = 22.
  LON(1) = 20.
  LONSTAT = 21.
  LATSTAT = -32.

  
	!Values at 4 points: 29, 30, 31 and 32
	!Answer: 30.5
	!Latitude: south point: -33, north point: -31
	!Longitude: west point: 20, east point: 22
	!Station point: 21; -32

   @assertEqual(30.5, INTERP(I,J,K,TEMP,LON,LAT,LONSTAT,LATSTAT), 'INTERP(I,J,K,TEMP,LON,LAT,LONSTAT,LATSTAT)')

end subroutine test_Inter_mod

```

We also need to update makefile:

```
my_tests_OTHER_LIBRARIES := -L. -lsut -lgomp
```

but then the test is failing so we need to investigate why:

```
jovyan@jupyter-annefou-2dhackathon-2dsouth-2dafrica-2dn80iq21v:~$ ./my_tests
.F
Time:         0.000 seconds

Failure
 in:
test_Inter_mod_suite.test_Inter_mod
  Location:
[test_Inter_mod.pf:33]
INTERP(I,J,K,TEMP,LON,LAT,LONSTAT,LATSTAT)
AssertEqual failure:
      Expected: <30.5000000>
        Actual: <30.2500000>
    Difference: <-0.250000000> (greater than tolerance of 0.00000000)

 FAILURES!!!
Tests run: 1, Failures: 1, Errors: 0
, Disabled: 0
ERROR STOP *** Encountered 1 or more failures/errors during testing. ***

Error termination. Backtrace:
#0  0x558a95cfabfc in __funit_MOD_finalize
        at /home/jovyan/pFUnit/src/funit/FUnit.F90:171
#1  0x558a95d0aee8 in funit_main_
        at /home/jovyan/pFUnit/src/funit/funit_main.F90:17
#2  0x558a95cb010f in ???
#3  0x7fa817b28bf6 in ???
#4  0x558a95cb0160 in ???

```

We chose to test the function itself (it is a bit of a hack e.g. we had to remove pure, etc. but at least we can check the values)

```
 MODULE INTER_MOD
   IMPLICIT NONE

    CONTAINS

  real function INTERP(I,J,K,TEMP,LONS,LATS,LONSTAT,LATSTAT)
     IMPLICIT NONE
     INTEGER, INTENT(IN) :: I
     INTEGER, INTENT(IN) :: J
     INTEGER, INTENT(IN) :: K
     REAL, INTENT(INOUT) :: TEMP(I-1:I,J-1:J)
     REAL, INTENT(INOUT) :: LONS(I-1:I)
     REAL, INTENT(INOUT) :: LATS(J-1:J)
     REAL, INTENT(INOUT) :: LONSTAT(K:K)
     REAL, INTENT(INOUT) :: LATSTAT(K:K)
!    REAL, INTENT(INOUT) :: LATSSTAT
     REAL :: VAR1
     REAL :: VAR2
       
       
  TEMP(I-1,J)=29.
  TEMP(2,J)=30.
  TEMP(I-1,J-1)=31.
  TEMP(I,J-1)=32.
  LATS(J) = -31.
  LATS(J-1) = -33.
  LONS(I) = 22.
  LONS(I-1) = 20.
  LONSTAT(K) = 21.
  LATSTAT(K) = -32.
       VAR1=(LONS(I)-LONSTAT(K))/(LONS(I)-LONS(I-1))*TEMP(I-1,J-1)+&
            (LONSTAT(K)-LONS(I-1))/(LONS(I)-LONS(I-1))*TEMP(I,J-1)
       VAR2=(LONS(I)-LONSTAT(K))/(LONS(I)-LONS(I-1))*TEMP(I-1,J)+&
            (LONSTAT(K)-LONS(I-1))/(LONS(I)-LONS(I-1))*TEMP(I-1,J)
       INTERP=(LATS(J)-LATSTAT(K))/(LATS(J)-LATS(J-1))*VAR1+&
            (LATSTAT(K)-LATS(J-1))/(LATS(J)-LATS(J-1))*VAR2

    END Function INTERP
    
   END MODULE INTER_MOD

```

The value returned is still 30.25. Now we see that the interpolation may not be as
simple as we though when we built the test.

We chose to add a tolerance in our test. When interpolation to a station location, we could use different interpolation method but the value should be withi 0.25 C.

```
!@test
subroutine test_Inter_mod()
   use Inter_mod
   use funit
  Implicit None
  Integer:: I=2
  Integer:: J=2
  Integer:: K=1
  REAL :: TEMP(2,2)
  REAL :: LON(2)
  REAL :: LAT(2)
  REAL :: LONSTAT(1)
  REAL :: LATSTAT(1)

  TEMP(1,2)=29.
  TEMP(2,2)=30.
  TEMP(1,1)=31.
  TEMP(2,1)=32.
  LAT(2) = -31.
  LAT(1) = -33.
  LON(2) = 22.
  LON(1) = 20.
  LONSTAT = 21.
  LATSTAT = -32.

 
	!Values at 4 points: 29, 30, 31 and 32
	!Answer: 30.5
	!Latitude: south point: -33, north point: -31
	!Longitude: west point: 20, east point: 22
	!Station point: 21; -32

#line 33 "/home/jovyan/test_Inter_mod.pf"
  call assertEqual(30.5, INTERP(I,J,K,TEMP,LON,LAT,LONSTAT,LATSTAT), tolerance=0.25, &
 & location=SourceLocation( &
 & 'test_Inter_mod.pf', &
 & 33) )
  if (anyExceptions()) return
#line 34 "/home/jovyan/test_Inter_mod.pf"

end subroutine test_Inter_mod

module Wraptest_Inter_mod
   use FUnit
   implicit none
   private

contains


end module Wraptest_Inter_mod

function test_Inter_mod_suite() result(suite)
   use FUnit
   use Wraptest_Inter_mod
   implicit none
   type (TestSuite) :: suite

   class (Test), allocatable :: t

   external test_Inter_mod


   suite = TestSuite('test_Inter_mod_suite')

   if(allocated(t)) deallocate(t)
   allocate(t, source=TestMethod('test_Inter_mod', test_Inter_mod))
   call suite%addTest(t)


end function test_Inter_mod_suite

```

Then it worked. 

When designing your test:
- Think about the "impact" of the algorithm you choose on the final result. Here, we take 4 values of a variable and interpolate it to a station location. What interpolation algorithm would be valid? What is the tolerance? etc.
- Make sure to comment your functions (add a link to a scientific paper or name the algorithm you use)


Now let's clean Inter_mod.F90

```
 MODULE INTER_MOD
   IMPLICIT NONE

    CONTAINS

  pure real function INTERP(I,J,K,TEMP,LONS,LATS,LONSTAT,LATSTAT)
     IMPLICIT NONE
     INTEGER, INTENT(IN) :: I
     INTEGER, INTENT(IN) :: J
     INTEGER, INTENT(IN) :: K
     REAL, INTENT(IN) :: TEMP(I-1:I,J-1:J)
     REAL, INTENT(IN) :: LONS(I-1:I)
     REAL, INTENT(IN) :: LATS(J-1:J)
     REAL, INTENT(IN) :: LONSTAT(K:K)
     REAL, INTENT(IN) :: LATSTAT(K:K)
     REAL :: VAR1
     REAL :: VAR2
       
       VAR1=(LONS(I)-LONSTAT(K))/(LONS(I)-LONS(I-1))*TEMP(I-1,J-1)+&
            (LONSTAT(K)-LONS(I-1))/(LONS(I)-LONS(I-1))*TEMP(I,J-1)
       VAR2=(LONS(I)-LONSTAT(K))/(LONS(I)-LONS(I-1))*TEMP(I-1,J)+&
            (LONSTAT(K)-LONS(I-1))/(LONS(I)-LONS(I-1))*TEMP(I-1,J)
       INTERP=(LATS(J)-LATSTAT(K))/(LATS(J)-LATS(J-1))*VAR1+&
            (LATSTAT(K)-LATS(J-1))/(LATS(J)-LATS(J-1))*VAR2

    PRINT*, 'INTERP = ', (LATS(J)-LATSTAT(K))/(LATS(J)-LATS(J-1))*VAR1+&
            (LATSTAT(K)-LATS(J-1))/(LATS(J)-LATS(J-1))*VAR2
    END Function INTERP
    
   END MODULE INTER_MOD
```