56 double *ahtd, *aqtd, *avtd, ahtdm, aqtdm[
NQ], avtdm, *lat1_old, *lat2_old,
57 *lh1, *lh2, *lon1_old, *lon2_old, *lv1, *lv2, *rhtd, *rqtd, *rvtd, rhtdm,
58 rel_min[
NQ], rqtdm[
NQ], rvtdm, t0 =
59 0, x0[3], x1[3], x2[3], z1, *z1_old, z2, *z2_old, *work;
66 ALLOC(lon1_old,
double,
68 ALLOC(lat1_old,
double,
76 ALLOC(lon2_old,
double,
78 ALLOC(lat2_old,
double,
106 ERRMSG(
"Missing or invalid command-line arguments.\n\n"
107 "Usage: atm_dist <ctl> <dist.tab> <param> <atm1a> <atm1b> [<atm2a> <atm2b> ...]\n\n"
108 "Use -h for full help.");
113 (int)
scan_ctl(argv[1], argc, argv,
"DIST_ENS", -1,
"-999", NULL);
115 P(
scan_ctl(argv[1], argc, argv,
"DIST_Z0", -1,
"-1000", NULL));
117 P(
scan_ctl(argv[1], argc, argv,
"DIST_Z1", -1,
"1000", NULL));
119 scan_ctl(argv[1], argc, argv,
"DIST_LAT0", -1,
"-1000", NULL);
121 scan_ctl(argv[1], argc, argv,
"DIST_LAT1", -1,
"1000", NULL);
123 scan_ctl(argv[1], argc, argv,
"DIST_LON0", -1,
"-1000", NULL);
125 scan_ctl(argv[1], argc, argv,
"DIST_LON1", -1,
"1000", NULL);
126 const double zscore =
127 scan_ctl(argv[1], argc, argv,
"DIST_ZSCORE", -1,
"-999", NULL);
129 for (
int iq = 0; iq <
NQ; iq++)
133 LOG(1,
"Write transport deviations: %s", argv[2]);
136 if (!(out = fopen(argv[2],
"w")))
137 ERRMSG(
"Cannot create file!");
142 "# $2 = time difference [s]\n"
143 "# $3 = absolute horizontal distance (%s) [km]\n"
144 "# $4 = relative horizontal distance (%s) [%%]\n"
145 "# $5 = absolute vertical distance (%s) [km]\n"
146 "# $6 = relative vertical distance (%s) [%%]\n",
147 argv[3], argv[3], argv[3], argv[3]);
148 for (
int iq = 0; iq < ctl.
nq; iq++) {
150 scan_ctl(argv[1], argc, argv,
"DIST_REL_MIN", iq,
"0", NULL);
153 "# Relative %s differences are masked where |q1| + |q2| <= %g %s.\n",
156 "# $%d = %s absolute difference (%s) [%s]\n"
157 "# $%d = %s relative difference (%s) [%%]\n",
159 8 + 2 * iq, ctl.
qnt_name[iq], argv[3]);
161 fprintf(out,
"# $%d = number of particles\n\n", 7 + 2 * ctl.
nq);
164 for (
int f = 4; f < argc; f += 2) {
172 if (atm1->
np != atm2->
np)
173 ERRMSG(
"Different numbers of particles!");
186 for (
int ip = 0; ip < atm1->
np; ip++) {
187 ahtd[ip] = avtd[ip] = rhtd[ip] = rvtd[ip] = 0;
188 for (
int iq = 0; iq < ctl.
nq; iq++)
189 aqtd[iq *
NP + ip] = rqtd[iq *
NP + ip] = 0;
193 for (
int ip = 0; ip < atm1->
np; ip++) {
198 ERRMSG(
"Air parcel index does not match!");
203 || atm2->
q[ctl.
qnt_ens][ip] != ens))
207 if (!isfinite(atm1->
time[ip]) || !isfinite(atm2->
time[ip]))
211 if (atm1->
p[ip] > p0 || atm1->
p[ip] < p1
212 || atm1->
lon[ip] < lon0 || atm1->
lon[ip] > lon1
213 || atm1->
lat[ip] < lat0 || atm1->
lat[ip] > lat1)
215 if (atm2->
p[ip] > p0 || atm2->
p[ip] < p1
216 || atm2->
lon[ip] < lon0 || atm2->
lon[ip] > lon1
217 || atm2->
lat[ip] < lat0 || atm2->
lat[ip] > lat1)
227 ahtd[np] =
DIST(x1, x2);
229 for (
int iq = 0; iq < ctl.
nq; iq++)
230 aqtd[iq *
NP + np] = atm1->
q[iq][ip] - atm2->
q[iq][ip];
236 geo2cart(0, lon1_old[ip], lat1_old[ip], x0);
237 lh1[ip] +=
DIST(x0, x1);
238 lv1[ip] += fabs(z1_old[ip] - z1);
240 geo2cart(0, lon2_old[ip], lat2_old[ip], x0);
241 lh2[ip] +=
DIST(x0, x2);
242 lv2[ip] += fabs(z2_old[ip] - z2);
245 if (lh1[ip] + lh2[ip] > 0)
246 rhtd[np] = 200. *
DIST(x1, x2) / (lh1[ip] + lh2[ip]);
247 if (lv1[ip] + lv2[ip] > 0)
248 rvtd[np] = 200. * (z1 - z2) / (lv1[ip] + lv2[ip]);
252 for (
int iq = 0; iq < ctl.
nq; iq++) {
253 const double q1 = atm1->
q[iq][ip];
254 const double q2 = atm2->
q[iq][ip];
255 const double denom = fabs(q1) + fabs(q2);
256 if (denom <= rel_min[iq])
257 rqtd[iq *
NP + np] = GSL_NAN;
259 rqtd[iq *
NP + np] = 200. * (q1 - q2) / denom;
263 lon1_old[ip] = atm1->
lon[ip];
264 lat1_old[ip] = atm1->
lat[ip];
267 lon2_old[ip] = atm2->
lon[ip];
268 lat2_old[ip] = atm2->
lat[ip];
276 if (zscore > 0 && np > 1) {
279 const size_t n = (size_t) np;
280 const double muh = gsl_stats_mean(ahtd, 1, n);
281 const double muv = gsl_stats_mean(avtd, 1, n);
282 const double sigh = gsl_stats_sd(ahtd, 1, n);
283 const double sigv = gsl_stats_sd(avtd, 1, n);
287 for (
size_t i = 0; i < n; i++)
288 if (fabs((ahtd[i] - muh) / sigh) < zscore
289 && fabs((avtd[i] - muv) / sigv) < zscore) {
294 for (
int iq = 0; iq < ctl.
nq; iq++) {
295 aqtd[iq *
NP + np] = aqtd[iq *
NP + (int) i];
296 rqtd[iq *
NP + np] = rqtd[iq *
NP + (int) i];
303 if (strcasecmp(argv[3],
"mean") == 0) {
304 ahtdm = gsl_stats_mean(ahtd, 1, (
size_t) np);
305 rhtdm = gsl_stats_mean(rhtd, 1, (
size_t) np);
306 avtdm = gsl_stats_mean(avtd, 1, (
size_t) np);
307 rvtdm = gsl_stats_mean(rvtd, 1, (
size_t) np);
308 for (
int iq = 0; iq < ctl.
nq; iq++) {
309 aqtdm[iq] = gsl_stats_mean(&aqtd[iq *
NP], 1, (
size_t) np);
312 }
else if (strcasecmp(argv[3],
"stddev") == 0) {
313 ahtdm = gsl_stats_sd(ahtd, 1, (
size_t) np);
314 rhtdm = gsl_stats_sd(rhtd, 1, (
size_t) np);
315 avtdm = gsl_stats_sd(avtd, 1, (
size_t) np);
316 rvtdm = gsl_stats_sd(rvtd, 1, (
size_t) np);
317 for (
int iq = 0; iq < ctl.
nq; iq++) {
318 aqtdm[iq] = gsl_stats_sd(&aqtd[iq *
NP], 1, (
size_t) np);
321 }
else if (strcasecmp(argv[3],
"min") == 0) {
322 ahtdm = gsl_stats_min(ahtd, 1, (
size_t) np);
323 rhtdm = gsl_stats_min(rhtd, 1, (
size_t) np);
324 avtdm = gsl_stats_min(avtd, 1, (
size_t) np);
325 rvtdm = gsl_stats_min(rvtd, 1, (
size_t) np);
326 for (
int iq = 0; iq < ctl.
nq; iq++) {
327 aqtdm[iq] = gsl_stats_min(&aqtd[iq *
NP], 1, (
size_t) np);
330 }
else if (strcasecmp(argv[3],
"max") == 0) {
331 ahtdm = gsl_stats_max(ahtd, 1, (
size_t) np);
332 rhtdm = gsl_stats_max(rhtd, 1, (
size_t) np);
333 avtdm = gsl_stats_max(avtd, 1, (
size_t) np);
334 rvtdm = gsl_stats_max(rvtd, 1, (
size_t) np);
335 for (
int iq = 0; iq < ctl.
nq; iq++) {
336 aqtdm[iq] = gsl_stats_max(&aqtd[iq *
NP], 1, (
size_t) np);
339 }
else if (strcasecmp(argv[3],
"skew") == 0) {
340 ahtdm = gsl_stats_skew(ahtd, 1, (
size_t) np);
341 rhtdm = gsl_stats_skew(rhtd, 1, (
size_t) np);
342 avtdm = gsl_stats_skew(avtd, 1, (
size_t) np);
343 rvtdm = gsl_stats_skew(rvtd, 1, (
size_t) np);
344 for (
int iq = 0; iq < ctl.
nq; iq++) {
345 aqtdm[iq] = gsl_stats_skew(&aqtd[iq *
NP], 1, (
size_t) np);
348 }
else if (strcasecmp(argv[3],
"kurt") == 0) {
349 ahtdm = gsl_stats_kurtosis(ahtd, 1, (
size_t) np);
350 rhtdm = gsl_stats_kurtosis(rhtd, 1, (
size_t) np);
351 avtdm = gsl_stats_kurtosis(avtd, 1, (
size_t) np);
352 rvtdm = gsl_stats_kurtosis(rvtd, 1, (
size_t) np);
353 for (
int iq = 0; iq < ctl.
nq; iq++) {
354 aqtdm[iq] = gsl_stats_kurtosis(&aqtd[iq *
NP], 1, (
size_t) np);
357 }
else if (strcasecmp(argv[3],
"absdev") == 0) {
358 ahtdm = gsl_stats_absdev_m(ahtd, 1, (
size_t) np, 0.0);
359 rhtdm = gsl_stats_absdev_m(rhtd, 1, (
size_t) np, 0.0);
360 avtdm = gsl_stats_absdev_m(avtd, 1, (
size_t) np, 0.0);
361 rvtdm = gsl_stats_absdev_m(rvtd, 1, (
size_t) np, 0.0);
362 for (
int iq = 0; iq < ctl.
nq; iq++) {
363 aqtdm[iq] = gsl_stats_absdev_m(&aqtd[iq *
NP], 1, (
size_t) np, 0.0);
366 }
else if (strcasecmp(argv[3],
"median") == 0) {
367 ahtdm = gsl_stats_median(ahtd, 1, (
size_t) np);
368 rhtdm = gsl_stats_median(rhtd, 1, (
size_t) np);
369 avtdm = gsl_stats_median(avtd, 1, (
size_t) np);
370 rvtdm = gsl_stats_median(rvtd, 1, (
size_t) np);
371 for (
int iq = 0; iq < ctl.
nq; iq++) {
372 aqtdm[iq] = gsl_stats_median(&aqtd[iq *
NP], 1, (
size_t) np);
375 }
else if (strcasecmp(argv[3],
"mad") == 0) {
376 ahtdm = gsl_stats_mad0(ahtd, 1, (
size_t) np, work);
377 rhtdm = gsl_stats_mad0(rhtd, 1, (
size_t) np, work);
378 avtdm = gsl_stats_mad0(avtd, 1, (
size_t) np, work);
379 rvtdm = gsl_stats_mad0(rvtd, 1, (
size_t) np, work);
380 for (
int iq = 0; iq < ctl.
nq; iq++) {
381 aqtdm[iq] = gsl_stats_mad0(&aqtd[iq *
NP], 1, (
size_t) np, work);
385 ERRMSG(
"Unknown parameter!");
388 fprintf(out,
"%.2f %.2f %g %g %g %g", t, t - t0,
389 ahtdm, rhtdm, avtdm, rvtdm);
390 for (
int iq = 0; iq < ctl.
nq; iq++) {
396 fprintf(out,
" %d\n", np);
432 printf(
"\nMPTRAC atm_dist tool.\n\n");
434 (
"Calculate transport deviations between pairs of trajectory data sets.\n");
438 (
" atm_dist <ctl> <dist.tab> <param> <atm1a> <atm1b> [<atm2a> <atm2b> ...]\n");
440 printf(
"Arguments:\n");
441 printf(
" <ctl> Control file.\n");
442 printf(
" <dist.tab> Output table.\n");
444 (
" <param> Statistic: mean, stddev, min, max, skew, kurt, absdev,\n");
445 printf(
" median, or mad.\n");
446 printf(
" <atm*a/b> Atmospheric input files to compare pairwise.\n");
447 printf(
"\nControl parameters:\n");
449 (
" DIST_REL_MIN[iq] Mask qnt-specific relative differences where\n");
451 (
" |q1| + |q2| is smaller than or equal to this\n");
452 printf(
" threshold in the qnt unit [default: 0].\n");
453 printf(
"\nFurther information:\n");
454 printf(
" Manual: https://slcs-jsc.github.io/mptrac/\n");
468 for (
int i = 0; i < np; i++)
469 if (isfinite(data[i]))
475 if (strcasecmp(param,
"mean") == 0)
476 return gsl_stats_mean(work, 1, (
size_t) n);
477 if (strcasecmp(param,
"stddev") == 0)
478 return gsl_stats_sd(work, 1, (
size_t) n);
479 if (strcasecmp(param,
"min") == 0)
480 return gsl_stats_min(work, 1, (
size_t) n);
481 if (strcasecmp(param,
"max") == 0)
482 return gsl_stats_max(work, 1, (
size_t) n);
483 if (strcasecmp(param,
"skew") == 0)
484 return gsl_stats_skew(work, 1, (
size_t) n);
485 if (strcasecmp(param,
"kurt") == 0)
486 return gsl_stats_kurtosis(work, 1, (
size_t) n);
487 if (strcasecmp(param,
"absdev") == 0)
488 return gsl_stats_absdev_m(work, 1, (
size_t) n, 0.0);
489 if (strcasecmp(param,
"median") == 0)
490 return gsl_stats_median(work, 1, (
size_t) n);
491 if (strcasecmp(param,
"mad") == 0)
492 return gsl_stats_mad0(work, 1, (
size_t) n, work);
494 ERRMSG(
"Unknown parameter!");
int main(int argc, char *argv[])
double finite_stat(const double *data, int np, const char *param, double *work)
Calculate a statistic on finite values only.
void usage(void)
Print command-line help.
double scan_ctl(const char *filename, int argc, char *argv[], const char *varname, const int arridx, const char *defvalue, char *value)
Scans a control file or command-line arguments for a specified variable.
double time_from_filename(const char *filename, const int offset)
Extracts and converts a timestamp from a filename to Julian seconds.
int mptrac_read_atm(const char *filename, const ctl_t *ctl, atm_t *atm)
Reads air parcel data from a specified file into the given atmospheric structure.
void mptrac_read_ctl(const char *filename, int argc, char *argv[], ctl_t *ctl)
Reads control parameters from a configuration file and populates the given structure.
void geo2cart(const double z, const double lon, const double lat, double *x)
Converts geographic coordinates (longitude, latitude, altitude) to Cartesian coordinates.
MPTRAC library declarations.
#define ERRMSG(...)
Print an error message with contextual information and terminate the program.
#define USAGE
Print usage information on -h or --help.
#define Z(p)
Convert pressure to altitude.
#define P(z)
Compute pressure at given altitude.
#define NQ
Maximum number of quantities per data point.
#define ALLOC(ptr, type, n)
Allocate memory for a pointer with error handling.
#define NP
Maximum number of atmospheric data points.
#define LOG(level,...)
Print a log message with a specified logging level.
#define DIST(a, b)
Calculate the distance between two points in Cartesian coordinates.
double lat[NP]
Latitude [deg].
double lon[NP]
Longitude [deg].
int np
Number of air parcels.
double q[NQ][NP]
Quantity data (for various, user-defined attributes).
double p[NP]
Pressure [hPa].
char qnt_format[NQ][LEN]
Quantity output format.
int atm_type
Type of atmospheric data files (0=ASCII, 1=binary, 2=netCDF, 3=CLaMS_traj, 4=CLaMS_pos).
char qnt_unit[NQ][LEN]
Quantity units.
char qnt_name[NQ][LEN]
Quantity names.
int qnt_ens
Quantity array index for ensemble IDs.
int qnt_idx
Quantity array index for air parcel IDs.
int nq
Number of quantities.