Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding pFUnit Support for Unit Testing in obs2ioda #23

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Check CMake version
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.20)
amstokely marked this conversation as resolved.
Show resolved Hide resolved

include("${CMAKE_SOURCE_DIR}/cmake/Obs2Ioda_Functions.cmake")
# Define the project
Expand All @@ -15,9 +15,21 @@ install(DIRECTORY ${CMAKE_BINARY_DIR}/${OBS2IODA_MODULE_DIR}/ DESTINATION ${CMAK

# Set the Fortran compiler and flags
set(NCEP_BUFR_LIB CACHE STRING "" )
# Set to ON to build tests. Default is OFF. If testing is enabled,
# pFUnit is required to be installed.
set(BUILD_TESTS CACHE BOOL OFF)
# Set the path to the pFUnit installation directory if BUILD_TESTS is ON
set(PFUNIT_DIR CACHE STRING "" )

# Find required packages
find_package(NetCDF REQUIRED COMPONENTS Fortran C)

add_subdirectory("${CMAKE_SOURCE_DIR}/obs2ioda-v2")

if (BUILD_TESTS)
enable_testing()
set(CMAKE_PREFIX_PATH ${PFUNIT_DIR})
find_package(PFUNIT REQUIRED)
add_subdirectory("${CMAKE_SOURCE_DIR}/test")
endif()

6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ File types that obs2ioda can handle:
* HS_H08_YYYYMMDD_HH00_BNN_FLDK_R20_S0210.DAT
* OR_ABI-L1b-Rad nc files

## See [obs2ioda-v1/README.md](https://github.com/jamiebresch/obs2ioda/blob/main/obs2ioda-v1/README.md) for compilation and usage.
## See [obs2ioda-v1/README.md](https://github.com/NCAR/obs2ioda/blob/main/obs2ioda-v1/README.md) for compilation and usage.

## See [obs2ioda-v2/README.md](https://github.com/jamiebresch/obs2ioda/blob/main/obs2ioda-v2/README.md) for compilation and usage.
## See [obs2ioda-v2/README.md](https://github.com/NCAR/obs2ioda/blob/main/obs2ioda-v2/README.md) for compilation and usage.

## See [goes_abi/README.md](https://github.com/jamiebresch/obs2ioda/blob/main/goes_abi/README.md) for compilation and usage.
## See [goes_abi/README.md](https://github.com/NCAR/obs2ioda/blob/main/goes_abi/README.md) for compilation and usage.

## Observation data sources:
* https://nomads.ncep.noaa.gov/pub/data/nccf/com/gfs/prod/
Expand Down
76 changes: 60 additions & 16 deletions obs2ioda-v2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@
obs2ioda-v2 utilizes CMake as its primary build system. Follow the steps below to build the project:

### Prerequisites
Please make sure the following libraries are installed:
- NetCDF
- NCEP BUFR library. (Instructions for installing the NCEP BUFR library are provided in a subsequent section)

Prior to building `obs2ioda`, ensure that you have installed the following libraries:

- **CMake**: Required (version 3.20 or higher).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should now read 3.12.

- **NetCDF**: Required.
- **NCEP BUFR Library**: Required.
- **pFUnit**: Optional (Needed for testing).

For instructions on how to install `pFUnit` and the `NCEP BUFR` library , please refer to the respective sections:

- [Installing NCEP BUFR Library](#installing-ncep-bufr-library)
- [Installing pFUnit](#installing-pfunit)

If you have an environment preconfigured for `mpas-jedi`, simply source that environment prior to building `obs2ioda`.
jim-p-w marked this conversation as resolved.
Show resolved Hide resolved

Expand All @@ -14,35 +23,45 @@ If you have an environment preconfigured for `mpas-jedi`, simply source that env
```bash
git clone https://github.com/NCAR/obs2ioda.git <OBS2IODA_ROOT_DIR>
```
2. Create a new directory `build` and navigate into it:
```bash
mkdir build && cd build
```
3. Locate the NCEP BUFR library by executing the following command in the `NCEP BUFR` library's build directory:
1. Create a new directory `<OBS2IODA_BUILD_DIR>` and navigate into it:
```bash
find . -name *libbufr*
mkdir <OBS2IODA_BUILD_DIR> && cd <OBS2IODA_BUILD_DIR>
```
4. Next, run CMake to configure the build. Specify the `CMAKE_BUILD_TYPE` option to set the build type. Currently, the supported types are `Release`, `RelWithDebInfo`, and `Debug`. Don't forget to include the path to the NCEP BUFR library:
1. Set the variable OBS2IODA_CMAKE_ARGS to include the `obs2ioda` root directory and the absolute path of the `NCEP BUFR` library:
```bash
OBS2IODA_CMAKE_ARGS="<OBS2IODA_ROOT_DIR> -DNCEP_BUFR_LIB=<NCEP_BUFR_LIB_PATH>"
amstokely marked this conversation as resolved.
Show resolved Hide resolved
```
1. If building in `Debug` mode, append the following to `OBS2IODA_CMAKE_ARGS`:
```bash
OBS2IODA_CMAKE_ARGS="${OBS2IODA_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=Debug"
```
1. If building unit tests, append the following to `OBS2IODA_CMAKE_ARGS`:
```bash
OBS2IODA_CMAKE_ARGS="${OBS2IODA_CMAKE_ARGS} -DBUILD_TESTS=ON -DPFUNIT_DIR=<PFUNIT_DIR>"
```
where `<PFUNIT_DIR>` is the directory of the `pFUnit` installation.

1. Next, run CMake to configure the build.
```bash
cmake <OBS2IODA_ROOT_DIR> -DNCEP_BUFR_LIB=<NCEP_BUFR_LIB_PATH> -DCMAKE_BUILD_TYPE=<BUILD_TYPE>
cmake <OBS2IODA_ROOT_DIR> ${OBS2IODA_CMAKE_ARGS}
amstokely marked this conversation as resolved.
Show resolved Hide resolved
```
5. Finally, build `obs2ioda` using `CMake`'s build tool. In this case, we use `GNU Make`, but other build tools supported by `CMake` can be used:
1. Finally, build `obs2ioda` using `CMake`'s build tool. In this case, we use `GNU Make`, but other build tools supported by `CMake` can be used:
```bash
make
```
The `obs2ioda-v2` executable will reside in the `bin` directory within the build directory.

---
## Installing NCEP BUFR Library
### Installing NCEP BUFR Library
To install the NCEP BUFR library, follow these steps:

1. Clone the NCEP BUFR repository into a directory of your choice (`<NCEP_BUFR_ROOT_DIR>`):
```bash
git clone https://github.com/NOAA-EMC/NCEPLIBS-bufr.git <NCEP_BUFR_ROOT_DIR>
```
2. Create a new directory `build` and navigate into it:
2. Create a new directory `<NCEP_BUFR_BUILD_DIR>` and navigate into it:
```bash
mkdir build && cd build
mkdir <NCEP_BUFR_BUILD_DIR> && cd <NCEP_BUFR_BUILD_DIR>
```
3. Run CMake to configure the build (Ensure NetCDF is installed):
```bash
Expand All @@ -54,9 +73,34 @@ To install the NCEP BUFR library, follow these steps:
```
5. To locate the NCEP BUFR library, run:
```bash
find . -name *libbufr*
bash -c "find . -name *libbufr*"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a specific reason this find command needs to be run in the bash shell?

```
Remember to note down the library path (`<NCEP_BUFR_LIB_PATH>`) required for the build process of `obs2ioda-v2`.

---

### Installing pFUnit

To install `pFUnit` version 4.10.0, follow these steps:

1. Clone the `pFUnit` repository into your desired directory (`<PFUNIT_ROOT_DIR>`):
```bash
git clone https://github.com/Goddard-Fortran-Ecosystem/pFUnit.git -b v4.10.0 <PFUNIT_ROOT_DIR>
```

2. Navigate to the `pFUnit` directory:
```bash
cd <PFUNIT_ROOT_DIR>
```
Follow the instructions for building version 4.10.0 with `CMake`, provided on the [pFUnit GitHub page](https://github.com/Goddard-Fortran-Ecosystem/pFUnit?tab=readme-ov-file#building-and-installing-pfunit).

**Note:** If you do not intend to use this installation of `pFUnit` for testing `MPI` or `OpenMP` libraries, pass the following options to `CMake` during the build process:
```bash
-DUSE_MPI=OFF -DUSE_OPENMP=OFF
```

amstokely marked this conversation as resolved.
Show resolved Hide resolved
---

## caveate
NetCDF-Fortran interface does not allow reading/writing NF90_STRING, so ``station_id`` and ``variable_names`` are still written out as
``char station_id(nlocs, nstring)``
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/fortran)
5 changes: 5 additions & 0 deletions test/fortran/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
add_pfunit_ctest(Test_foo
TEST_SOURCES Test_foo_mod.pf
LINK_LIBRARIES v2
)
target_include_directories(Test_foo PRIVATE ${PFUNIT_INCLUDE_DIRS})
45 changes: 45 additions & 0 deletions test/fortran/Test_foo_mod.pf
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module Test_foo_mod
use funit
use iso_c_binding, only: c_ptr, c_loc, c_f_pointer, c_float, c_double
use kinds, only: r_double, r_kind
implicit none

contains

! Subroutine foo
! This subroutine demonstrates a bug that would not cause obs2ioda to crash,
! but rather would introduce a potentially subtle numerical error that would
! only appear in some calculations. The bug is in the pointer type declaration.
subroutine foo(a, b)
real(r_double), target, intent(in) :: a
real(r_double), intent(out) :: b
type(c_ptr) :: b_cptr
! real(c_double), pointer :: b_pointer ! Corrected code.
real, pointer :: b_pointer ! Purposeful mistake to illustrate the importance of unit testing.
b_cptr = c_loc(a)
call c_f_pointer(b_cptr, b_pointer)
b = b_pointer
end subroutine foo

! Function acos_foo
! This function calculates the arccosine of the input value divided by 10.
function acos_foo(val)
real(r_double), intent(in) :: val
real(r_double) :: acos_foo
acos_foo = acos(val / 10)
end function acos_foo

! Test subroutine for foo
! This test demonstrates the bug in the foo subroutine. It shows that the bug
! would not cause obs2ioda to crash, but would introduce a potentially subtle
! numerical error that only appears in some calculations.
@Test
subroutine test_foo()
real(r_double) :: a, b, c
a = 1.3
call foo(a, b)
c = acos_foo(b)
write(*, *) 'Taking the arccosine of b / 10 = ', b / 10, ' gives c = ', c
call assertEqual(a, b)
end subroutine test_foo
end module Test_foo_mod