Skip to content

Commit a1089ce

Browse files
authored
Merge pull request #14 from BerkeleyLab/netcdf-read-write-example
Netcdf read/write example
2 parents 1136bed + 887677c commit a1089ce

File tree

4 files changed

+285
-20
lines changed

4 files changed

+285
-20
lines changed

.github/workflows/CI.yml

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,20 @@ name: CI
22

33
on: [push, pull_request]
44

5+
56
jobs:
67
Build:
7-
runs-on: ubuntu-latest
8+
runs-on: ${{ matrix.os }}
89
strategy:
10+
matrix:
11+
os: [macOS-latest]
912
fail-fast: true
1013

11-
env:
12-
FC: gfortran
13-
GCC_V: 10
14-
1514
steps:
1615
- name: Checkout code
1716
uses: actions/checkout@v2
1817

19-
- name: Install Dependencies
20-
run: |
21-
sudo apt install -y gfortran-${GCC_V} cmake mpich
22-
sudo update-alternatives --install /usr/bin/gfortran gfortran /usr/bin/gfortran-${GCC_V} 100
23-
git clone https://github.com/sourceryinstitute/opencoarrays
24-
mkdir -p opencoarrays/build
25-
cd opencoarrays/build
26-
cmake ..
27-
sudo make -j $(nproc) install
28-
cd -
29-
git clone https://github.com/fortran-lang/fpm
30-
cd fpm
31-
./install.sh
32-
3318
- name: Build and Test
3419
run: |
3520
export PATH="${HOME}/.local/bin:$PATH"
36-
fpm test
21+
./setup.sh

example/netCDF_IO.f90

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
program netCDF_IO
2+
use netcdf, only : &
3+
nf90_create, nf90_def_dim, nf90_def_var, nf90_enddef, nf90_put_var, nf90_inquire_dimension, & ! functions
4+
nf90_close, nf90_open, nf90_inq_varid, nf90_get_var, nf90_inquire_variable, &
5+
nf90_clobber, nf90_noerr, nf90_strerror, nf90_int, nf90_nowrite ! constants
6+
use assert_m, only : assert
7+
implicit none
8+
9+
integer i, j
10+
integer, parameter :: ny = 12, nx = 6
11+
integer, parameter :: data_written(*,*) = reshape([((i*j, i=1,nx), j=1,ny)], [ny,nx])
12+
integer, allocatable :: data_read(:,:)
13+
character(len=*), parameter :: file_name = "netCDF_example.nc"
14+
15+
call netCDF_write(file_name, data_written)
16+
call netCDF_read(file_name, data_read)
17+
18+
call assert(all(data_written == data_read) , "netCDF_IO: all(data_written == data_read)")
19+
20+
print *, "-----> netCDF file '" // file_name // "' written and read without error <-----"
21+
22+
contains
23+
24+
subroutine netCDF_write(file_name_, data_out)
25+
character(len=*), intent(in) :: file_name_
26+
integer, intent(in) :: data_out(:,:)
27+
28+
integer ncid, varid, x_dimid, y_dimid
29+
30+
associate(nf_status => nf90_create(file_name_, nf90_clobber, ncid)) ! create or ovewrite file
31+
call assert(nf_status == nf90_noerr, "nf90_create(file_name, nf90_clobber, ncid) succeeds",trim(nf90_strerror(nf_status)))
32+
end associate
33+
associate(nf_status => nf90_def_dim(ncid, "x", size(data_out,2), x_dimid)) ! define x dimension & get its ID
34+
call assert(nf_status == nf90_noerr,'nf90_def_dim(ncid,"x",size(data_out,2),x_dimid) succeeds',trim(nf90_strerror(nf_status)))
35+
end associate
36+
associate(nf_status => nf90_def_dim(ncid, "y", size(data_out,1), y_dimid)) ! define y dimension & get its ID
37+
call assert(nf_status==nf90_noerr, 'nf90_def_dim(ncid,"y",size(data_out,2),y_dimid) succeeds', trim(nf90_strerror(nf_status)))
38+
end associate
39+
associate(nf_status => nf90_def_var(ncid, "data", nf90_int, [y_dimid, x_dimid], varid))!define integer 'data' variable & get ID
40+
call assert(nf_status == nf90_noerr, 'nf90_def_var(ncid,"data",nf90_int,[y_dimid,x_dimid],varid) succeds', &
41+
trim(nf90_strerror(nf_status)))
42+
end associate
43+
associate(nf_status => nf90_enddef(ncid)) ! exit define mode: tell netCDF we are done defining metadata
44+
call assert(nf_status == nf90_noerr, 'nff90_noerr == nf90_enddef(ncid)', trim(nf90_strerror(nf_status)))
45+
end associate
46+
associate(nf_status => nf90_put_var(ncid, varid, data_out)) ! write all data to file
47+
call assert(nf_status == nf90_noerr, 'nff90_noerr == nf90_put_var(ncid, varid, data_out)', trim(nf90_strerror(nf_status)))
48+
end associate
49+
associate(nf_status => nf90_close(ncid)) ! close file to free associated netCDF resources and flush buffers
50+
call assert(nf_status == nf90_noerr, 'nff90_noerr == nf90_close(ncid)', trim(nf90_strerror(nf_status)))
51+
end associate
52+
53+
end subroutine
54+
55+
subroutine netCDF_read(file_name_, data_in)
56+
character(len=*), intent(in) :: file_name_
57+
integer, intent(inout), allocatable :: data_in(:,:)
58+
integer ncid, varid, data_in_rank
59+
60+
associate( nf_status => nf90_open(file_name_, nf90_nowrite, ncid) ) ! open file with read-only acces
61+
call assert(nf_status == nf90_noerr, "nf90_open(file_name_, NF90_NOWRITE, ncid) succeeds", trim(nf90_strerror(nf_status)))
62+
end associate
63+
64+
associate( nf_status => nf90_inq_varid(ncid, "data", varid)) ! Get data variable's ID
65+
call assert(nf_status == nf90_noerr, 'nf90_inq_varid(ncid, "data", varid) succeeds', trim(nf90_strerror(nf_status)))
66+
end associate
67+
68+
associate(data_in_shape => get_shape(ncid, "data"))
69+
allocate(data_in(data_in_shape(1), data_in_shape(2)))
70+
end associate
71+
72+
associate( nf_status => nf90_get_var(ncid, varid, data_in)) ! Read data
73+
call assert(nf_status == nf90_noerr, "nf90_get_var(ncid, varid, data_in) succeeds", trim(nf90_strerror(nf_status)))
74+
end associate
75+
76+
end subroutine
77+
78+
function get_shape(ncid, varname) result(array_shape)
79+
implicit none
80+
character(len=*), intent(in) :: varname
81+
integer, intent(in) :: ncid
82+
integer, allocatable :: array_shape(:)
83+
character(len=32) varid_string
84+
85+
integer varid, dimlen, i, var_rank
86+
integer, parameter :: max_rank=15
87+
integer,dimension(max_rank+1) :: dims, dimIds
88+
89+
associate(nf_status => nf90_inq_varid(ncid, varname, varid))
90+
write(varid_string, *) varid
91+
call assert(nf_status == nf90_noerr, "nf90_noerr == nf90_inq_varid(ncid, varname, varid) (" // &
92+
trim(nf90_strerror(nf_status)) // "(" // trim(varid_string)// ")")
93+
end associate
94+
associate(nf_status => nf90_inquire_variable(ncid, varid, ndims = var_rank))
95+
call assert(nf_status == nf90_noerr, "nf90_noerr == nf90_inquire_variable(ncid, varid, ndims = var_rank) (" // &
96+
trim(nf90_strerror(nf_status)) // "(" // varname // ")")
97+
end associate
98+
associate(nf_status => nf90_inquire_variable(ncid, varid, dimids = dimIds(:var_rank)))
99+
call assert(nf_status == nf90_noerr, "nf90_noerr == nf90_inquire_variable(ncid, varid, dimids = dimIds(:var_rank))", &
100+
trim(nf90_strerror(nf_status)) // "(" // varname // ")")
101+
end associate
102+
103+
do i=1,var_rank
104+
associate(nf_status => nf90_inquire_dimension(ncid, dimIds(i), len = dimlen))
105+
call assert(nf_status == nf90_noerr, "nf90_noerr == nf90_inquire_dimension(ncid, dimIds(i), len = dimlen)", &
106+
trim(nf90_strerror(nf_status)) // "(" // varname // ")")
107+
end associate
108+
dims(i+1)=dimlen
109+
end do
110+
111+
array_shape = dims(2:var_rank+1)
112+
end function
113+
114+
end program netCDF_IO

fpm.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ maintainer = "[email protected]"
77
[dependencies]
88
assert = {git = "https://github.com/sourceryinstitute/assert", tag = "1.4.0"}
99
sourcery = {git = "https://github.com/sourceryinstitute/sourcery", tag = "3.5.0"}
10+
netcdf-interfaces = {git = "https://github.com/LKedward/netcdf-interfaces.git"}

setup.sh

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#!/bin/sh
2+
3+
set -e # exit on error
4+
5+
usage()
6+
{
7+
echo "Inference Engine Setup Script"
8+
echo ""
9+
echo "USAGE:"
10+
echo "./setup.sh [--help|-h] | [-p|--prefix=PREFIX]"
11+
echo ""
12+
echo " --help Display this help text"
13+
echo " --prefix=PREFIX Install binary in 'PREFIX/bin'"
14+
echo " Default prefix='\$HOME/.local/bin'"
15+
echo ""
16+
}
17+
18+
PREFIX="$HOME/.local"
19+
20+
while [ "$1" != "" ]; do
21+
PARAM=$(echo "$1" | awk -F= '{print $1}')
22+
VALUE=$(echo "$1" | awk -F= '{print $2}')
23+
case $PARAM in
24+
-h | --help)
25+
usage
26+
exit
27+
;;
28+
-p | --prefix)
29+
PREFIX=$VALUE
30+
;;
31+
*)
32+
echo "ERROR: unknown parameter \"$PARAM\""
33+
usage
34+
exit 1
35+
;;
36+
esac
37+
shift
38+
done
39+
40+
set -u # error on use of undefined variable
41+
42+
if ! command -v brew > /dev/null ; then
43+
if ! command -v curl > /dev/null ; then
44+
echo "Please install curl and then rerun ./install.sh"
45+
exit 1
46+
fi
47+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
48+
if [ $(uname) = "Linux" ]; then
49+
if [ -z "$PATH" ]; then
50+
PATH=/home/linuxbrew/.linuxbrew/bin/
51+
else
52+
PATH=/home/linuxbrew/.linuxbrew/bin/:"$PATH"
53+
fi
54+
fi
55+
fi
56+
57+
58+
brew tap fortran-lang/fortran # required for building fpm
59+
brew install fpm cmake netcdf pkg-config coreutils # coreutils supports `realpath` below
60+
61+
PREFIX=`realpath $PREFIX`
62+
63+
mkdir -p build/dependencies
64+
if [ ! -d ./build/dependencies/netcdf-fortran ]; then
65+
git clone https://github.com/Unidata/netcdf-fortran.git build/dependencies/netcdf-fortran
66+
fi
67+
mkdir -p build/dependencies/netcdf-fortran/build
68+
69+
CI=${CI:-"false"} # GitHub Actions workflows set CI=true
70+
71+
cd build/dependencies/netcdf-fortran/build
72+
GCC_VER="12" # This should be replaced with code extracting the version number from Homebrew
73+
export FC=gfortran-${GCC_VER} CC=gcc-${GCC_VER} CXX=g++-${GCC_VER}
74+
if [ $CI = true ]; then
75+
NETCDFF_PREFIX="/usr/local"
76+
else
77+
NETCDFF_PREFIX="$PREFIX"
78+
fi
79+
NETCDF_PREFIX="`brew --prefix netcdf`"
80+
cmake .. \
81+
-DNETCDF_C_LIBRARY="$NETCDF_PREFIX/lib" \
82+
-DNETCDF_C_INCLUDE_DIR="$NETCDF_PREFIX/include" \
83+
-DCMAKE_INSTALL_PREFIX="$NETCDFF_PREFIX"
84+
make -j4
85+
sudo make install
86+
cd -
87+
88+
GIT_VERSION=`git describe --long --dirty --all --always | sed -e's/heads\///'`
89+
NETCDF_LIB_PATH="`brew --prefix netcdf`/lib"
90+
HDF5_LIB_PATH="`brew --prefix hdf5`/lib"
91+
NETCDFF_LIB_PATH="$NETCDFF_PREFIX/lib"
92+
93+
FPM_FLAG="-cpp -DUSE_ASSERTIONS=.true."
94+
FPM_FLAG=" $FPM_FLAG -fallow-argument-mismatch -ffree-line-length-none"
95+
FPM_FLAG=" $FPM_FLAG -DVERSION=\\\'$GIT_VERSION\\\'"
96+
FPM_FLAG=" $FPM_FLAG -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH -L$NETCDFF_LIB_PATH"
97+
FPM_FC="$FC"
98+
FPM_CC="$CC"
99+
100+
if [ $CI = true ]; then
101+
PKG_CONFIG_PATH=`realpath ./build/pkgconfig`
102+
echo "---------------"
103+
echo "PKG_CONFIG_PATH=$PKG_CONFIG_PATH"
104+
echo "---------------"
105+
else
106+
PKG_CONFIG_PATH=`realpath "$PREFIX"/lib/pkgconfig`
107+
fi
108+
109+
if [ ! -d $PKG_CONFIG_PATH ]; then
110+
mkdir -p $PKG_CONFIG_PATH
111+
fi
112+
113+
INFERENCE_ENGINE_PC="$PKG_CONFIG_PATH/inference-engine.pc"
114+
echo "INFERENCE_ENGINE_FPM_CXX=\"$CXX\"" > $INFERENCE_ENGINE_PC
115+
echo "INFERENCE_ENGINE_FPM_CC=\"$FPM_CC\"" >> $INFERENCE_ENGINE_PC
116+
echo "INFERENCE_ENGINE_FPM_FC=\"$FPM_FC\"" >> $INFERENCE_ENGINE_PC
117+
echo "INFERENCE_ENGINE_FPM_FLAG=\"$FPM_FLAG\"" >> $INFERENCE_ENGINE_PC
118+
echo "Name: inference-engine" >> $INFERENCE_ENGINE_PC
119+
echo "Description: Inference Engine" >> $INFERENCE_ENGINE_PC
120+
echo "URL: https://github.com/berkeleylab/inference-engine" >> $INFERENCE_ENGINE_PC
121+
echo "Version: 0.1.0" >> $INFERENCE_ENGINE_PC
122+
if [ $CI = true ]; then
123+
echo "---------------"
124+
echo "cat $INFERENCE_ENGINE_PC"
125+
cat $INFERENCE_ENGINE_PC
126+
echo "---------------"
127+
fi
128+
129+
export PKG_CONFIG_PATH
130+
RUN_FPM_SH="`realpath ./build/run-fpm.sh`"
131+
echo "#!/bin/sh" > $RUN_FPM_SH
132+
echo "#-- DO NOT EDIT -- created by inference-engine/install.sh" >> $RUN_FPM_SH
133+
echo "export PKG_CONFIG_PATH" >> $RUN_FPM_SH
134+
echo "`brew --prefix fpm`/bin/fpm \$@ --verbose \\" >> $RUN_FPM_SH
135+
echo "--profile debug \\" >> $RUN_FPM_SH
136+
echo "--c-compiler \"`pkg-config inference-engine --variable=INFERENCE_ENGINE_FPM_CC`\" \\" >> $RUN_FPM_SH
137+
echo "--compiler \"`pkg-config inference-engine --variable=INFERENCE_ENGINE_FPM_FC`\" \\" >> $RUN_FPM_SH
138+
echo "--flag \"`pkg-config inference-engine --variable=INFERENCE_ENGINE_FPM_FLAG`\"" >> $RUN_FPM_SH
139+
chmod u+x $RUN_FPM_SH
140+
if [ $CI = true ]; then
141+
echo "---------------"
142+
echo "cat $RUN_FPM_SH"
143+
cat $RUN_FPM_SH
144+
echo "---------------"
145+
fi
146+
147+
if command -v fpm > /dev/null 2>&1; then
148+
brew tap fortran-lang/fortran
149+
brew install fpm
150+
fi
151+
152+
echo "$RUN_FPM_SH test"
153+
$RUN_FPM_SH test
154+
155+
echo ""
156+
echo "______________ Inference-Engine has been installed! ______________"
157+
echo ""
158+
echo "To use use or test with Inference-Engine with the Fortran Package"
159+
echo "Manager (fpm) and using the required compiler/linker flags, pass"
160+
echo "a fpm command to the build/run-fpm.sh script. For example, to"
161+
echo "run one of the provided example programs with a path of the form"
162+
echo "example/<name>.f90, enter a command of the following form at "
163+
echo "a shell command prompt:"
164+
echo ""
165+
echo "./build/run-fpm.sh run --example <name>"

0 commit comments

Comments
 (0)