From 43148348f4dd5a88bdf61769333169e2ad8965c2 Mon Sep 17 00:00:00 2001 From: wkliao Date: Sun, 4 Jan 2026 15:05:09 -0600 Subject: [PATCH] Separate ncmpidiff core subroutines into a file and build it as a library This enables PnetCDF test programs to call the ncmpidiff core subroutine to make comparison of two files. --- src/utils/ncmpidiff/Makefile.am | 7 +- src/utils/ncmpidiff/ncmpidiff.c | 952 +------------------------- src/utils/ncmpidiff/ncmpidiff_core.c | 954 +++++++++++++++++++++++++++ src/utils/ncmpidiff/ncmpidiff_core.h | 23 + 4 files changed, 1010 insertions(+), 926 deletions(-) create mode 100644 src/utils/ncmpidiff/ncmpidiff_core.c create mode 100644 src/utils/ncmpidiff/ncmpidiff_core.h diff --git a/src/utils/ncmpidiff/Makefile.am b/src/utils/ncmpidiff/Makefile.am index 0cbc72adb..0abd162c2 100644 --- a/src/utils/ncmpidiff/Makefile.am +++ b/src/utils/ncmpidiff/Makefile.am @@ -11,8 +11,11 @@ AM_CPPFLAGS += -I$(top_builddir)/src/include bin_PROGRAMS = ncmpidiff cdfdiff +noinst_LTLIBRARIES = libncmpidiff_core.la +libncmpidiff_core_la_SOURCES = ncmpidiff_core.c + ncmpidiff_SOURCES = ncmpidiff.c -ncmpidiff_LDADD = $(top_builddir)/src/libs/libpnetcdf.la +ncmpidiff_LDADD = $(top_builddir)/src/libs/libpnetcdf.la $(noinst_LTLIBRARIES) ncmpidiff_LDADD += @NETCDF4_LDFLAGS@ @ADIOS_LDFLAGS@ @NETCDF4_LIBS@ @ADIOS_LIBS@ cdfdiff_SOURCES = cdfdiff.c @@ -25,6 +28,8 @@ $(top_builddir)/src/libs/libpnetcdf.la: dist_man_MANS = ncmpidiff.1 cdfdiff.1 +EXTRA_DIST = ncmpidiff_core.h + CLEANFILES = core core.* *.gcda *.gcno *.gcov gmon.out dist-hook: diff --git a/src/utils/ncmpidiff/ncmpidiff.c b/src/utils/ncmpidiff/ncmpidiff.c index d397ecf80..3524a6485 100644 --- a/src/utils/ncmpidiff/ncmpidiff.c +++ b/src/utils/ncmpidiff/ncmpidiff.c @@ -31,259 +31,14 @@ #include #include +#include -#ifndef ubyte -#define ubyte unsigned char -#endif -#ifndef ushort -#define ushort unsigned short -#endif -#ifndef uint -#define uint unsigned int -#endif -#ifndef int64 -#define int64 long long -#endif -#ifndef uint64 -#define uint64 unsigned long long -#endif - -static int first_diff; -static char cmd_opts[1024]; - -#define PRINT_CMD_OPTS \ - if (first_diff) { \ - printf("%s\n", cmd_opts); \ - first_diff = 0; \ - } #define OOM_ERROR { \ fprintf(stderr, "Error: calloc() out of memory at line %d\n",__LINE__); \ exit(1); \ } -#define HANDLE_ERROR { \ - if (err != NC_NOERR) { \ - fprintf(stderr, "Error at line %d of file %s (%s)\n", __LINE__, \ - __FILE__, ncmpi_strerror(err)); \ - MPI_Abort(MPI_COMM_WORLD, -1); \ - exit(-1); \ - } \ -} - -#define HANDLE_FILE_ERR(filename) { \ - if (err != NC_NOERR) { \ - fprintf(stderr, "Error at line %d: input file %s (%s)\n", __LINE__, \ - filename, ncmpi_strerror(err)); \ - MPI_Abort(MPI_COMM_WORLD, -1); \ - exit(-1); \ - } \ -} - -#define CHECK_GLOBAL_ATT_DIFF_CHAR { \ - int pos; \ - char *b1 = (char *)calloc((attlen[0] + 1) * 2, sizeof(char)); \ - char *b2 = b1 + attlen[0] + 1; \ - if (!b1) OOM_ERROR \ - err = ncmpi_get_att_text(ncid[0], NC_GLOBAL, name[0], b1); \ - HANDLE_ERROR \ - err = ncmpi_get_att_text(ncid[1], NC_GLOBAL, name[0], b2); \ - HANDLE_ERROR \ - for (pos=0; pos= 0) ? (x) : (-x) -#define UABS(x) (x) - -#define CHECK_VAR_DIFF(type, func, xabs) { \ - int pos, isDiff, worst = -1; \ - type *b1, *b2; \ - b1 = (type *)calloc(varsize * 2, sizeof(type)); \ - if (!b1) OOM_ERROR \ - b2 = b1 + varsize; \ - err = ncmpi_get_vara_##func(ncid[0], varid1, start, shape, b1); \ - HANDLE_ERROR \ - err = ncmpi_get_vara_##func(ncid[1], varid2, start, shape, b2); \ - HANDLE_ERROR \ - if (!check_tolerance) { \ - for (pos=0; pos abs_b2) ? abs_b1 : abs_b2; \ - diff = b1[pos] - b2[pos]; \ - diff = (diff >= 0) ? diff : -diff; \ - ratio = diff / abs_max; \ - if (diff <= tolerance_difference || ratio <= tolerance_ratio) \ - continue; \ - /* fail to meet both tolerance errors */ \ - worst = pos; \ - break; \ - } \ - } \ - if (pos != varsize || worst != -1) { /* diff is found */ \ - double v1, v2; \ - if (ndims[0] == 0) { /* scalar variable */ \ - PRINT_CMD_OPTS \ - if (worst == -1) \ - printf("DIFF: scalar variable \"%s\" of type \"%s\"\n", \ - name[0], get_type(xtype[0])); \ - else { \ - v1 = b1[worst]; \ - v2 = b2[worst]; \ - printf("DIFF (tolerance): scalar variable \"%s\" of type \"%s\" of value %g vs %g (difference = %e)\n", \ - name[0], get_type(xtype[0]), v1, v2, v1-v2); \ - } \ - } else { \ - int _i; \ - MPI_Offset *diffStart; \ - diffStart = (MPI_Offset*) malloc(sizeof(MPI_Offset) * ndims[0]); \ - if (worst != -1) pos = worst; \ - v1 = b1[pos]; \ - v2 = b2[pos]; \ - for (_i=ndims[0]-1; _i>=0; _i--) { \ - diffStart[_i] = pos % shape[_i] + start[_i]; \ - pos /= shape[_i]; \ - } \ - PRINT_CMD_OPTS \ - if (worst == -1) \ - printf("DIFF: variable \"%s\" of type \"%s\" at element ["OFFFMT, \ - name[0], get_type(xtype[0]), diffStart[0]); \ - else \ - printf("DIFF (tolerance): variable \"%s\" of type \"%s\" at element ["OFFFMT, \ - name[0], get_type(xtype[0]), diffStart[0]); \ - for (_i=1; _i-----------------------------------------------------*/ static void -get_var_names(char *opt_arg, struct vspec* vspecp) +get_var_names(char *opt_arg, + int *nvars, + char ***names) { char *cp=opt_arg, **cpp; - int nvars = 1; + *nvars = 1; /* compute number of variable names in comma-delimited list */ - vspecp->nvars = 1; while (*cp++) if (*cp == ',') - nvars++; + (*nvars)++; - vspecp->names = (char **) calloc((size_t)nvars, sizeof(char*)); - if (!vspecp->names) OOM_ERROR + *names = (char **) calloc((size_t)*nvars, sizeof(char*)); + if (!*names) OOM_ERROR - cpp = vspecp->names; + cpp = *names; /* copy variable names into list */ for (cp = strtok(opt_arg, ","); cp != NULL; @@ -350,27 +106,6 @@ get_var_names(char *opt_arg, struct vspec* vspecp) if (!*cpp) OOM_ERROR cpp++; } - vspecp->nvars = nvars; -} - -/*----< get_type() >----------------------------------------------------------*/ -static char* -get_type(int type) -{ - switch (type) { - case NC_BYTE: return "NC_BYTE"; - case NC_CHAR: return "NC_CHAR"; - case NC_SHORT: return "NC_SHORT"; - case NC_INT: return "NC_INT"; - case NC_FLOAT: return "NC_FLOAT"; - case NC_DOUBLE: return "NC_DOUBLE"; - case NC_UBYTE: return "NC_UBYTE"; - case NC_USHORT: return "NC_USHORT"; - case NC_UINT: return "NC_UINT"; - case NC_INT64: return "NC_INT64"; - case NC_UINT64: return "NC_UINT64"; - } - return "NC_NAT"; } /*----< main() >--------------------------------------------------------------*/ @@ -378,18 +113,14 @@ int main(int argc, char **argv) { extern char *optarg; extern int optind; - char *name[2]; - int i, j, c, err, rank, nprocs, verbose, quiet, check_tolerance; - int ncid[2], ndims[2], nvars[2], natts[2], recdim[2], *dimids[2], fmt[2]; - int cmp_nvars, check_header, check_variable_list, check_entire_file; - long long numVarDIFF=0, numHeadDIFF=0, varDIFF, numDIFF; + char cmd_opts[1024], **var_names; + int i, c, rank, nprocs, verbose, quiet, check_tolerance; + int first_diff, ncid[2], num_vars; + int check_header, check_variable_list, check_entire_file; + long long numDIFF; double tolerance_ratio, tolerance_difference; - MPI_Offset *shape=NULL, varsize, *start=NULL; - MPI_Offset attlen[2], dimlen[2]; MPI_Comm comm=MPI_COMM_WORLD; MPI_Info info = MPI_INFO_NULL; - nc_type xtype[2]; - struct vspec var_list; MPI_Init(&argc, &argv); MPI_Comm_size(comm, &nprocs); @@ -410,8 +141,8 @@ int main(int argc, char **argv) check_header = 0; check_variable_list = 0; check_entire_file = 0; - var_list.names = NULL; - var_list.nvars = 0; + var_names = NULL; + num_vars = 0; check_tolerance = 0; first_diff = 1; @@ -423,7 +154,7 @@ int main(int argc, char **argv) break; case 'v': /* variable names */ /* make list of names of variables specified */ - get_var_names(optarg, &var_list); + get_var_names(optarg, &num_vars, &var_names); check_variable_list = 1; break; case 'b': @@ -493,648 +224,19 @@ int main(int argc, char **argv) /* Nov. 18, 2014 -- disable subfiling as it does not correctly handle the * cases when nprocs < num_subfiles */ - MPI_Info_create (&info); - MPI_Info_set (info, "pnetcdf_subfiling", "disable"); - - ncid[0] = ncid[1] = -1; - - /* open files and retrieve headers into memory buffers */ - for (i=0; i<2; i++) { /* i=0 for 1st file, i=1 for 2nd file */ - if (verbose && rank == 0) { - if (i == 0) printf("First file: %s\n", argv[optind+i]); - if (i == 1) printf("Second file: %s\n", argv[optind+i]); - } - - /* file format version */ - err = ncmpi_inq_file_format(argv[optind+i], &fmt[i]); - HANDLE_FILE_ERR(argv[optind+i]) - - if (fmt[i] == NC_FORMAT_NETCDF4 || fmt[i] == NC_FORMAT_NETCDF4_CLASSIC) { -#ifndef ENABLE_NETCDF4 - /* HDF5 files are not supported when --enable-netcdf4 is not used */ - if (rank == 0) - fprintf(stderr, "Error: HDF5 based NetCDF4 file %s is not supported\n", - argv[optind+i]); - numHeadDIFF++; - quiet = 1; - goto cmp_exit; -#endif - } else if (fmt[i] == NC_FORMAT_BP) { - /* BP files are not supported */ - if (rank == 0) - fprintf(stderr, "Error: BP file %s is not supported\n", - argv[optind+i]); - numHeadDIFF++; - quiet = 1; - goto cmp_exit; - } else if (fmt[i] != NC_FORMAT_CLASSIC && - fmt[i] != NC_FORMAT_CDF2 && - fmt[i] != NC_FORMAT_CDF5) { - /* valid classic NetCDF files are CDF-1, CDF-2, and CDF-5 */ - if (rank == 0) - fprintf(stderr, "Error: %s is not a classic NetCDF file\n", - argv[optind+i]); - numHeadDIFF++; - quiet = 1; - goto cmp_exit; - } - - name[i] = (char*) calloc(NC_MAX_NAME, 1); - if (!name[i]) OOM_ERROR - - /* open files */ - err = ncmpi_open(comm, argv[optind+i], NC_NOWRITE, info, &ncid[i]); - HANDLE_ERROR - - err = ncmpi_inq(ncid[i], &ndims[i], &nvars[i], &natts[i], &recdim[i]); - HANDLE_ERROR - } - - /* compare file format */ - if (fmt[0] != fmt[1]) { - if (!quiet && rank == 0) - printf("DIFF: file format (CDF-%d) != (CDF-%d)\n",fmt[0], fmt[1]); - numHeadDIFF++; - /* even formats are different, we continue to compare the contents - * of the files (headers and variables). - */ - } - - /* compare file header */ - if (check_header && rank == 0) { /* only root checks header */ - int attnump; - - /* compare number of dimensions defined */ - if (ndims[0] != ndims[1]) { - if (!quiet) - printf("DIFF: number of dimensions (%d) != (%d)\n",ndims[0], ndims[1]); - numHeadDIFF++; - } - else if (verbose) - printf("SAME: number of dimensions (%d)\n",ndims[0]); - - /* compare number of variables defined */ - if (nvars[0] != nvars[1]) { - if (!quiet) - printf("DIFF: number of variables (%d) != (%d)\n",nvars[0], nvars[1]); - numHeadDIFF++; - } - else if (verbose) - printf("SAME: number of variables (%d)\n",nvars[0]); - - /* compare number of global attributes defined */ - if (natts[0] != natts[1]) { - if (!quiet) - printf("DIFF: number of global attributes (%d) != (%d)\n",natts[0], natts[1]); - numHeadDIFF++; - } - else if (verbose) - printf("SAME: number of global attributes (%d)\n",natts[0]); - - /* compare attributes defined in 1st file and also in 2nd file */ - for (i=0; i 0 && ndims[1] > 0) { - if (verbose) - printf("Dimension:\n"); - } else - goto cmp_vars; - - /* check dimensions in 1st file also appear in 2nd file */ - for (i=0; i 0 && nvars[1] > 0) { - if (verbose) - printf("Variables:\n"); - } else - goto cmp_exit; - - /* check variables defined in 1st file and also in 2nd file */ - for (i=0; i 0 && dimids[0][0] == recdim[0]) { /* record variable */ - err = ncmpi_inq_dimlen(ncid[0], recdim[0], &shape[0]); - HANDLE_ERROR - if (shape[0] == 0) { - /* No record has been written to the file, skip comparison */ - free(shape); - free(dimids[0]); - free(dimids[1]); - continue; - } - } - - /* calculate read amount of this process in start[] and shape[] */ - for (j=0; j= nprocs) { - MPI_Offset dimLen = shape[j]; - shape[j] = dimLen / nprocs; - start[j] = shape[j] * rank; - if (rank < dimLen % nprocs) { - start[j] += rank; - shape[j]++; - } - else - start[j] += dimLen % nprocs; - break; - } - } - /* if none of shape[*] >= nprocs, then let all processes compare the - * whole variable - */ - - varsize = 1; - /* block partition the variable along the 1st dimension */ - for (j=0; j= 0) { - err = ncmpi_close(ncid[i]); - HANDLE_ERROR - } - } - if (info != MPI_INFO_NULL) - MPI_Info_free(&info); - - /* summary of the difference */ - MPI_Reduce(&numVarDIFF, &varDIFF, 1, MPI_LONG_LONG_INT, MPI_SUM, 0, comm); - if (rank == 0 && !quiet) { - if (check_header) { - if (numHeadDIFF == 0) - printf("Headers of two files are the same\n"); - else - printf("Number of differences in header %lld\n",numHeadDIFF); - } - if (check_variable_list) { - if (varDIFF == 0) - printf("Compared variable(s) are the same\n"); - else - printf("Compared variables(s) has %lld differences\n",varDIFF); - } - if (check_entire_file) { - if (varDIFF == 0) - printf("All variables of two files are the same\n"); - else - printf("Number of differences in variables %lld\n",varDIFF); - } - } - - if (rank == 0) numDIFF = varDIFF + numHeadDIFF; - MPI_Bcast(&numDIFF, 1, MPI_LONG_LONG_INT, 0, comm); + MPI_Info_free(&info); MPI_Finalize(); + exit((numDIFF == 0) ? EXIT_SUCCESS : EXIT_FAILURE); } diff --git a/src/utils/ncmpidiff/ncmpidiff_core.c b/src/utils/ncmpidiff/ncmpidiff_core.c new file mode 100644 index 000000000..10e55c4d5 --- /dev/null +++ b/src/utils/ncmpidiff/ncmpidiff_core.c @@ -0,0 +1,954 @@ +/* + * Copyright (C) 2025, Northwestern University and Argonne National Laboratory + * See COPYRIGHT notice in top-level directory. + */ + +/* This core subroutines of utility program ncmpidiff. It compares header and + * variables of two files regardless the define order of the variables and + * attributes. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include /* stat() */ +#include /* stat() */ +#include /* stat() */ +#include /* errno */ +#include /* INFINITY */ + +#include +#include + +#include + +#ifndef ubyte +#define ubyte unsigned char +#endif +#ifndef ushort +#define ushort unsigned short +#endif +#ifndef uint +#define uint unsigned int +#endif +#ifndef int64 +#define int64 long long +#endif +#ifndef uint64 +#define uint64 unsigned long long +#endif + +#define PRINT_CMD_OPTS \ + if (first_diff && cmd_opts != NULL) { \ + printf("%s\n", cmd_opts); \ + first_diff = 0; \ + } + +#define OOM_ERROR { \ + fprintf(stderr, "Error: calloc() out of memory at line %d\n",__LINE__); \ + exit(1); \ +} + +#define HANDLE_ERROR { \ + if (err != NC_NOERR) { \ + fprintf(stderr, "Error at line %d of file %s (%s)\n", __LINE__, \ + __FILE__, ncmpi_strerror(err)); \ + MPI_Abort(MPI_COMM_WORLD, -1); \ + exit(-1); \ + } \ +} + +#define HANDLE_FILE_ERR(filename) { \ + if (err != NC_NOERR) { \ + fprintf(stderr, "Error at line %d: input file %s (%s)\n", __LINE__, \ + filename, ncmpi_strerror(err)); \ + MPI_Abort(MPI_COMM_WORLD, -1); \ + exit(-1); \ + } \ +} + +#define CHECK_GLOBAL_ATT_DIFF_CHAR { \ + int pos; \ + char *b1 = (char *)calloc((attlen[0] + 1) * 2, sizeof(char)); \ + char *b2 = b1 + attlen[0] + 1; \ + if (!b1) OOM_ERROR \ + err = ncmpi_get_att_text(ncid[0], NC_GLOBAL, name[0], b1); \ + HANDLE_ERROR \ + err = ncmpi_get_att_text(ncid[1], NC_GLOBAL, name[0], b2); \ + HANDLE_ERROR \ + for (pos=0; pos= 0) ? (x) : (-x) +#define UABS(x) (x) + +#define CHECK_VAR_DIFF(type, func, xabs) { \ + int pos, isDiff, worst = -1; \ + type *b1, *b2; \ + b1 = (type *)calloc(varsize * 2, sizeof(type)); \ + if (!b1) OOM_ERROR \ + b2 = b1 + varsize; \ + err = ncmpi_get_vara_##func(ncid[0], varid1, start, shape, b1); \ + HANDLE_ERROR \ + err = ncmpi_get_vara_##func(ncid[1], varid2, start, shape, b2); \ + HANDLE_ERROR \ + if (!check_tolerance) { \ + for (pos=0; pos abs_b2) ? abs_b1 : abs_b2; \ + diff = b1[pos] - b2[pos]; \ + diff = (diff >= 0) ? diff : -diff; \ + ratio = diff / abs_max; \ + if (diff <= tolerance_difference || ratio <= tolerance_ratio) \ + continue; \ + /* fail to meet both tolerance errors */ \ + worst = pos; \ + break; \ + } \ + } \ + if (pos != varsize || worst != -1) { /* diff is found */ \ + double v1, v2; \ + if (ndims[0] == 0) { /* scalar variable */ \ + PRINT_CMD_OPTS \ + if (worst == -1) \ + printf("DIFF: scalar variable \"%s\" of type \"%s\"\n", \ + name[0], get_type(xtype[0])); \ + else { \ + v1 = b1[worst]; \ + v2 = b2[worst]; \ + printf("DIFF (tolerance): scalar variable \"%s\" of type \"%s\" of value %g vs %g (difference = %e)\n", \ + name[0], get_type(xtype[0]), v1, v2, v1-v2); \ + } \ + } else { \ + int _i; \ + MPI_Offset *diffStart; \ + diffStart = (MPI_Offset*) malloc(sizeof(MPI_Offset) * ndims[0]); \ + if (worst != -1) pos = worst; \ + v1 = b1[pos]; \ + v2 = b2[pos]; \ + for (_i=ndims[0]-1; _i>=0; _i--) { \ + diffStart[_i] = pos % shape[_i] + start[_i]; \ + pos /= shape[_i]; \ + } \ + PRINT_CMD_OPTS \ + if (worst == -1) \ + printf("DIFF: variable \"%s\" of type \"%s\" at element ["OFFFMT, \ + name[0], get_type(xtype[0]), diffStart[0]); \ + else \ + printf("DIFF (tolerance): variable \"%s\" of type \"%s\" at element ["OFFFMT, \ + name[0], get_type(xtype[0]), diffStart[0]); \ + for (_i=1; _i----------------------------------------------------------*/ +static char* +get_type(int type) +{ + switch (type) { + case NC_BYTE: return "NC_BYTE"; + case NC_CHAR: return "NC_CHAR"; + case NC_SHORT: return "NC_SHORT"; + case NC_INT: return "NC_INT"; + case NC_FLOAT: return "NC_FLOAT"; + case NC_DOUBLE: return "NC_DOUBLE"; + case NC_UBYTE: return "NC_UBYTE"; + case NC_USHORT: return "NC_USHORT"; + case NC_UINT: return "NC_UINT"; + case NC_INT64: return "NC_INT64"; + case NC_UINT64: return "NC_UINT64"; + } + return "NC_NAT"; +} + +/*----< ncmpidiff_core() >---------------------------------------------------*/ +int ncmpidiff_core(const char *file1, + const char *file2, + MPI_Comm comm, + MPI_Info info, + int verbose, + int quiet, + int check_header, + int check_variable_list, + int check_entire_file, + int num_vars, + char **var_names, + int check_tolerance, + int first_diff, + char *cmd_opts, + double tolerance_difference, + double tolerance_ratio) +{ + char *name[2]; + int i, j, err, rank, nprocs; + int ncid[2], ndims[2], nvars[2], natts[2], recdim[2], *dimids[2], fmt[2]; + int cmp_nvars; + long long numVarDIFF=0, numHeadDIFF=0, varDIFF, numDIFF; + MPI_Offset *shape=NULL, varsize, *start=NULL; + MPI_Offset attlen[2], dimlen[2]; + nc_type xtype[2]; + + MPI_Comm_size(comm, &nprocs); + MPI_Comm_rank(comm, &rank); + + ncid[0] = ncid[1] = -1; + + if (verbose && rank == 0) { + printf("First file: %s\n", file1); + printf("Second file: %s\n", file2); + } + + /* compare file format */ + err = ncmpi_inq_file_format(file1, &fmt[0]); + HANDLE_FILE_ERR(file1) + err = ncmpi_inq_file_format(file2, &fmt[1]); + HANDLE_FILE_ERR(file2) + + if (fmt[0] != fmt[1]) { + if (!quiet && rank == 0) + printf("DIFF: file format (CDF-%d) != (CDF-%d)\n",fmt[0], fmt[1]); + numHeadDIFF++; + /* even formats are different, we continue to compare the contents + * of the files (headers and variables). + */ + } + + /* open files and retrieve headers into memory buffers */ + name[0] = (char*) calloc(NC_MAX_NAME, 1); + if (!name[0]) OOM_ERROR + name[1] = (char*) calloc(NC_MAX_NAME, 1); + if (!name[1]) OOM_ERROR + + /* open files */ + err = ncmpi_open(comm, file1, NC_NOWRITE, info, &ncid[0]); + HANDLE_ERROR + err = ncmpi_open(comm, file1, NC_NOWRITE, info, &ncid[1]); + HANDLE_ERROR + + /* retrieve metadata */ + err = ncmpi_inq(ncid[0], &ndims[0], &nvars[0], &natts[0], &recdim[0]); + HANDLE_ERROR + err = ncmpi_inq(ncid[1], &ndims[1], &nvars[1], &natts[1], &recdim[1]); + HANDLE_ERROR + + /* compare file header */ + if (check_header && rank == 0) { /* only root checks header */ + int attnump; + + /* compare number of dimensions defined */ + if (ndims[0] != ndims[1]) { + if (!quiet) + printf("DIFF: number of dimensions (%d) != (%d)\n",ndims[0], ndims[1]); + numHeadDIFF++; + } + else if (verbose) + printf("SAME: number of dimensions (%d)\n",ndims[0]); + + /* compare number of variables defined */ + if (nvars[0] != nvars[1]) { + if (!quiet) + printf("DIFF: number of variables (%d) != (%d)\n",nvars[0], nvars[1]); + numHeadDIFF++; + } + else if (verbose) + printf("SAME: number of variables (%d)\n",nvars[0]); + + /* compare number of global attributes defined */ + if (natts[0] != natts[1]) { + if (!quiet) + printf("DIFF: number of global attributes (%d) != (%d)\n",natts[0], natts[1]); + numHeadDIFF++; + } + else if (verbose) + printf("SAME: number of global attributes (%d)\n",natts[0]); + + /* compare attributes defined in 1st file and also in 2nd file */ + for (i=0; i 0 && ndims[1] > 0) { + if (verbose) + printf("Dimension:\n"); + } else + goto cmp_vars; + + /* check dimensions in 1st file also appear in 2nd file */ + for (i=0; i 0 && nvars[1] > 0) { + if (verbose) + printf("Variables:\n"); + } else + goto cmp_exit; + + /* check variables defined in 1st file and also in 2nd file */ + for (i=0; i 0 && dimids[0][0] == recdim[0]) { /* record variable */ + err = ncmpi_inq_dimlen(ncid[0], recdim[0], &shape[0]); + HANDLE_ERROR + if (shape[0] == 0) { + /* No record has been written to the file, skip comparison */ + free(shape); + free(dimids[0]); + free(dimids[1]); + continue; + } + } + + /* calculate read amount of this process in start[] and shape[] */ + for (j=0; j= nprocs) { + MPI_Offset dimLen = shape[j]; + shape[j] = dimLen / nprocs; + start[j] = shape[j] * rank; + if (rank < dimLen % nprocs) { + start[j] += rank; + shape[j]++; + } + else + start[j] += dimLen % nprocs; + break; + } + } + /* if none of shape[*] >= nprocs, then let all processes compare the + * whole variable + */ + + varsize = 1; + /* block partition the variable along the 1st dimension */ + for (j=0; j= 0) { + err = ncmpi_close(ncid[i]); + HANDLE_ERROR + } + } + + /* summary of the difference */ + MPI_Reduce(&numVarDIFF, &varDIFF, 1, MPI_LONG_LONG_INT, MPI_SUM, 0, comm); + if (rank == 0 && !quiet) { + if (check_header) { + if (numHeadDIFF == 0) + printf("Headers of two files are the same\n"); + else + printf("Number of differences in header %lld\n",numHeadDIFF); + } + if (check_variable_list) { + if (varDIFF == 0) + printf("Compared variable(s) are the same\n"); + else + printf("Compared variables(s) has %lld differences\n",varDIFF); + } + if (check_entire_file) { + if (varDIFF == 0) + printf("All variables of two files are the same\n"); + else + printf("Number of differences in variables %lld\n",varDIFF); + } + } + + if (rank == 0) numDIFF = varDIFF + numHeadDIFF; + MPI_Bcast(&numDIFF, 1, MPI_LONG_LONG_INT, 0, comm); + + return numDIFF; +} diff --git a/src/utils/ncmpidiff/ncmpidiff_core.h b/src/utils/ncmpidiff/ncmpidiff_core.h new file mode 100644 index 000000000..0368fc03b --- /dev/null +++ b/src/utils/ncmpidiff/ncmpidiff_core.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2025, Northwestern University and Argonne National Laboratory + * See COPYRIGHT notice in top-level directory. + */ + +#include + +int ncmpidiff_core(const char *file1, + const char *file2, + MPI_Comm comm, + MPI_Info info, + int verbose, + int quiet, + int check_header, + int check_variable_list, + int check_entire_file, + int num_vars, + char **var_names, + int check_tolerance, + int first_diff, + char *cmd_opts, + double tolerance_difference, + double tolerance_ratio);