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");
318 printf(
" the simulated output.\n");
320 (
" EXECUTION <mode> Select execution backend: batch (default) or scalar.\n");
322 (
" BATCH_SIZE <n> Batch size for TASK=time with EXECUTION=batch.\n");
324 printf(
"Common control parameters:\n");
326 (
" TBLBASE, TBLFMT Lookup-table base name and format.\n");
328 (
" ATMFMT, OBSFMT Atmospheric and observation file formats.\n");
329 printf(
" NG, EMITTER[i] Active emitters.\n");
330 printf(
" ND, NU[i], NW, WINDOW[i] Spectral channels and windows.\n");
332 (
" NCL, CLNU[i], NSF, SFNU[i] Cloud and surface spectral grids.\n");
334 (
" WRITE_BBT, FORMOD Output units and forward-model selection.\n");
335 printf(
" CTM_*, REFRAC Continua and refractivity.\n");
337 (
" RAYDS, RAYDZ, FOV Ray tracing and field of view.\n\n");
338 printf(
"Further information:\n");
339 printf(
" Manual: https://slcs-jsc.github.io/jurassic/\n");
347 if (task[0] ==
'-' || strcmp(task,
"forward") == 0)
349 if (task[0] ==
'c' || strcmp(task,
"contrib") == 0
350 || strcmp(task,
"contributions") == 0)
352 if (task[0] ==
'p' || strcmp(task,
"profile") == 0
353 || strcmp(task,
"profiles") == 0)
355 if (task[0] ==
's' || strcmp(task,
"stepsize") == 0)
357 if (task[0] ==
't' || strcmp(task,
"time") == 0
358 || strcmp(task,
"benchmark") == 0)
361 ERRMSG(
"Unknown TASK '%s'!", task);
372 scan_ctl(argc, argv,
"EXECUTION", -1,
"batch", execution);
374 if (strcmp(execution,
"batch") == 0)
376 if (strcmp(execution,
"scalar") == 0)
379 ERRMSG(
"Unknown EXECUTION '%s'!", execution);
389 const int batch_size =
390 (int)
scan_ctl(argc, argv,
"BATCH_SIZE", -1,
"1", NULL);
393 ERRMSG(
"BATCH_SIZE must be positive!");
410 const int status =
formod(ctl, tbl, atm, obs, los_scratch, obs_scratch);
412 ERRMSG(
"Forward model failed with status code %d!", status);
417 formod_batch(ctl, tbl, atm, obs, 1, status, los_scratch, obs_scratch);
419 ERRMSG(
"Forward model failed with status code %d!", status[0]);
439 for (
int ir = 0; ir < obs->
nr; ir++) {
442 for (
int ip = 0; ip < atm->
np; ip++)
443 if (atm->
time[ip] == obs->
time[ir]) {
444 atm_scratch->
time[atm_scratch->
np] = atm->
time[ip];
445 atm_scratch->
z[atm_scratch->
np] = atm->
z[ip];
446 atm_scratch->
lon[atm_scratch->
np] = atm->
lon[ip];
447 atm_scratch->
lat[atm_scratch->
np] = atm->
lat[ip];
448 atm_scratch->
p[atm_scratch->
np] = atm->
p[ip];
449 atm_scratch->
t[atm_scratch->
np] = atm->
t[ip];
450 for (
int ig = 0; ig < ctl->
ng; ig++)
451 atm_scratch->
q[ig][atm_scratch->
np] = atm->
q[ig][ip];
452 for (
int iw = 0; iw < ctl->
nw; iw++)
453 atm_scratch->
k[iw][atm_scratch->
np] = atm->
k[iw][ip];
458 obs_single->
time[0] = obs->
time[ir];
459 obs_single->
vpz[0] = obs->
vpz[ir];
462 obs_single->
obsz[0] = obs->
obsz[ir];
466 if (atm_scratch->
np > 0) {
468 obs_scratch, formod_scalar);
470 for (
int id = 0;
id < ctl->
nd;
id++) {
471 obs->
rad[id][ir] = obs_single->
rad[id][0];
472 obs->
tau[id][ir] = obs_single->
tau[id][0];
492 read_obs(wrkdir, obsref, ctl, obs_ref, 0);
494 double mre[
ND], sdre[
ND], minre[
ND], maxre[
ND];
495 double meanae[
ND], sdae[
ND], minae[
ND], maxae[
ND];
499 for (
int id = 0;
id < ctl->
nd;
id++) {
501 (
"EVAL: nu= %.4f cm^-1 | MeanRE= %g %% | SDRE= %g %% | MinRE= %g %% | MaxRE= %g %%\n",
502 ctl->
nu[
id], mre[
id], sdre[
id], minre[
id], maxre[
id]);
504 (
"EVAL: nu= %.4f cm^-1 | MeanAE= %g | SDAE= %g | MinAE= %g | MaxAE= %g\n",
505 ctl->
nu[
id], meanae[
id], sdae[
id], minae[
id], maxae[
id]);
532 for (
int ig = 0; ig < ctl->
ng; ig++) {
535 for (
int iw = 0; iw < ctl->
nw; iw++)
536 for (
int ip = 0; ip < atm_scratch->
np; ip++)
537 atm_scratch->
k[iw][ip] = 0;
539 for (
int ig2 = 0; ig2 < ctl->
ng; ig2++)
541 for (
int ip = 0; ip < atm_scratch->
np; ip++)
542 atm_scratch->
q[ig2][ip] = 0;
545 obs_scratch, formod_scalar);
547 sprintf(filename,
"%s.%s", radfile, ctl->
emitter[ig]);
548 write_obs(wrkdir, filename, ctl, obs, 0);
553 for (
int ig = 0; ig < ctl->
ng; ig++)
554 for (
int ip = 0; ip < atm_scratch->
np; ip++)
555 atm_scratch->
q[ig][ip] = 0;
560 sprintf(filename,
"%s.EXTINCT", radfile);
561 write_obs(wrkdir, filename, ctl, obs, 0);
574 double *dtemp, *dpress, *dq;
577 ALLOC(dpress,
double,
584 gsl_rng *rng = gsl_rng_alloc(gsl_rng_default);
585 gsl_rng_set(rng, 0UL);
586 for (
int ib = 0; ib < batch_size; ib++) {
589 for (
int ig = 0; ig < ctl->
ng; ig++)
590 dq[ib *
NG + ig] = 1;
593 dtemp[ib] = 40.0 * (gsl_rng_uniform(rng) - 0.5);
594 dpress[ib] = 1.0 - 0.1 * gsl_rng_uniform(rng);
595 for (
int ig = 0; ig < ctl->
ng; ig++)
596 dq[ib *
NG + ig] = 0.8 + 0.4 * gsl_rng_uniform(rng);
601#pragma omp parallel for schedule(static)
602 for (
int ib = 0; ib < batch_size; ib++) {
603 atm_batch[ib] = *atm;
604 obs_batch[ib] = *obs;
605 for (
int ip = 0; ip < atm_batch[ib].
np; ip++) {
606 atm_batch[ib].
t[ip] += dtemp[ib];
607 atm_batch[ib].
p[ip] *= dpress[ib];
608 for (
int ig = 0; ig < ctl->
ng; ig++)
609 atm_batch[ib].q[ig][ip] *= dq[ib *
NG + ig];
632 double t_min = 0, t_max = 0, t_mean = 0, t_sd = 0;
636 gsl_rng *rng = gsl_rng_alloc(gsl_rng_default);
638 if (formod_scalar && batch_size > 1)
639 ERRMSG(
"BATCH_SIZE > 1 cannot be combined with EXECUTION scalar!");
642 atm_t *atm_batch = NULL;
643 obs_t *obs_batch = NULL;
645 los_t *los_batch = NULL;
646 obs_t *obs_scratch_batch = NULL;
647 if (batch_size > 1) {
657 formod_batch(ctl, tbl, atm_batch, obs_batch, batch_size, status,
658 los_batch, obs_scratch_batch);
662 const char *env_max_iter = getenv(
"JURASSIC_MAX_ITER");
663 const int max_iter = env_max_iter ? atoi(env_max_iter) : INT_MAX;
664 const char *env_budget = getenv(
"JURASSIC_TIME_BUDGET");
665 const double t_budget = env_budget ? atof(env_budget) : 10.0;
671 if (batch_size > 1) {
672 const double t0 = omp_get_wtime();
673 formod_batch(ctl, tbl, atm_batch, obs_batch, batch_size, status,
674 los_batch, obs_scratch_batch);
675 dt = omp_get_wtime() - t0;
676 for (
int ib = 0; ib < batch_size; ib++)
678 ERRMSG(
"Batch benchmark failed with status %d at element %d!",
682 const double dtemp = 40. * (gsl_rng_uniform(rng) - 0.5);
683 const double dpress = 1. - 0.1 * gsl_rng_uniform(rng);
685 for (
int ig = 0; ig < ctl->
ng; ig++)
686 dq[ig] = 0.8 + 0.4 * gsl_rng_uniform(rng);
687 for (
int ip = 0; ip < atm_scratch->
np; ip++) {
688 atm_scratch->
t[ip] += dtemp;
689 atm_scratch->
p[ip] *= dpress;
690 for (
int ig = 0; ig < ctl->
ng; ig++)
691 atm_scratch->
q[ig][ip] *= dq[ig];
694 const double t0 = omp_get_wtime();
696 obs_scratch, formod_scalar);
697 dt = omp_get_wtime() - t0;
702 if (n == 0 || dt < t_min)
704 if (n == 0 || dt > t_max)
708 }
while (t_mean < t_budget && n < max_iter);
711 if (batch_size > 1) {
713 free(obs_scratch_batch);
720 t_mean /= (double) n;
721 t_sd = sqrt(t_sd / (
double) n -
POW2(t_mean));
722 printf(
"RUNTIME: execution= %s | threads= %d | batch_size= %d | mean= %g s"
723 " | stddev= %g s | min= %g s | max= %g s | per_model= %g s"
724 " | throughput= %g models/s | samples= %d\n",
725 formod_scalar ?
"scalar" :
"batch", omp_get_max_threads(),
726 batch_size, t_mean, t_sd, t_min, t_max,
727 t_mean / batch_size, batch_size / t_mean, n);
752 for (
double dz = 0.01; dz <= 2; dz *= 1.1)
753 for (
double ds = 0.1; ds <= 50; ds *= 1.1) {
757 double t0 = omp_get_wtime();
759 obs_scratch, formod_scalar);
760 double dt = omp_get_wtime() - t0;
762 double mre[
ND], sdre[
ND], minre[
ND], maxre[
ND];
765 for (
int id = 0;
id < ctl->
nd;
id++)
767 (
"STEPSIZE: ds= %.4f km | dz= %g km | t= %g s | nu= %.4f cm^-1"
768 " | MeanRE= %g %% | SDRE= %g %% | MinRE= %g %% | MaxRE= %g %%\n",
769 ds, dz, dt, ctl->
nu[
id], mre[
id], sdre[
id], minre[
id], maxre[
id]);
787 static atm_t atm, atm2;
788 static obs_t obs, obs2, obs_ref, obs_scratch;
789 static los_t los_scratch;
793 read_atm(wrkdir, atmfile, ctl, &atm, 0);
797 read_obs(wrkdir, obsfile, ctl, &obs, 0);
800 if (task_mode ==
'p') {
802 &los_scratch, &obs_scratch, formod_scalar);
808 if (task_mode !=
't' && batch_size > 1)
809 ERRMSG(
"BATCH_SIZE > 1 is only supported for TASK=time!");
811 if (task_mode ==
't') {
819 write_obs(wrkdir, radfile, ctl, &obs, 0);
821 if (obsref[0] !=
'-') {
825 if (task_mode ==
'c') {
827 &los_scratch, &obs_scratch, formod_scalar);
830 if (task_mode ==
't') {
832 &obs_scratch, formod_scalar, batch_size);
835 if (task_mode ==
's') {
837 &obs_scratch, formod_scalar);
846 const obs_t *obs_test,
847 const obs_t *obs_ref,
854 for (
int id = 0;
id < ctl->
nd;
id++) {
856 double sum = 0, sum2 = 0;
862 for (
int ir = 0; ir < obs_test->
nr; ir++) {
865 if (!isfinite(obs_test->
rad[
id][ir])
866 || !isfinite(obs_ref->
rad[
id][ir])
867 || obs_ref->
rad[
id][ir] == 0)
871 double err = 100.0 * (obs_test->
rad[id][ir] - obs_ref->
rad[id][ir])
872 / obs_ref->
rad[
id][ir];
887 sdre[id] = sqrt(fmax(0, sum2 / n - mre[
id] * mre[
id]));
889 mre[id] = sdre[id] = minre[id] = maxre[id] = NAN;
897 const obs_t *obs_test,
898 const obs_t *obs_ref,
905 for (
int id = 0;
id < ctl->
nd;
id++) {
907 double sum = 0, sum2 = 0;
913 for (
int ir = 0; ir < obs_test->
nr; ir++) {
916 if (!isfinite(obs_test->
rad[
id][ir])
917 || !isfinite(obs_ref->
rad[
id][ir]))
921 double err = obs_test->
rad[id][ir] - obs_ref->
rad[id][ir];
935 meanae[id] = sum / n;
936 sdae[id] = sqrt(fmax(0, sum2 / n - meanae[
id] * meanae[
id]));
938 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.