28#include "jurassic_unified_library.h"
85 const obs_t * obs_test,
86 const obs_t * obs_ref,
95 const obs_t * obs_test,
96 const obs_t * obs_ref,
179 ERRMSG(
"Missing or invalid command-line arguments.\n\n"
180 "Usage: formod <ctl> <obs> <atm> <rad> [KEY VALUE ...]\n\n"
181 "Use -h for full help.");
194 read_obs(NULL, argv[2], &ctl, &obs, 0);
198 read_atm(NULL, argv[3], &ctl, &atm, 0);
202 jur_unified_init(argc, argv);
203 jur_unified_formod_multiple_packages(&atm, &obs, 1, NULL);
211 char dirlist[
LEN], obsref[
LEN], task[
LEN];
221 scan_ctl(argc, argv,
"TASK", -1,
"forward", task);
226 scan_ctl(argc, argv,
"DIRLIST", -1,
"-", dirlist);
230 scan_ctl(argc, argv,
"OBSREF", -1,
"-", obsref);
233 if (dirlist[0] ==
'-')
234 call_formod(&ctl, tbl, NULL, argv[2], argv[3], argv[4], task_mode, obsref,
235 formod_scalar, batch_size);
242 if (!(in = fopen(dirlist,
"r")))
243 ERRMSG(
"Cannot open directory list!");
247 while (fscanf(in,
"%4999s", wrkdir) != EOF) {
250 LOG(1,
"\nWorking directory: %s", wrkdir);
253 call_formod(&ctl, tbl, wrkdir, argv[2], argv[3], argv[4], task_mode,
254 obsref, formod_scalar, batch_size);
276 printf(
"\nJURASSIC forward model.\n\n");
278 (
"Compute simulated radiances or transmittances for the configured\n");
280 (
"channels from observation geometry, atmospheric state, and control\n");
281 printf(
"settings.\n\n");
283 printf(
" formod <ctl> <obs> <atm> <rad> [KEY VALUE ...]\n\n");
284 printf(
"Arguments:\n");
285 printf(
" <ctl> Control file.\n");
286 printf(
" <obs> Observation geometry input file.\n");
287 printf(
" <atm> Atmospheric state input file.\n");
288 printf(
" <rad> Output file for simulated radiances or transmittances.\n");
289 printf(
" [KEY VALUE] Optional control parameters.\n\n");
290 printf(
"Tool-specific control parameters:\n");
291 printf(
" TASK <name> Select the operation mode.\n");
293 (
" forward or omitted: regular forward-model simulation.\n");
295 (
" contrib: write separate outputs for individual emitter\n");
296 printf(
" extinction contributions.\n");
298 (
" profiles: process observation data profile by profile,\n");
300 (
" matching each profile to the corresponding\n");
301 printf(
" atmospheric profile by time.\n");
303 (
" stepsize: analyze sensitivity to ray-tracing step\n");
306 (
" time: run the built-in runtime benchmark using\n");
308 (
" perturbed versions of the input atmosphere.\n");
310 (
" DIRLIST <file> Read working directories from <file> and run one case\n");
312 (
" per directory using the same <obs>, <atm>, and <rad>\n");
313 printf(
" filenames relative to each listed directory.\n");
315 (
" OBSREF <file> Read reference observations from <file> and print\n");
317 (
" relative and signed absolute-error diagnostics against\n");
319 (
" the simulated output.\n");
321 (
" EXECUTION <mode> Select execution backend: batch (default) or scalar.\n");
323 (
" BATCH_SIZE <n> Batch size for TASK=time with EXECUTION=batch.\n");
325 printf(
"Common control parameters:\n");
327 (
" TBLBASE, TBLFMT Lookup-table base name and format.\n");
329 (
" ATMFMT, OBSFMT Atmospheric and observation file formats.\n");
330 printf(
" NG, EMITTER[i] Active emitters.\n");
331 printf(
" ND, NU[i], NW, WINDOW[i] Spectral channels and windows.\n");
333 (
" NCL, CLNU[i], NSF, SFNU[i] Cloud and surface spectral grids.\n");
335 (
" WRITE_BBT, FORMOD Output units and forward-model selection.\n");
336 printf(
" CTM_*, REFRAC Continua and refractivity.\n");
338 (
" RAYDS, RAYDZ, FOV Ray tracing and field of view.\n\n");
339 printf(
"Further information:\n");
340 printf(
" Manual: https://slcs-jsc.github.io/jurassic/\n");
348 if (task[0] ==
'-' || strcmp(task,
"forward") == 0)
350 if (task[0] ==
'c' || strcmp(task,
"contrib") == 0
351 || strcmp(task,
"contributions") == 0)
353 if (task[0] ==
'p' || strcmp(task,
"profile") == 0
354 || strcmp(task,
"profiles") == 0)
356 if (task[0] ==
's' || strcmp(task,
"stepsize") == 0)
358 if (task[0] ==
't' || strcmp(task,
"time") == 0
359 || strcmp(task,
"benchmark") == 0)
362 ERRMSG(
"Unknown TASK '%s'!", task);
373 scan_ctl(argc, argv,
"EXECUTION", -1,
"batch", execution);
375 if (strcmp(execution,
"batch") == 0)
377 if (strcmp(execution,
"scalar") == 0)
380 ERRMSG(
"Unknown EXECUTION '%s'!", execution);
390 const int batch_size =
391 (int)
scan_ctl(argc, argv,
"BATCH_SIZE", -1,
"1", NULL);
394 ERRMSG(
"BATCH_SIZE must be positive!");
411 const int status =
formod(ctl, tbl, atm, obs, los_scratch, obs_scratch);
413 ERRMSG(
"Forward model failed with status code %d!", status);
418 formod_batch(ctl, tbl, atm, obs, 1, status, los_scratch, obs_scratch);
420 ERRMSG(
"Forward model failed with status code %d!", status[0]);
440 for (
int ir = 0; ir < obs->
nr; ir++) {
443 for (
int ip = 0; ip < atm->
np; ip++)
444 if (atm->
time[ip] == obs->
time[ir]) {
445 atm_scratch->
time[atm_scratch->
np] = atm->
time[ip];
446 atm_scratch->
z[atm_scratch->
np] = atm->
z[ip];
447 atm_scratch->
lon[atm_scratch->
np] = atm->
lon[ip];
448 atm_scratch->
lat[atm_scratch->
np] = atm->
lat[ip];
449 atm_scratch->
p[atm_scratch->
np] = atm->
p[ip];
450 atm_scratch->
t[atm_scratch->
np] = atm->
t[ip];
451 for (
int ig = 0; ig < ctl->
ng; ig++)
452 atm_scratch->
q[ig][atm_scratch->
np] = atm->
q[ig][ip];
453 for (
int iw = 0; iw < ctl->
nw; iw++)
454 atm_scratch->
k[iw][atm_scratch->
np] = atm->
k[iw][ip];
459 obs_single->
time[0] = obs->
time[ir];
460 obs_single->
vpz[0] = obs->
vpz[ir];
463 obs_single->
obsz[0] = obs->
obsz[ir];
467 if (atm_scratch->
np > 0) {
469 obs_scratch, formod_scalar);
471 for (
int id = 0;
id < ctl->
nd;
id++) {
472 obs->
rad[id][ir] = obs_single->
rad[id][0];
473 obs->
tau[id][ir] = obs_single->
tau[id][0];
493 read_obs(wrkdir, obsref, ctl, obs_ref, 0);
495 double mre[
ND], sdre[
ND], minre[
ND], maxre[
ND];
496 double meanae[
ND], sdae[
ND], minae[
ND], maxae[
ND];
500 for (
int id = 0;
id < ctl->
nd;
id++) {
502 (
"EVAL: nu= %.4f cm^-1 | MeanRE= %g %% | SDRE= %g %% | MinRE= %g %% | MaxRE= %g %%\n",
503 ctl->
nu[
id], mre[
id], sdre[
id], minre[
id], maxre[
id]);
505 (
"EVAL: nu= %.4f cm^-1 | MeanAE= %g | SDAE= %g | MinAE= %g | MaxAE= %g\n",
506 ctl->
nu[
id], meanae[
id], sdae[
id], minae[
id], maxae[
id]);
533 for (
int ig = 0; ig < ctl->
ng; ig++) {
536 for (
int iw = 0; iw < ctl->
nw; iw++)
537 for (
int ip = 0; ip < atm_scratch->
np; ip++)
538 atm_scratch->
k[iw][ip] = 0;
540 for (
int ig2 = 0; ig2 < ctl->
ng; ig2++)
542 for (
int ip = 0; ip < atm_scratch->
np; ip++)
543 atm_scratch->
q[ig2][ip] = 0;
546 obs_scratch, formod_scalar);
548 sprintf(filename,
"%s.%s", radfile, ctl->
emitter[ig]);
549 write_obs(wrkdir, filename, ctl, obs, 0);
554 for (
int ig = 0; ig < ctl->
ng; ig++)
555 for (
int ip = 0; ip < atm_scratch->
np; ip++)
556 atm_scratch->
q[ig][ip] = 0;
561 sprintf(filename,
"%s.EXTINCT", radfile);
562 write_obs(wrkdir, filename, ctl, obs, 0);
575 double *dtemp, *dpress, *dq;
578 ALLOC(dpress,
double,
585 gsl_rng *rng = gsl_rng_alloc(gsl_rng_default);
586 gsl_rng_set(rng, 0UL);
587 for (
int ib = 0; ib < batch_size; ib++) {
590 for (
int ig = 0; ig < ctl->
ng; ig++)
591 dq[ib *
NG + ig] = 1;
594 dtemp[ib] = 40.0 * (gsl_rng_uniform(rng) - 0.5);
595 dpress[ib] = 1.0 - 0.1 * gsl_rng_uniform(rng);
596 for (
int ig = 0; ig < ctl->
ng; ig++)
597 dq[ib *
NG + ig] = 0.8 + 0.4 * gsl_rng_uniform(rng);
602#pragma omp parallel for schedule(static)
603 for (
int ib = 0; ib < batch_size; ib++) {
604 atm_batch[ib] = *atm;
605 obs_batch[ib] = *obs;
606 for (
int ip = 0; ip < atm_batch[ib].
np; ip++) {
607 atm_batch[ib].
t[ip] += dtemp[ib];
608 atm_batch[ib].
p[ip] *= dpress[ib];
609 for (
int ig = 0; ig < ctl->
ng; ig++)
610 atm_batch[ib].q[ig][ip] *= dq[ib *
NG + ig];
633 double t_min = 0, t_max = 0, t_mean = 0, t_sd = 0;
637 gsl_rng *rng = gsl_rng_alloc(gsl_rng_default);
639 if (formod_scalar && batch_size > 1)
640 ERRMSG(
"BATCH_SIZE > 1 cannot be combined with EXECUTION scalar!");
643 atm_t *atm_batch = NULL;
644 obs_t *obs_batch = NULL;
646 los_t *los_batch = NULL;
647 obs_t *obs_scratch_batch = NULL;
648 if (batch_size > 1) {
658 formod_batch(ctl, tbl, atm_batch, obs_batch, batch_size, status,
659 los_batch, obs_scratch_batch);
663 const char *env_max_iter = getenv(
"JURASSIC_MAX_ITER");
664 const int max_iter = env_max_iter ? atoi(env_max_iter) : INT_MAX;
665 const char *env_budget = getenv(
"JURASSIC_TIME_BUDGET");
666 const double t_budget = env_budget ? atof(env_budget) : 10.0;
672 if (batch_size > 1) {
673 const double t0 = omp_get_wtime();
674 formod_batch(ctl, tbl, atm_batch, obs_batch, batch_size, status,
675 los_batch, obs_scratch_batch);
676 dt = omp_get_wtime() - t0;
677 for (
int ib = 0; ib < batch_size; ib++)
679 ERRMSG(
"Batch benchmark failed with status %d at element %d!",
683 const double dtemp = 40. * (gsl_rng_uniform(rng) - 0.5);
684 const double dpress = 1. - 0.1 * gsl_rng_uniform(rng);
686 for (
int ig = 0; ig < ctl->
ng; ig++)
687 dq[ig] = 0.8 + 0.4 * gsl_rng_uniform(rng);
688 for (
int ip = 0; ip < atm_scratch->
np; ip++) {
689 atm_scratch->
t[ip] += dtemp;
690 atm_scratch->
p[ip] *= dpress;
691 for (
int ig = 0; ig < ctl->
ng; ig++)
692 atm_scratch->
q[ig][ip] *= dq[ig];
695 const double t0 = omp_get_wtime();
697 obs_scratch, formod_scalar);
698 dt = omp_get_wtime() - t0;
703 if (n == 0 || dt < t_min)
705 if (n == 0 || dt > t_max)
709 }
while (t_mean < t_budget && n < max_iter);
712 if (batch_size > 1) {
714 free(obs_scratch_batch);
721 t_mean /= (double) n;
722 t_sd = sqrt(t_sd / (
double) n -
POW2(t_mean));
723 printf(
"RUNTIME: execution= %s | threads= %d | batch_size= %d | mean= %g s"
724 " | stddev= %g s | min= %g s | max= %g s | per_model= %g s"
725 " | throughput= %g models/s | samples= %d\n",
726 formod_scalar ?
"scalar" :
"batch", omp_get_max_threads(),
727 batch_size, t_mean, t_sd, t_min, t_max,
728 t_mean / batch_size, batch_size / t_mean, n);
753 for (
double dz = 0.01; dz <= 2; dz *= 1.1)
754 for (
double ds = 0.1; ds <= 50; ds *= 1.1) {
758 double t0 = omp_get_wtime();
760 obs_scratch, formod_scalar);
761 double dt = omp_get_wtime() - t0;
763 double mre[
ND], sdre[
ND], minre[
ND], maxre[
ND];
766 for (
int id = 0;
id < ctl->
nd;
id++)
768 (
"STEPSIZE: ds= %.4f km | dz= %g km | t= %g s | nu= %.4f cm^-1"
769 " | MeanRE= %g %% | SDRE= %g %% | MinRE= %g %% | MaxRE= %g %%\n",
770 ds, dz, dt, ctl->
nu[
id], mre[
id], sdre[
id], minre[
id],
789 static atm_t atm, atm2;
790 static obs_t obs, obs2, obs_ref, obs_scratch;
791 static los_t los_scratch;
795 read_atm(wrkdir, atmfile, ctl, &atm, 0);
799 read_obs(wrkdir, obsfile, ctl, &obs, 0);
802 if (task_mode ==
'p') {
804 &los_scratch, &obs_scratch, formod_scalar);
810 if (task_mode !=
't' && batch_size > 1)
811 ERRMSG(
"BATCH_SIZE > 1 is only supported for TASK=time!");
813 if (task_mode ==
't') {
822 write_obs(wrkdir, radfile, ctl, &obs, 0);
824 if (obsref[0] !=
'-') {
828 if (task_mode ==
'c') {
830 &los_scratch, &obs_scratch, formod_scalar);
833 if (task_mode ==
't') {
835 &obs_scratch, formod_scalar, batch_size);
838 if (task_mode ==
's') {
840 &obs_scratch, formod_scalar);
849 const obs_t *obs_test,
850 const obs_t *obs_ref,
857 for (
int id = 0;
id < ctl->
nd;
id++) {
859 double sum = 0, sum2 = 0;
865 for (
int ir = 0; ir < obs_test->
nr; ir++) {
868 if (!isfinite(obs_test->
rad[
id][ir])
869 || !isfinite(obs_ref->
rad[
id][ir])
870 || obs_ref->
rad[
id][ir] == 0)
874 double err = 100.0 * (obs_test->
rad[id][ir] - obs_ref->
rad[id][ir])
875 / obs_ref->
rad[
id][ir];
890 sdre[id] = sqrt(fmax(0, sum2 / n - mre[
id] * mre[
id]));
892 mre[id] = sdre[id] = minre[id] = maxre[id] = NAN;
900 const obs_t *obs_test,
901 const obs_t *obs_ref,
908 for (
int id = 0;
id < ctl->
nd;
id++) {
910 double sum = 0, sum2 = 0;
916 for (
int ir = 0; ir < obs_test->
nr; ir++) {
919 if (!isfinite(obs_test->
rad[
id][ir])
920 || !isfinite(obs_ref->
rad[
id][ir]))
924 double err = obs_test->
rad[id][ir] - obs_ref->
rad[id][ir];
938 meanae[id] = sum / n;
939 sdae[id] = sqrt(fmax(0, sum2 / n - meanae[
id] * meanae[
id]));
941 meanae[id] = sdae[id] = minae[id] = maxae[id] = NAN;
void tbl_free(const ctl_t *ctl, tbl_t *tbl)
Free lookup table and all internally allocated memory.
void read_ctl(int argc, char *argv[], ctl_t *ctl)
Read model control parameters from command-line and configuration input.
void read_obs(const char *dirname, const char *filename, const ctl_t *ctl, obs_t *obs, int profile)
Read observation data from an input file.
void write_obs(const char *dirname, const char *filename, const ctl_t *ctl, const obs_t *obs, int profile)
Write observation data to an output file in ASCII or binary format.
void read_atm(const char *dirname, const char *filename, const ctl_t *ctl, atm_t *atm, int profile)
Read atmospheric input data from a file.
void copy_obs(const ctl_t *ctl, obs_t *obs_dest, const obs_t *obs_src, const int init)
Copy or initialize observation geometry and radiance data.
void formod_batch(const ctl_t *ctl, const tbl_t *tbl, atm_t *atm, obs_t *obs, const int nbatch, int *status, los_t *los_scratch, obs_t *obs_scratch)
Execute the forward model for a batch of independent cases.
double scan_ctl(int argc, char *argv[], const char *varname, const int arridx, const char *defvalue, char *value)
Scan control file or command-line arguments for a configuration variable.
tbl_t * read_tbl(const ctl_t *ctl)
Read emissivity lookup tables from disk.
void copy_atm(const ctl_t *ctl, atm_t *atm_dest, const atm_t *atm_src, const int init)
Copy or initialize atmospheric profile data.
int formod(const ctl_t *ctl, const tbl_t *tbl, atm_t *atm, obs_t *obs, los_t *los_scratch, obs_t *obs_scratch)
Execute one complete forward-model calculation.
JURASSIC library declarations.
#define LEN
Maximum length of ASCII data lines.
#define POW2(x)
Compute the square of a value.
#define SELECT_TIMER(id, group)
Switch to a named aggregated timer.
#define ERRMSG(...)
Print an error message with contextual information and terminate the program.
#define USAGE
Print usage information on -h or --help.
#define ND
Maximum number of radiance channels.
#define ALLOC(ptr, type, n)
Allocate memory for an array.
#define PRINT_TIMERS
Print aggregated timer statistics.
#define NG
Maximum number of emitters.
#define LOG(level,...)
Print a log message with a specified logging level.
Atmospheric profile data.
double time[NP]
Time (seconds since 2000-01-01T00:00Z).
double k[NW][NP]
Extinction [km^-1].
double lat[NP]
Latitude [deg].
double lon[NP]
Longitude [deg].
double t[NP]
Temperature [K].
int np
Number of data points.
double z[NP]
Altitude [km].
double q[NG][NP]
Volume mixing ratio [ppv].
double p[NP]
Pressure [hPa].
int nw
Number of spectral windows.
double nu[ND]
Centroid wavenumber of each channel [cm^-1].
int ctm_co2
Compute CO2 continuum (0=no, 1=yes).
int ctm_n2
Compute N2 continuum (0=no, 1=yes).
int ctm_h2o
Compute H2O continuum (0=no, 1=yes).
int ng
Number of emitters.
int nd
Number of radiance channels.
int ctm_o2
Compute O2 continuum (0=no, 1=yes).
char emitter[NG][LEN]
Name of each emitter.
double rayds
Maximum step length for raytracing [km].
double raydz
Vertical step length for raytracing [km].
Observation geometry and radiance data.
double tau[ND][NR]
Transmittance of ray path.
double rad[ND][NR]
Radiance [W/(m^2 sr cm^-1)].
double vpz[NR]
View point altitude [km].
double vplat[NR]
View point latitude [deg].
double obslon[NR]
Observer longitude [deg].
double obslat[NR]
Observer latitude [deg].
double obsz[NR]
Observer altitude [km].
double vplon[NR]
View point longitude [deg].
double time[NR]
Time (seconds since 2000-01-01T00:00Z).
int nr
Number of ray paths.
Emissivity look-up tables.