Skip to content

Commit 52e2dfb

Browse files
Adds ADIOS2, high performance parallel I/O backend to WRF [after fixes] (wrf-model#1787)
Optional high-performance parallel-I/O option added to WRF, enabling in-situ analysis using the ADIOS2 library. TYPE: new feature KEYWORDS: ADIOS2, In-situ, I/O, Parallel, High-performance SOURCE: Michael Laufer (Toga Networks, a Huawei Company), Erick Fredj (The Jerusalem College of Technology, Rutgers University, Toga Networks, a Huawei Company), Joseph Brodie (Rutgers University) and Lori Garzio (Rutgers University) DESCRIPTION OF CHANGES: This PR adds [ADIOS2 ](https://github.com/ornladios/ADIOS2) as a high-performance parallel I/O backend option to WRF. ADIOS2 is a data management library that can transport groups of self-describing variables and attributes across different media types (such as files, wide-area-networks, and remote direct memory access). See [here ](https://doi.org/10.1016/j.softx.2020.100561) for additional information on ADIOS2. Our testing demonstrates an order of magnitude performance increase over PnetCDF and can even outperform split NetCDF (io_form_* 102) at scale without the need to merge the files back together, while attaining compression ratios similar to NetCDF4 compression. ADIOS2 additionally supports in-situ processing and code-coupling without requiring data to traverse via the file system. A complete description of this new I/O backend, as well as some of the new features now accessible in WRF, can be found in our research article ([https://arxiv.org/abs/2201.08228](https://arxiv.org/abs/2201.08228)). A [conversion script](https://github.com/MichaelLaufer/WRF-ADIOS2-to-NetCDF4) converting the ADIOS2 file format to the NetCDF4 file format is also provided for backward compatibility. Details on configuring and running should be found in the file doc/README.adios2. LIST OF MODIFIED FILES: M Registry/Registry.CONVERT M Registry/Registry.EM_COMMON M Registry/Registry.EM_COMMON.var M Registry/registry.io_boilerplate M arch/Config.pl M arch/postamble M arch/preamble M configure M external/Makefile A external/io_adios2/Makefile A external/io_adios2/ext_adios2_get_dom_ti.code A external/io_adios2/ext_adios2_get_var_td.code A external/io_adios2/ext_adios2_get_var_ti.code A external/io_adios2/ext_adios2_put_dom_ti.code A external/io_adios2/ext_adios2_put_var_td.code A external/io_adios2/ext_adios2_put_var_ti.code A external/io_adios2/field_routines.F90 A external/io_adios2/transpose.code A external/io_adios2/wrf_io.F90 M external/ioapi_share/wrf_status_codes.h M frame/md_calls.m4 M frame/module_io.F M share/module_io_domain.F M share/output_wrf.F M share/wrf_ext_write_field.F A doc/README.adios2 TESTS CONDUCTED: - Gnu and Intel compilers were used for testing with dmpar and dm+sm. The serial configuration is not applicable, as MPI is required for this implementation. - Restarting WRF via the ADIOS2 restart file works properly. The data in subsequent output files matches the data in NetCDF output files. - Passed regression tests and no impact on other part of the code. RELEASE NOTE: ADIOS2 has been added to WRF as an optional high-performance I/O backend (io_form_* = 14). File write times are substantially lower than PnetCDF, with compression ratios close to NetCDF4. Allows for new in-situ processing and code-coupling capabilities through the ADIOS2 library.
1 parent 9e97f22 commit 52e2dfb

26 files changed

+4756
-5
lines changed

Registry/Registry.CONVERT

+1-1
Original file line numberDiff line numberDiff line change
@@ -516,4 +516,4 @@ package io_zzz io_form_restart==9 - -
516516
package io_grib2 io_form_restart==10 - -
517517
package io_pnetcdf io_form_restart==11 - -
518518
package io_pio io_form_restart==12 - -
519-
519+
package io_adios2 io_form_restart==14 - -

Registry/Registry.EM_COMMON

+1
Original file line numberDiff line numberDiff line change
@@ -3316,6 +3316,7 @@ package io_grib2 io_form_restart==10 - -
33163316
package io_pnetcdf io_form_restart==11 - -
33173317
package io_pio io_form_restart==12 - -
33183318
package io_netcdfpar io_form_restart==13 - -
3319+
package io_adios2 io_form_restart==14 - -
33193320

33203321
#WRF Hydro
33213322
package no_wrfhydro wrf_hydro==0 - -

Registry/Registry.EM_COMMON.var

+1
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ package io_grib2 io_form_restart==10 - -
405405
package io_pnetcdf io_form_restart==11 - -
406406
package io_pio io_form_restart==12 - -
407407
package io_netcdfpar io_form_restart==13 - -
408+
package io_adios2 io_form_restart==14 - -
408409

409410
#WRF Hydro
410411
package no_wrfhydro wrf_hydro==0 - -

Registry/registry.io_boilerplate

+4
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,7 @@ rconfig logical ignore_iofields_warning namelist,time_control 1 .
145145
# nofill = true means only a single write, not the write/read/write sequence
146146
rconfig logical ncd_nofill namelist,time_control 1 .true.
147147

148+
# for ADIOS2 specific features
149+
rconfig logical adios2_compression_enable namelist,namelist_adios2 1 .true.
150+
rconfig character adios2_blosc_compressor namelist,namelist_adios2 1 "lz4"
151+
rconfig integer adios2_numaggregators namelist,namelist_adios2 1 0

arch/Config.pl

+52
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
$sw_netcdf_path = "" ;
1414
$sw_pnetcdf_path = "" ;
1515
$sw_netcdfpar_path = "" ;
16+
$sw_adios2_path = "" ;
1617
$sw_hdf5_path="";
1718
$sw_phdf5_path="";
1819
$sw_jasperlib_path="";
@@ -103,6 +104,10 @@
103104
{
104105
$sw_netcdfpar_path = substr( $ARGV[0], 11 ) ;
105106
}
107+
if ( substr( $ARGV[0], 1, 7 ) eq "adios2=" )
108+
{
109+
$sw_adios2_path = substr( $ARGV[0], 8 ) ;
110+
}
106111
if ( substr( $ARGV[0], 1, 5 ) eq "hdf5=" )
107112
{
108113
$sw_hdf5_path = substr( $ARGV[0], 6 ) ;
@@ -612,6 +617,7 @@
612617
$_ =~ s/CONFIGURE_NETCDF_PATH/$sw_netcdf_path/g ;
613618
$_ =~ s/CONFIGURE_PNETCDF_PATH/$sw_pnetcdf_path/g ;
614619
$_ =~ s/CONFIGURE_NETCDFPAR_PATH/$sw_netcdfpar_path/g ;
620+
$_ =~ s/CONFIGURE_ADIOS2_PATH/$sw_adios2_path/g ;
615621
$_ =~ s/CONFIGURE_HDF5_PATH/$sw_hdf5_path/g ;
616622
$_ =~ s/CONFIGURE_PHDF5_PATH/$sw_phdf5_path/g ;
617623
$_ =~ s/CONFIGURE_LDFLAGS/$sw_ldflags/g ;
@@ -702,6 +708,29 @@
702708
$_ =~ s:CONFIGURE_PNETCDF_LIB_PATH::g ;
703709
}
704710

711+
if ( $sw_adios2_path )
712+
{ $_ =~ s/CONFIGURE_WRFIO_ADIOS2/wrfio_adios2/g ;
713+
$_ =~ s:CONFIGURE_ADIOS2_FLAG:-DADIOS2: ;
714+
if ( -d "$sw_adios2_path/lib" )
715+
{
716+
$adios2_libdir = "$sw_adios2_path/lib" ;
717+
}
718+
elsif ( -d "$sw_adios2_path/lib64" )
719+
{
720+
$adios2_libdir = "$sw_adios2_path/lib64" ;
721+
}
722+
else
723+
{
724+
die "ADIOS2 environment variable was set, but neither $sw_adios2_path/lib nor $sw_adios2_path/lib64 were found." ;
725+
}
726+
$_ =~ s:CONFIGURE_ADIOS2_LIB_PATH:-L\$\(WRF_SRC_ROOT_DIR\)/external/io_adios2 -lwrfio_adios2 -L$adios2_libdir -ladios2_fortran_mpi -ladios2_fortran:;
727+
}
728+
else
729+
{ $_ =~ s/CONFIGURE_WRFIO_ADIOS2//g ;
730+
$_ =~ s:CONFIGURE_ADIOS2_FLAG::g ;
731+
$_ =~ s:CONFIGURE_ADIOS2_LIB_PATH::g ;
732+
}
733+
705734
if ( $sw_hdf5_path )
706735
{ $_ =~ s:CONFIGURE_HDF5_LIB_PATH:-L$sw_hdf5_path/lib -lhdf5hl_fortran -lhdf5_hl -lhdf5_fortran -lhdf5 -lm -lz: ;
707736
$_ =~ s:CONFIGURE_HDF5_FLAG:-DHDF5: ;
@@ -1067,6 +1096,29 @@
10671096
$_ =~ s:CONFIGURE_PNETCDF_LIB_PATH::g ;
10681097
}
10691098

1099+
if ( $sw_adios2_path )
1100+
{ $_ =~ s/CONFIGURE_WRFIO_ADIOS2/wrfio_adios2/g ;
1101+
$_ =~ s:CONFIGURE_ADIOS2_FLAG:-DADIOS2: ;
1102+
if ( -d "$sw_adios2_path/lib" )
1103+
{
1104+
$adios2_libdir = "$sw_adios2_path/lib" ;
1105+
}
1106+
elsif ( -d "$sw_adios2_path/lib64" )
1107+
{
1108+
$adios2_libdir = "$sw_adios2_path/lib64" ;
1109+
}
1110+
else
1111+
{
1112+
die "ADIOS2 environment variable was set, but neither $sw_adios2_path/lib nor $sw_adios2_path/lib64 were found." ;
1113+
}
1114+
$_ =~ s:CONFIGURE_ADIOS2_LIB_PATH:-L\$\(WRF_SRC_ROOT_DIR\)/external/io_adios2 -lwrfio_adios2 -L$adios2_libdir -ladios2_fortran_mpi -ladios2_fortran:;
1115+
}
1116+
else
1117+
{ $_ =~ s/CONFIGURE_WRFIO_ADIOS2//g ;
1118+
$_ =~ s:CONFIGURE_ADIOS2_FLAG::g ;
1119+
$_ =~ s:CONFIGURE_ADIOS2_LIB_PATH::g ;
1120+
}
1121+
10701122
if ( $sw_hdf5_path )
10711123
{ $_ =~ s:CONFIGURE_HDF5_LIB_PATH:-L$sw_hdf5_path/lib -lhdf5hl_fortran -lhdf5_hl -lhdf5_fortran -lhdf5 -lm -lz: ;
10721124
$_ =~ s:CONFIGURE_HDF5_FLAG:-DHDF5: ;

arch/postamble

+8-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ ARCHFLAGS = $(COREDEFS) -DIWORDSIZE=$(IWORDSIZE) -DDWORDSIZE=$(DWORDSIZ
1111
CONFIGURE_NETCDF_FLAG \
1212
CONFIGURE_NETCDFPAR_FLAG \
1313
CONFIGURE_PNETCDF_FLAG \
14+
CONFIGURE_ADIOS2_FLAG \
1415
CONFIGURE_ESMF_FLAG \
1516
CONFIGURE_GRIB2_FLAG \
1617
CONFIGURE_RTTOV_FLAG \
@@ -67,10 +68,11 @@ HDF5PATH = CONFIGURE_HDF5_PATH
6768
WRFPLUSPATH = CONFIGURE_WRFPLUS_PATH
6869
RTTOVPATH = CONFIGURE_RTTOV_PATH
6970
PNETCDFPATH = CONFIGURE_PNETCDF_PATH
71+
ADIOS2PATH = CONFIGURE_ADIOS2_PATH
7072

7173
bundled: io_only CONFIGURE_ATMOCN
7274
external: io_only CONFIGURE_COMMS_EXTERNAL $(ESMF_TARGET)
73-
io_only: esmf_time CONFIGURE_WRFIO_NF CONFIGURE_WRFIO_NFPAR CONFIGURE_WRFIO_PNF CONFIGURE_WRFIO_GRIB2 \
75+
io_only: esmf_time CONFIGURE_WRFIO_NF CONFIGURE_WRFIO_NFPAR CONFIGURE_WRFIO_PNF CONFIGURE_WRFIO_ADIOS2 CONFIGURE_WRFIO_GRIB2 \
7476
wrf_ioapi_includes wrfio_grib_share wrfio_grib1 wrfio_int fftpack
7577

7678

@@ -108,6 +110,11 @@ wrfio_pnf :
108110
make $(J) NETCDFPATH="$(PNETCDFPATH)" RANLIB="$(RANLIB)" CPP="$(CPP) $(ARCHFLAGS)" \
109111
FC="$(FC) $(PROMOTION) $(OMP) $(FCFLAGS)" TRADFLAG="$(TRADFLAG)" AR="$(AR)" ARFLAGS="$(ARFLAGS)" )
110112

113+
wrfio_adios2 :
114+
( cd $(WRF_SRC_ROOT_DIR)/external/io_adios2 ; \
115+
make $(J) ADIOS2="$(ADIOS2PATH)" RANLIB="$(RANLIB)" CPP="$(CPP) $(ARCHFLAGS)" \
116+
FC="$(FC) $(PROMOTION) $(OMP) $(FCFLAGS)" TRADFLAG="$(TRADFLAG)" AR="$(AR)" ARFLAGS="$(ARFLAGS)" )
117+
111118
wrfio_grib_share :
112119
( cd $(WRF_SRC_ROOT_DIR)/external/io_grib_share ; \
113120
make $(J) CC="$(SCC)" CFLAGS="$(CFLAGS)" RM="$(RM)" RANLIB="$(RANLIB)" CPP="$(CPP)" \

arch/preamble

+1-2
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ ESMF_TARGET = ESMFTARGET
8787

8888
# ESMFINCLUDEGOESHERE
8989

90-
9190
#### NETCDF4 pieces
9291

9392
NETCDF4_IO_OPTS = -DUSE_NETCDF4_FEATURES -DWRFIO_NCD_LARGE_FILE_SUPPORT
@@ -119,7 +118,7 @@ LIBWRFLIB = libwrflib.a
119118
#NOWIN $(WRF_SRC_ROOT_DIR)/frame/pack_utils.o
120119

121120
#NOWIN LIB_EXTERNAL = \
122-
#NOWIN CONFIGURE_NETCDF_LIB_PATH CONFIGURE_NETCDFPAR_LIB_PATH CONFIGURE_PNETCDF_LIB_PATH CONFIGURE_GRIB2_LIB CONFIGURE_ATMOCN_LIB CONFIGURE_HDF5_LIB_PATH
121+
#NOWIN CONFIGURE_NETCDF_LIB_PATH CONFIGURE_NETCDFPAR_LIB_PATH CONFIGURE_PNETCDF_LIB_PATH CONFIGURE_GRIB2_LIB CONFIGURE_ATMOCN_LIB CONFIGURE_HDF5_LIB_PATH CONFIGURE_ADIOS2_LIB_PATH
123122

124123

125124
#### Architecture specific settings ####

configure

+7-1
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,12 @@ if [ -n "$PNETCDF" ] ; then
354354
# echo "Will configure for use without NetCDF"
355355
fi
356356

357+
if [ -n "$ADIOS2" ] ; then
358+
echo "Will use ADIOS2 in dir: $ADIOS2"
359+
else
360+
echo "ADIOS2 not set in environment. Will configure WRF for use without."
361+
fi
362+
357363
if [ -n "$HDF5" ] ; then
358364
echo "Will use HDF5 in dir: $HDF5"
359365
else
@@ -566,7 +572,7 @@ if test -n "$PERL" ; then
566572
srch=`grep -i "^#ARCH.*$os" arch/configure.defaults | grep -i "$mach"`
567573
if [ -n "$srch" ] ; then
568574
$PERL arch/Config.pl -dmparallel=$COMMLIB -ompparallel=$OMP -perl=$PERL \
569-
-netcdf=$NETCDF -pnetcdf=$PNETCDF -netcdfpar=$NETCDFPAR -hdf5=$HDF5 -phdf5=$PHDF5 -os=$os -mach=$mach -ldflags=$ldflags \
575+
-netcdf=$NETCDF -pnetcdf=$PNETCDF -netcdfpar=$NETCDFPAR -adios2=$ADIOS2 -hdf5=$HDF5 -phdf5=$PHDF5 -os=$os -mach=$mach -ldflags=$ldflags \
570576
-compileflags=$compileflags -opt_level=$opt_level -USENETCDFF=$USENETCDFF -USENETCDF=$USENETCDF \
571577
-time=$FORTRAN_COMPILER_TIMER -tfl="$TFL" -cfl="$CFL" -config_line="$config_line" \
572578
-wrf_core=$wrf_core -gpfs=$GPFS_PATH -curl=$CURL_PATH -dep_lib_path="$DEP_LIB_PATH"

doc/README.adios2

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
The ADIOS2 I/O option in WRF improves I/O at scale, as well as enabling in-situ processing and code-coupling capabilities.
2+
3+
USAGE:
4+
The ADIOS2 I/O option for history and/or restart file is enabled by setting one of the following:
5+
io_form_history = 14
6+
io_form_restart = 14
7+
8+
Additionally, the ADIOS2 I/O implementation allows for optional configuration that can be appended to the namelist.input:
9+
&namelist_adios2
10+
adios2_compression_enable = .true.,
11+
adios2_blosc_compressor = "lz4",
12+
adios2_numaggregators = 0,
13+
/
14+
15+
- ADIOS2 compression is enabled by default but can be disabled with the adios2_compression_enable parameter.
16+
- Different Blosc compression codecs are available: lz4 (default), lz4hc, blosclz, zstd, zlib.
17+
The ideal compression codec depends on the platform architecture as well as required compression ratios.
18+
- adios2_numaggregators is the number of aggregator ranks (and resulting sub-files created). This number can greatly affect write times, and is the primary tuning knob in ADIOS2.
19+
By default this is set to 0, which is an alias for a single aggregator per compute node, which is found to be a good option at large process counts,
20+
as file system contention is minimized while removing inter-node communication that is seen in MPI-I/O based approaches.
21+
Alternatively, this can be set to any number between 1 and the number of processes participating in the computation.
22+
Using adios2_numaggregators set to the number of participating processes (or larger) essentially achieves file-per-process I/O which is highly performant at lower process/node counts.
23+
24+
As ADIOS2 is "Step-based" approach (to resemble actual production of data in "steps"), optimal performance is achieved when history outputs are appended to the same file.
25+
This is achieved in WRF by setting the frames_per_outfile namelist.input parameter to a large number.
26+
27+
As ADIOS2 generates data in its own file format (BP3/4/5), post-processing scripts will need to altered to use the ADIOS2 read API.
28+
For backwards compatibility purposes, a conversion script converting the ADIOS2 file format to NetCDF4 file format conversion script is also available.
29+
See https://github.com/MichaelLaufer/WRF-ADIOS2-to-NetCDF4 for details.
30+
File conversions time per output file for a case such as the benchmark CONUS 2.5km resolution take just a few seconds on a local machine.
31+
32+
INSTALLATION:
33+
WRF is configured for use with ADIOS2 by setting the $ADIOS2 environment variable to the ADIOS2 root installation directory, just like the PnetCDF, and PHDF5 backends.
34+
ADIOS2 installation should be configured for MPI and Blosc, and requires a version of ADIOS2 >= v2.8.0.
35+
36+
ADVANCED:
37+
In general ADIOS2 parameters can be set using an XML file (see ADIOS2 documentation for more details).
38+
But as transformation parameters (like compression) must be specified in the XML on a per-variable basis, which in WRF can be hundreds of variables, this is not feasible.
39+
Therefore compression is hardcoded in the WRF-ADIOS2 implementation, but with the compression controlled by the aforementioned namelist.input parameters.
40+
That being said, additional features in ADIOS2 like SST for in-situ processing, and node-local burst buffer write capabilities are enabled by using an XML file (named adios2.xml,
41+
and should be placed in the run directory).
42+
The ADIOS2 "io name" in the XML should be set to the name of the file that would be written to the disk, and frames_per_outfile should be set to a large number,
43+
to enforce all of the data to use the same ADIOS2 "io name".
44+
When using the ADIOS2 SST file engine (e.g. for in-situ processing), the parameter SpeculativePreloadMode should be set to "OFF", to prevent ADIOS2 preemptively sending unneeded data to the data consumer.
45+
46+
Example adios2.xml for node-local burst buffer functionality:
47+
<?xml version="1.0"?>
48+
<adios-config>
49+
<io name="wrfout_d01_2018-06-17_00:00:00">
50+
<engine type="BP4">
51+
<parameter key="BurstBufferPath" value="/mnt/Burst/Buffer/PATH"/>
52+
</engine>
53+
</io>
54+
</adios-config>
55+
56+
Example adios2.xml for in-situ analysis functionality:
57+
<?xml version="1.0"?>
58+
<adios-config>
59+
<io name="wrfout_d01_2018-06-17_00:00:00">
60+
<engine type="SST">
61+
<parameter key="SpeculativePreloadMode" value="OFF"/>
62+
<parameter key="QueueLimit" value="1"/>
63+
</engine>
64+
</io>
65+
</adios-config>

external/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ superclean :
77
@( cd io_int ; make -s superclean )
88
@( cd io_netcdf ; make -s superclean )
99
@( cd io_netcdfpar ; make -s superclean )
10+
@( cd io_adios2 ; make -s superclean )
1011
@( cd io_phdf5 ; make -s superclean )
1112
@( cd io_grib1 ; make -s superclean )
1213
@( cd io_grib_share ; make -s superclean )

external/io_adios2/Makefile

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#makefile to build a wrf_io with ADIOS2
2+
3+
OBJSL = wrf_io.o field_routines.o
4+
OBJS = $(OBJSL)
5+
CODE = ext_adios2_get_dom_ti.code \
6+
ext_adios2_get_var_td.code \
7+
ext_adios2_get_var_ti.code \
8+
ext_adios2_put_dom_ti.code \
9+
ext_adios2_put_var_td.code \
10+
ext_adios2_put_var_ti.code \
11+
transpose.code
12+
13+
FFLAGS = $(FCFLAGS) -I$(ADIOS2)/include/adios2/fortran -I../ioapi_share
14+
CPP1 = $(CPP) -P $(TRADFLAG)
15+
M4 = m4 -Uinclude -Uindex -Ulen
16+
AR = ar
17+
18+
.SUFFIXES: .F90 .f .o .code
19+
20+
all : libwrfio_adios2.a
21+
22+
libwrfio_adios2.a: $(OBJS) $(CODE)
23+
/bin/rm -f libwrfio_adios2.a
24+
$(AR) cr libwrfio_adios2.a $(OBJSL)
25+
$(RANLIB) libwrfio_adios2.a
26+
27+
wrf_io.o: wrf_io.F90 $(CODE)
28+
$(CPP1) -I$(ADIOS2)/include/adios2/fortran -I../ioapi_share wrf_io.F90 | $(M4) - > wrf_io.f
29+
$(FC) $(FFLAGS) -c wrf_io.f
30+
31+
field_routines.o: field_routines.F90 wrf_io.o
32+
$(CPP1) -I$(ADIOS2)/include/adios2/fortran -I../ioapi_share field_routines.F90 > field_routines.f
33+
$(FC) $(FFLAGS) -c field_routines.f
34+
35+
superclean:
36+
@/bin/rm -f *.f *.o *.mod *.smod libwrfio_adios2.a

0 commit comments

Comments
 (0)