MPTRAC
mptrac.c
Go to the documentation of this file.
1/*
2 This file is part of MPTRAC.
3
4 MPTRAC is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 MPTRAC is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with MPTRAC. If not, see <http://www.gnu.org/licenses/>.
16
17 Copyright (C) 2013-2026 Forschungszentrum Juelich GmbH
18*/
19
25#include "mptrac.h"
26
27#include <limits.h>
28
29#ifdef KPP
30#include "kpp_chem.h"
31#endif
32
34static gsl_rng *rng[NTHREADS];
35
37static uint64_t rng_ctr;
38
40#ifdef CURAND
41static curandGenerator_t rng_curand;
42#endif
43
44/*****************************************************************************/
45
46#ifdef MPI
48 void *data,
49 size_t N) {
50
51#define CHUNK_SIZE 2147483647
52
53 /* Broadcast the size of the data first... */
54 MPI_Bcast(&N, 1, MPI_UINT64_T, 0, MPI_COMM_WORLD);
55
56 /* Calculate the number of chunks... */
57 const size_t num_chunks = (N + CHUNK_SIZE - 1) / CHUNK_SIZE;
58
59 /* Loop over chunks... */
60 for (size_t i = 0; i < num_chunks; i++) {
61
62 /* Determine the start and end indices for the current chunk... */
63 const size_t start = i * CHUNK_SIZE;
64 const size_t end = (start + CHUNK_SIZE > N) ? N : start + CHUNK_SIZE;
65 const size_t chunk_size = end - start;
66
67 /* Broadcast the current chunk... */
68 MPI_Bcast((char *) data + start, (int) chunk_size, MPI_BYTE, 0,
69 MPI_COMM_WORLD);
70 }
71}
72#endif
73
74/*****************************************************************************/
75
77 const double *x,
78 double *z,
79 double *lon,
80 double *lat) {
81
82 const double radius = sqrt(DOTP(x, x));
83
84 *lat = RAD2DEG(asin(x[2] / radius));
85 *lon = RAD2DEG(atan2(x[1], x[0]));
86 *z = radius - RE;
87}
88
89/*****************************************************************************/
90
91double clim_oh(
92 const ctl_t *ctl,
93 const clim_t *clim,
94 const double t,
95 const double lon,
96 const double lat,
97 const double p) {
98
99 /* Set SZA threshold... */
100 const double sza_thresh = DEG2RAD(85.), csza_thresh = cos(sza_thresh);
101
102 /* Set reference coordinates... */
103 const double lat_ref =
104 ctl->met_coord_type == 0 ? lat : ctl->met_utm_ref_lat;
105 double lon_ref = ctl->met_coord_type == 0 ? lon : ctl->met_utm_ref_lon;
106 while (lon_ref < -180.0)
107 lon_ref += 360.0;
108 while (lon_ref >= 180.0)
109 lon_ref -= 360.0;
110
111 /* Get OH data from climatology... */
112 const double oh = clim_zm(&clim->oh, t, lat_ref, p);
113
114 /* Check beta... */
115 if (ctl->oh_chem_beta <= 0)
116 return oh;
117
118 /* Apply diurnal correction... */
119 const double csza = cos_sza(t, lon_ref, lat_ref);
120 const double denom = (csza >= csza_thresh) ? csza : csza_thresh;
121 return oh * exp(-ctl->oh_chem_beta / denom);
122}
123
124/*****************************************************************************/
125
127 const ctl_t *ctl,
128 clim_t *clim) {
129
130 /* Set SZA threshold... */
131 const double sza_thresh = DEG2RAD(85.), csza_thresh = cos(sza_thresh);
132
133 /* Loop over climatology data points... */
134 for (int it = 0; it < clim->oh.ntime; it++)
135 for (int iz = 0; iz < clim->oh.np; iz++)
136 for (int iy = 0; iy < clim->oh.nlat; iy++) {
137
138 /* Init... */
139 int n = 0;
140 double sum = 0;
141
142 /* Integrate day/night correction factor over longitude... */
143 for (double lon = -180; lon < 180; lon += 1.0) {
144 const double csza =
145 cos_sza(clim->oh.time[it], lon, clim->oh.lat[iy]);
146 const double denom = (csza >= csza_thresh) ? csza : csza_thresh;
147 sum += exp(-ctl->oh_chem_beta / denom);
148 n++;
149 }
150
151 /* Apply scaling factor to OH data... */
152 clim->oh.vmr[it][iz][iy] /= (sum / (double) n);
153 }
154}
155
156/*****************************************************************************/
157
159 const double rate[CP][CSZA][CO3],
160 const clim_photo_t *photo,
161 const double p,
162 const double sza,
163 const double o3c) {
164
165 /* Check pressure range... */
166 double p_help = p;
167 if (p < photo->p[photo->np - 1])
168 p_help = photo->p[photo->np - 1];
169 else if (p > photo->p[0])
170 p_help = photo->p[0];
171
172 /* Check sza range... */
173 double sza_help = sza;
174 if (sza < photo->sza[0])
175 sza_help = photo->sza[0];
176 else if (sza > photo->sza[photo->nsza - 1])
177 sza_help = photo->sza[photo->nsza - 1];
178
179 /* Check ozone column range... */
180 double o3c_help = o3c;
181 if (o3c < photo->o3c[0])
182 o3c_help = photo->o3c[0];
183 else if (o3c > photo->o3c[photo->no3c - 1])
184 o3c_help = photo->o3c[photo->no3c - 1];
185
186 /* Get indices... */
187 const int ip = locate_irr(photo->p, photo->np, p_help);
188 const int isza = locate_reg(photo->sza, photo->nsza, sza_help);
189 const int io3c = locate_reg(photo->o3c, photo->no3c, o3c_help);
190
191 /* Interpolate photolysis rate... */
192 const double aux00 = LIN(photo->p[ip], rate[ip][isza][io3c],
193 photo->p[ip + 1], rate[ip + 1][isza][io3c],
194 p_help);
195 const double aux01 = LIN(photo->p[ip], rate[ip][isza][io3c + 1],
196 photo->p[ip + 1], rate[ip + 1][isza][io3c + 1],
197 p_help);
198 const double aux10 = LIN(photo->p[ip], rate[ip][isza + 1][io3c],
199 photo->p[ip + 1], rate[ip + 1][isza + 1][io3c],
200 p_help);
201 const double aux11 = LIN(photo->p[ip], rate[ip][isza + 1][io3c + 1],
202 photo->p[ip + 1], rate[ip + 1][isza + 1][io3c + 1],
203 p_help);
204 const double aux0 =
205 LIN(photo->o3c[io3c], aux00, photo->o3c[io3c + 1], aux01, o3c_help);
206 const double aux1 =
207 LIN(photo->o3c[io3c], aux10, photo->o3c[io3c + 1], aux11, o3c_help);
208 const double aux =
209 LIN(photo->sza[isza], aux0, photo->sza[isza + 1], aux1, sza_help);
210 return MAX(aux, 0.0);
211}
212
213/*****************************************************************************/
214
216 const clim_t *clim,
217 const double t,
218 const double lat) {
219
220 /* Get seconds since begin of year... */
221 double sec = FMOD(t, 365.25 * 86400.);
222 while (sec < 0)
223 sec += 365.25 * 86400.;
224
225 /* Get indices... */
226 const int isec = locate_irr(clim->tropo_time, clim->tropo_ntime, sec);
227 const int ilat = locate_reg(clim->tropo_lat, clim->tropo_nlat, lat);
228
229 /* Interpolate tropopause pressure... */
230 const double p0 = LIN(clim->tropo_lat[ilat],
231 clim->tropo[isec][ilat],
232 clim->tropo_lat[ilat + 1],
233 clim->tropo[isec][ilat + 1], lat);
234 const double p1 = LIN(clim->tropo_lat[ilat],
235 clim->tropo[isec + 1][ilat],
236 clim->tropo_lat[ilat + 1],
237 clim->tropo[isec + 1][ilat + 1], lat);
238 return LIN(clim->tropo_time[isec], p0, clim->tropo_time[isec + 1], p1, sec);
239}
240
241/*****************************************************************************/
242
244 clim_t *clim) {
245
246 /* Write info... */
247 LOG(1, "Initialize tropopause data...");
248
249 /* Set time [s]... */
250 clim->tropo_ntime = 12;
251 double tropo_time[12] = {
252 1209600.00, 3888000.00, 6393600.00,
253 9072000.00, 11664000.00, 14342400.00,
254 16934400.00, 19612800.00, 22291200.00,
255 24883200.00, 27561600.00, 30153600.00
256 };
257 memcpy(clim->tropo_time, tropo_time, sizeof(clim->tropo_time));
258
259 /* Set latitudes [deg]... */
260 clim->tropo_nlat = 73;
261 const double tropo_lat[73] = {
262 -90, -87.5, -85, -82.5, -80, -77.5, -75, -72.5, -70, -67.5,
263 -65, -62.5, -60, -57.5, -55, -52.5, -50, -47.5, -45, -42.5,
264 -40, -37.5, -35, -32.5, -30, -27.5, -25, -22.5, -20, -17.5,
265 -15, -12.5, -10, -7.5, -5, -2.5, 0, 2.5, 5, 7.5, 10, 12.5,
266 15, 17.5, 20, 22.5, 25, 27.5, 30, 32.5, 35, 37.5, 40, 42.5,
267 45, 47.5, 50, 52.5, 55, 57.5, 60, 62.5, 65, 67.5, 70, 72.5,
268 75, 77.5, 80, 82.5, 85, 87.5, 90
269 };
270 memcpy(clim->tropo_lat, tropo_lat, sizeof(clim->tropo_lat));
271
272 /* Set tropopause pressure [hPa] (NCEP/NCAR Reanalysis 1)... */
273 const double tropo[12][73] = {
274 {324.1, 325.6, 325, 324.3, 322.5, 319.7, 314, 307.2, 301.8, 299.6,
275 297.1, 292.2, 285.6, 276.1, 264, 248.9, 231.9, 213.5, 194.4,
276 175.3, 157, 140.4, 126.7, 116.3, 109.5, 105.4, 103, 101.4, 100.4,
277 99.69, 99.19, 98.84, 98.56, 98.39, 98.39, 98.42, 98.44, 98.54,
278 98.68, 98.81, 98.89, 98.96, 99.12, 99.65, 101.4, 105.4, 113.5, 128,
279 152.1, 184.7, 214, 234.1, 247.3, 255.8, 262.6, 267.7, 271.7, 275,
280 277.2, 279, 280.1, 280.4, 280.6, 280.1, 279.3, 278.3, 276.8, 275.8,
281 275.3, 275.6, 275.4, 274.1, 273.5},
282 {337.3, 338.7, 337.8, 336.4, 333, 328.8, 321.1, 312.6, 306.6, 303.7,
283 300.2, 293.8, 285.4, 273.8, 259.6, 242.7, 224.4, 205.2, 186, 167.5,
284 150.3, 135, 122.8, 113.9, 108.2, 104.7, 102.5, 101.1, 100.2, 99.42,
285 98.88, 98.52, 98.25, 98.09, 98.07, 98.1, 98.12, 98.2, 98.25, 98.27,
286 98.26, 98.27, 98.36, 98.79, 100.2, 104.2, 113.7, 131.2, 159.5, 193,
287 220.4, 238.1, 250.2, 258.1, 264.7, 269.7, 273.7, 277.3, 280.2, 282.8,
288 284.9, 286.5, 288.1, 288.8, 289, 288.5, 287.2, 286.3, 286.1, 287.2,
289 287.5, 286.2, 285.8},
290 {335, 336, 335.7, 335.1, 332.3, 328.1, 320.6, 311.8, 305.1, 301.9,
291 297.6, 290, 280.4, 268.3, 254.6, 239.6, 223.9, 207.9, 192.2, 176.9,
292 161.7, 146.4, 132.2, 120.6, 112.3, 107.2, 104.3, 102.4, 101.3,
293 100.4, 99.86, 99.47, 99.16, 98.97, 98.94, 98.97, 99, 99.09, 99.2,
294 99.31, 99.35, 99.41, 99.51, 99.86, 101.1, 104.9, 114.3, 131, 156.8,
295 186.3, 209.3, 224.6, 236.8, 246.3, 254.9, 262.3, 268.8, 274.8,
296 279.9, 284.6, 288.6, 291.6, 294.9, 297.5, 299.8, 301.8, 303.1,
297 304.3, 304.9, 306, 306.6, 306.2, 306},
298 {306.2, 306.7, 305.7, 307.1, 307.3, 306.4, 301.8, 296.2, 292.4,
299 290.3, 287.1, 280.9, 273.4, 264.3, 254.1, 242.8, 231, 219, 207.2,
300 195.5, 183.3, 169.7, 154.7, 138.7, 124.1, 113.6, 107.8, 104.7,
301 102.8, 101.7, 100.9, 100.4, 100, 99.79, 99.7, 99.66, 99.68, 99.79,
302 99.94, 100.2, 100.5, 100.9, 101.4, 102.1, 103.4, 107, 115.2, 129.1,
303 148.7, 171, 190.8, 205.6, 218.4, 229.4, 239.6, 248.6, 256.5,
304 263.7, 270.3, 276.6, 282.6, 288.1, 294.5, 300.4, 306.3, 311.4,
305 315.1, 318.3, 320.3, 322.2, 322.8, 321.5, 321.1},
306 {266.5, 264.9, 260.8, 261, 262, 263, 261.3, 259.7, 259.2, 259.8,
307 260.1, 258.6, 256.7, 253.6, 249.5, 243.9, 237.4, 230, 222.1, 213.9,
308 205, 194.4, 180.4, 161.8, 140.7, 122.9, 112.1, 106.7, 104.1, 102.7,
309 101.8, 101.4, 101.1, 101, 101, 101, 101.1, 101.2, 101.5, 101.9,
310 102.4, 103, 103.8, 104.9, 106.8, 110.1, 115.6, 124, 135.2, 148.9,
311 165.2, 181.3, 198, 211.8, 223.5, 233.8, 242.9, 251.5, 259, 266.2,
312 273.1, 279.2, 286.2, 292.8, 299.6, 306, 311.1, 315.5, 318.8, 322.6,
313 325.3, 325.8, 325.8},
314 {220.1, 218.1, 210.8, 207.2, 207.6, 210.5, 211.4, 213.5, 217.3,
315 222.4, 227.9, 232.8, 237.4, 240.8, 242.8, 243, 241.5, 238.6, 234.2,
316 228.5, 221, 210.7, 195.1, 172.9, 147.8, 127.6, 115.6, 109.9, 107.1,
317 105.7, 105, 104.8, 104.8, 104.9, 105, 105.1, 105.3, 105.5, 105.8,
318 106.4, 107, 107.6, 108.1, 108.8, 110, 111.8, 114.2, 117.4, 121.6,
319 127.9, 137.3, 151.2, 169.5, 189, 205.8, 218.9, 229.1, 237.8, 245,
320 251.5, 257.1, 262.3, 268.2, 274, 280.4, 286.7, 292.4, 297.9, 302.9,
321 308.5, 312.2, 313.1, 313.3},
322 {187.4, 184.5, 173.3, 166.1, 165.4, 167.8, 169.6, 173.6, 179.6,
323 187.9, 198.9, 210, 220.5, 229.2, 235.7, 239.9, 241.8, 241.6, 239.6,
324 235.8, 229.4, 218.6, 200.9, 175.9, 149.4, 129.4, 118.3, 113.1,
325 110.8, 109.7, 109.3, 109.4, 109.7, 110, 110.2, 110.4, 110.5, 110.7,
326 111, 111.4, 111.8, 112.1, 112.3, 112.7, 113.2, 113.9, 115, 116.4,
327 117.9, 120.4, 124.1, 130.9, 142.2, 159.6, 179.6, 198.5, 212.9,
328 224.2, 232.7, 239.1, 243.8, 247.7, 252.4, 257.3, 263.2, 269.5,
329 275.4, 281.1, 286.3, 292, 296.3, 298.2, 298.8},
330 {166, 166.4, 155.7, 148.3, 147.1, 149, 152.1, 157, 163.6, 172.4,
331 185.3, 199.2, 212.6, 224, 233.2, 239.6, 243.3, 244.6, 243.6, 240.3,
332 233.9, 222.6, 203.7, 177, 149.5, 129.7, 119, 114, 111.7, 110.7,
333 110.3, 110.3, 110.6, 110.9, 111.1, 111.3, 111.5, 111.6, 111.9,
334 112.2, 112.5, 112.6, 112.8, 113, 113.4, 114, 115.1, 116.5, 118.3,
335 120.9, 124.4, 130.2, 139.4, 154.6, 173.8, 193.1, 208.1, 220.4,
336 230.1, 238.2, 244.7, 249.5, 254.5, 259.3, 264.5, 269.4, 273.7,
337 278.2, 282.6, 287.4, 290.9, 292.5, 293},
338 {171.9, 172.8, 166.2, 162.3, 161.4, 162.5, 165.2, 169.6, 175.3,
339 183.1, 193.8, 205.9, 218.3, 229.6, 238.5, 244.3, 246.9, 246.7,
340 243.8, 238.4, 230.2, 217.9, 199.6, 174.9, 148.9, 129.8, 119.5,
341 114.8, 112.3, 110.9, 110.3, 110.1, 110.2, 110.3, 110.4, 110.5,
342 110.6, 110.8, 111, 111.4, 111.8, 112, 112.2, 112.4, 112.9, 113.6,
343 114.7, 116.3, 118.4, 121.9, 127.1, 136.1, 149.8, 168.4, 186.9,
344 203.3, 217, 229.1, 238.7, 247, 254, 259.3, 264.3, 268.3, 272.5,
345 276.6, 280.4, 284.4, 288.4, 293.3, 297.2, 298.7, 299.1},
346 {191.6, 192.2, 189, 188.1, 190.2, 193.7, 197.8, 202.9, 208.5,
347 215.6, 224.2, 233.1, 241.2, 247.3, 250.8, 251.3, 248.9, 244.2,
348 237.3, 228.4, 217.2, 202.9, 184.5, 162.5, 140.7, 124.8, 116.2,
349 111.8, 109.4, 107.9, 107, 106.7, 106.6, 106.6, 106.7, 106.7,
350 106.8, 107, 107.4, 108, 108.7, 109.3, 109.8, 110.4, 111.2,
351 112.4, 114.2, 116.9, 121.1, 127.9, 139.3, 155.2, 173.6, 190.7,
352 206.1, 220.1, 232.3, 243, 251.8, 259.2, 265.7, 270.6, 275.3,
353 279.3, 283.3, 286.9, 289.7, 292.8, 296.1, 300.5, 303.9, 304.8,
354 305.1},
355 {241.5, 239.6, 236.8, 237.4, 239.4, 242.3, 244.2, 246.4, 249.2,
356 253.6, 258.6, 262.7, 264.8, 264.2, 260.6, 254.1, 245.5, 235.3,
357 223.9, 211.7, 198.3, 183.1, 165.6, 147.1, 130.5, 118.7, 111.9,
358 108.1, 105.8, 104.3, 103.4, 102.8, 102.5, 102.4, 102.5, 102.5,
359 102.5, 102.7, 103.1, 103.8, 104.6, 105.4, 106.1, 107, 108.2,
360 109.9, 112.8, 117.5, 126, 140.4, 161, 181.9, 201.2, 216.8, 230.4,
361 241.8, 251.4, 259.9, 266.9, 272.8, 277.4, 280.4, 282.9, 284.6,
362 286.1, 287.4, 288.3, 289.5, 290.9, 294.2, 296.9, 297.5, 297.6},
363 {301.2, 300.3, 296.6, 295.4, 295, 294.3, 291.2, 287.4, 284.9, 284.7,
364 284.1, 281.5, 277.1, 270.4, 261.7, 250.6, 237.6, 223.1, 207.9, 192,
365 175.8, 158.8, 142.1, 127.6, 116.8, 109.9, 106, 103.6, 102.1, 101.1,
366 100.4, 99.96, 99.6, 99.37, 99.32, 99.32, 99.31, 99.46, 99.77, 100.2,
367 100.7, 101.3, 101.8, 102.7, 104.1, 106.8, 111.9, 121, 136.7, 160,
368 186.9, 209.9, 228.1, 241.2, 251.5, 259.5, 265.7, 270.9, 274.8, 278,
369 280.3, 281.8, 283, 283.3, 283.7, 283.8, 283, 282.2, 281.2, 281.4,
370 281.7, 281.1, 281.2}
371 };
372 memcpy(clim->tropo, tropo, sizeof(clim->tropo));
373
374 /* Get range... */
375 double tropomin = 1e99, tropomax = -1e99;
376 for (int it = 0; it < clim->tropo_ntime; it++)
377 for (int iy = 0; iy < clim->tropo_nlat; iy++) {
378 tropomin = MIN(tropomin, clim->tropo[it][iy]);
379 tropomax = MAX(tropomax, clim->tropo[it][iy]);
380 }
381
382 /* Write info... */
383 LOG(2, "Number of time steps: %d", clim->tropo_ntime);
384 LOG(2, "Time steps: %.2f, %.2f ... %.2f s",
385 clim->tropo_time[0], clim->tropo_time[1],
386 clim->tropo_time[clim->tropo_ntime - 1]);
387 LOG(2, "Number of latitudes: %d", clim->tropo_nlat);
388 LOG(2, "Latitudes: %g, %g ... %g deg",
389 clim->tropo_lat[0], clim->tropo_lat[1],
390 clim->tropo_lat[clim->tropo_nlat - 1]);
391 LOG(2, "Tropopause altitude range: %g ... %g hPa", Z(tropomax),
392 Z(tropomin));
393 LOG(2, "Tropopause pressure range: %g ... %g hPa", tropomin, tropomax);
394}
395
396/*****************************************************************************/
397
398double clim_ts(
399 const clim_ts_t *ts,
400 const double t) {
401
402 /* Interpolate... */
403 if (t <= ts->time[0])
404 return ts->vmr[0];
405 else if (t >= ts->time[ts->ntime - 1])
406 return ts->vmr[ts->ntime - 1];
407 else {
408 const int idx = locate_irr(ts->time, ts->ntime, t);
409 return LIN(ts->time[idx], ts->vmr[idx],
410 ts->time[idx + 1], ts->vmr[idx + 1], t);
411 }
412}
413
414/*****************************************************************************/
415
416double clim_zm(
417 const clim_zm_t *zm,
418 const double t,
419 const double lat,
420 const double p) {
421
422 /* Get seconds since begin of year... */
423 double sec = FMOD(t, 365.25 * 86400.);
424 while (sec < 0)
425 sec += 365.25 * 86400.;
426
427 /* Check pressure range... */
428 double p_help = p;
429 if (p < zm->p[zm->np - 1])
430 p_help = zm->p[zm->np - 1];
431 else if (p > zm->p[0])
432 p_help = zm->p[0];
433
434 /* Check latitude range... */
435 double lat_help = lat;
436 if (lat < zm->lat[0])
437 lat_help = zm->lat[0];
438 else if (lat > zm->lat[zm->nlat - 1])
439 lat_help = zm->lat[zm->nlat - 1];
440
441 /* Get indices... */
442 const int isec = locate_irr(zm->time, zm->ntime, sec);
443 const int ilat = locate_reg(zm->lat, zm->nlat, lat_help);
444 const int ip = locate_irr(zm->p, zm->np, p_help);
445
446 /* Interpolate climatology data... */
447 const double aux00 = LIN(zm->p[ip], zm->vmr[isec][ip][ilat],
448 zm->p[ip + 1], zm->vmr[isec][ip + 1][ilat],
449 p_help);
450 const double aux01 = LIN(zm->p[ip], zm->vmr[isec][ip][ilat + 1],
451 zm->p[ip + 1], zm->vmr[isec][ip + 1][ilat + 1],
452 p_help);
453 const double aux10 = LIN(zm->p[ip], zm->vmr[isec + 1][ip][ilat],
454 zm->p[ip + 1], zm->vmr[isec + 1][ip + 1][ilat],
455 p_help);
456 const double aux11 = LIN(zm->p[ip], zm->vmr[isec + 1][ip][ilat + 1],
457 zm->p[ip + 1], zm->vmr[isec + 1][ip + 1][ilat + 1],
458 p_help);
459 const double aux0 =
460 LIN(zm->lat[ilat], aux00, zm->lat[ilat + 1], aux01, lat_help);
461 const double aux1 =
462 LIN(zm->lat[ilat], aux10, zm->lat[ilat + 1], aux11, lat_help);
463 const double aux = LIN(zm->time[isec], aux0, zm->time[isec + 1], aux1, sec);
464 return MAX(aux, 0.0);
465}
466
467/*****************************************************************************/
468
469#ifdef CMS
470void compress_cms(
471 const ctl_t *ctl,
472 const met_t *met,
473 const char *varname,
474 float *array,
475 const int decompress,
476 FILE *level_log,
477 FILE *inout) {
478
479 const size_t nx = (size_t) met->nx;
480 const size_t ny = (size_t) met->ny;
481 const size_t np = (size_t) met->np;
482 const size_t nxy = nx * ny;
483
484 /* Get meteo variable... */
485 const int metvar = compress_metvar_index(varname);
486
487 /* Set lon-lat grid... */
488 double lon[EX], lat[EY];
489 for (size_t ix = 0; ix < nx; ix++)
490 lon[ix] = 360. * (double) ix / ((double) nx - 1.);
491 for (size_t iy = 0; iy < ny; iy++)
492 lat[iy] = 90. - 180. * (double) iy / ((double) ny - 1.);
493
494 /* Set multiscale parameters... */
495 const char domain[] = "[0.0, 360.0]x[-90.0, 90.0]";
496 const int Nd0_x = ctl->met_cms_nd0x;
497 const int Nd0_y = ctl->met_cms_nd0y;
498 const int max_level_grid = ctl->met_cms_maxlev;
499 cms_param_t *cms_param
500 = cms_set_parameters(nx, ny, max_level_grid, Nd0_x, Nd0_y, domain);
501
502 /* Init... */
503 double cr = 0, t_coars = 0, t_eval = 0;
504
505 /* Read compressed stream and decompress array... */
506 if (decompress) {
507 t_eval = 0;
508
509 /* Loop over levels... */
510 for (size_t ip = 0; ip < np; ip++) {
511
512 /* Initialize multiscale module... */
513 cms_module_t *cms_ptr = cms_init(cms_param);
514
515 /* Read binary data... */
516 cms_sol_t *cms_sol;
517 if (ctl->met_cms_zstd == 1)
518 cms_sol = cms_read_zstd_sol(cms_ptr, inout);
519 else
520 cms_sol = cms_read_sol(cms_ptr, inout);
521
522 /* Evaluate current level... */
523 const double t0 = omp_get_wtime();
524#pragma omp parallel for collapse(2) default(shared)
525 for (size_t ix = 0; ix < nx; ix++)
526 for (size_t iy = 0; iy < ny; iy++) {
527 double val;
528 const double x[] = { lon[ix], lat[iy] };
529 cms_eval(cms_ptr, cms_sol, x, &val);
530 array[ARRAY_3D(ix, iy, ny, ip, np)] = (float) val;
531 }
532 t_eval += omp_get_wtime() - t0;
533
534 /* Calculate harmonic mean of compression rates... */
535 cr += 1.0 / cms_compression_rate(cms_ptr, cms_sol);
536
537 /* Free... */
538 cms_delete_sol(cms_sol);
539 cms_delete_module(cms_ptr);
540 }
541
542 /* Write info... */
543 LOG(2, "Read 3-D variable: %s"
544 " (CMS, RATIO=%g, BPV=%g, T_DECOMP=%g s, V_DECOMP=%g MiB/s)",
545 varname, (double) np / cr, 32. * cr / (double) np, t_eval,
546 COMPRESS_SPEED(nx * ny * np * sizeof(float), t_eval));
547 }
548
549 /* Compress array and output compressed stream... */
550 else {
551
552 /* Init... */
553 cms_module_t *cms_ptr[EP];
554 cms_sol_t *cms_sol[EP];
555 double ratio[EP], t_coars_level[EP], t_eval_level[EP];
556
557 /* Loop over batches... */
558 const size_t dip = (ctl->met_cms_batch <= 0
559 ? (size_t) omp_get_max_threads()
560 : (size_t) ctl->met_cms_batch);
561 for (size_t ip0 = 0; ip0 < np; ip0 += dip) {
562
563 /* Measure time... */
564 double t0 = omp_get_wtime();
565
566 /* Loop over levels... */
567#pragma omp parallel for default(shared)
568 for (size_t ip = ip0; ip < MIN(ip0 + dip, np); ip++) {
569 const double t1 = omp_get_wtime();
570
571 /* Allocate... */
572 float *tmp_arr;
573 ALLOC(tmp_arr, float,
574 nxy);
575
576 /* Copy level data... */
577 for (size_t ix = 0; ix < nx; ++ix)
578 for (size_t iy = 0; iy < ny; ++iy)
579 tmp_arr[ARRAY_2D(ix, iy, ny)] =
580 array[ARRAY_3D(ix, iy, ny, ip, np)];
581
582 /* Initialize current level... */
583 cms_ptr[ip] = cms_init(cms_param);
584
585 /* Coarsening... */
586 cms_sol[ip] =
587 cms_read_arr_new(cms_ptr[ip], tmp_arr, lon, lat,
588 nx, ny, ctl->met_cms_eps[metvar]);
589 t_coars_level[ip] = omp_get_wtime() - t1;
590
591 /* Free... */
592 free(tmp_arr);
593 }
594
595 /* Measure time... */
596 t_coars += (omp_get_wtime() - t0);
597
598 /* Loop over levels... */
599 for (size_t ip = ip0; ip < MIN(ip0 + dip, np); ip++) {
600
601 /* Allocate... */
602 float *tmp_cms, *tmp_org;
603 ALLOC(tmp_cms, float,
604 nxy);
605 ALLOC(tmp_org, float,
606 nxy);
607
608 /* Measure time... */
609 t0 = omp_get_wtime();
610
611 /* Evaluate... */
612#pragma omp parallel for collapse(2) default(shared)
613 for (size_t ix = 0; ix < nx; ix++)
614 for (size_t iy = 0; iy < ny; iy++) {
615 const size_t idx = ARRAY_2D(ix, iy, ny);
616 const double x[] = { lon[ix], lat[iy] };
617 double val;
618 cms_eval(cms_ptr[ip], cms_sol[ip], x, &val);
619 tmp_cms[idx] = (float) val;
620 tmp_org[idx] = array[ARRAY_3D(ix, iy, ny, ip, np)];
621 }
622
623 /* Measure time... */
624 t_eval_level[ip] = omp_get_wtime() - t0;
625 t_eval += t_eval_level[ip];
626
627 /* Calculate harmonic mean of compression rates... */
628 ratio[ip] = cms_compression_rate(cms_ptr[ip], cms_sol[ip]);
629 cr += 1.0 / ratio[ip];
630
631 /* Write per-level diagnostics... */
632 if (level_log)
633 compress_log_level(level_log, "CMS", varname, ip, met->p[ip],
634 ratio[ip], 32. / ratio[ip],
635 t_coars_level[ip], t_eval_level[ip], nxy,
636 nxy * sizeof(float), tmp_org, tmp_cms);
637
638 /* Save binary data... */
639 if (ctl->met_cms_zstd == 1)
640 cms_save_zstd_sol(cms_sol[ip], inout, 3);
641 else
642 cms_save_sol(cms_sol[ip], inout);
643
644 /* Free... */
645 cms_delete_sol(cms_sol[ip]);
646 cms_delete_module(cms_ptr[ip]);
647 free(tmp_cms);
648 free(tmp_org);
649 }
650 }
651
652 /* Write info... */
653 LOG(2, "Write 3-D variable: %s"
654 " (CMS, RATIO=%g, BPV=%g, T_COMP=%g s, V_COMP=%g MiB/s,"
655 " T_DECOMP=%g s, V_DECOMP=%g MiB/s)",
656 varname, (double) np / cr, 32. * cr / (double) np, t_coars,
657 COMPRESS_SPEED(nx * ny * np * sizeof(float), t_coars), t_eval,
658 COMPRESS_SPEED(nx * ny * np * sizeof(float), t_eval));
659 }
660
661 /* Free... */
662 cms_delete_param(cms_param);
663}
664#endif
665
666/*****************************************************************************/
667
669 FILE *out,
670 const char *codec,
671 const char *varname,
672 const size_t lev,
673 const double plev,
674 const double ratio,
675 const double bpv,
676 const double t_comp,
677 const double t_decomp,
678 const size_t n,
679 const size_t nbytes,
680 const float *org,
681 const float *cmp) {
682
683 static FILE *last_out = NULL;
684 static char last_var[LEN] = "";
685
686 /* Calculate error statistics... */
687 double mean_err = 0.0, stddev_err = 0.0;
688 double nrmse = NAN, mean_orig = 0.0, orig_range = 0.0, rho = NAN;
689
690 double sum_err = 0.0, sumsq_err = 0.0;
691 double sum_orig = 0.0, sum_cmp = 0.0;
692 double sumsq_orig = 0.0, sumsq_cmp = 0.0, sum_org_cmp = 0.0;
693
694 double min_orig = 0.0, max_orig = 0.0;
695 double min_err = 0.0, max_err = 0.0;
696
697 /* Accumulate error, signal and cross-product sums in one pass... */
698 for (size_t i = 0; i < n; i++) {
699 const double o = (double) org[i];
700 const double c = (double) cmp[i];
701 const double e = c - o;
702
703 sum_err += e;
704 sumsq_err += e * e;
705 sum_orig += o;
706 sum_cmp += c;
707
708 sumsq_orig += o * o;
709 sumsq_cmp += c * c;
710 sum_org_cmp += o * c;
711
712 if (e < min_err || i == 0)
713 min_err = e;
714 if (e > max_err || i == 0)
715 max_err = e;
716
717 if (o < min_orig || i == 0)
718 min_orig = o;
719 if (o > max_orig || i == 0)
720 max_orig = o;
721 }
722
723 /* Derive the basic error and signal summary statistics... */
724 mean_err = sum_err / (double) n;
725 mean_orig = sum_orig / (double) n;
726 orig_range = max_orig - min_orig;
727
728 /* Convert the accumulated error sums into a standard deviation... */
729 const double var_err =
730 (sumsq_err - (sum_err * sum_err) / (double) n) / (double) n;
731 stddev_err = sqrt(var_err > 0.0 ? var_err : 0.0);
732
733 /* Normalize RMSE by the original data range if available... */
734 nrmse = (orig_range > 0.0)
735 ? sqrt(sumsq_err / (double) n) / orig_range : NAN;
736
737 /* Compute the Pearson correlation from the accumulated moments... */
738 const double nn = (double) n;
739 const double cov = sum_org_cmp - sum_orig * sum_cmp / nn;
740 const double var_orig = sumsq_orig - sum_orig * sum_orig / nn;
741 const double var_cmp = sumsq_cmp - sum_cmp * sum_cmp / nn;
742 const double denom = sqrt(var_orig * var_cmp);
743 rho = denom > 0.0 ? cov / denom : NAN;
744
745 /* Reset the variable separator state when the target file changes... */
746 if (out != last_out) {
747 last_out = out;
748 last_var[0] = '\0';
749 }
750
751 /* Separate diagnostic blocks when switching to a new variable... */
752 if (last_var[0] != '\0' && strcmp(last_var, varname) != 0)
753 fprintf(out, "\n");
754 snprintf(last_var, LEN, "%s", varname);
755
756 /* Logging... */
757 fprintf(out,
758 "%s %s %lu %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g\n",
759 codec, varname, (unsigned long) lev, plev, ratio, bpv, rho,
760 mean_err, stddev_err, min_err, max_err, mean_orig, orig_range,
761 nrmse, t_comp, COMPRESS_SPEED(nbytes, t_comp), t_decomp,
762 COMPRESS_SPEED(nbytes, t_decomp));
763}
764
765/*****************************************************************************/
766
768 FILE *out,
769 const char *codec,
770 const char *varname,
771 const met_t *met,
772 const float *org_all,
773 const float *cmp_all,
774 const size_t nxy,
775 const size_t nz,
776 const double ratio,
777 const double bpv,
778 const double t_comp,
779 const double t_decomp,
780 const size_t nbytes) {
781
782 if (!out)
783 return;
784
785 /* Allocate temporary buffers for one level... */
786 float *tmp_org, *tmp_cmp;
787 ALLOC(tmp_org, float,
788 nxy);
789 ALLOC(tmp_cmp, float,
790 nxy);
791
792 for (size_t lev = 0; lev < nz; lev++) {
793
794 /* Extract current level... */
795#pragma omp parallel for default(shared)
796 for (size_t ixy = 0; ixy < nxy; ixy++) {
797 tmp_org[ixy] = org_all[ixy * nz + lev];
798 tmp_cmp[ixy] = cmp_all[ixy * nz + lev];
799 }
800
801 /* Write diagnostics for the extracted level... */
802 compress_log_level(out, codec, varname, lev, met->p[lev], ratio, bpv,
803 t_comp, t_decomp, nxy, nbytes, tmp_org, tmp_cmp);
804 }
805
806 /* Free... */
807 free(tmp_org);
808 free(tmp_cmp);
809}
810
811/*****************************************************************************/
812
814 float *array,
815 const size_t nxy,
816 const size_t nz,
817 double *off,
818 double *scl) {
819
820 /* Initialize per-level minima and maxima from the first sample... */
821 for (size_t iz = 0; iz < nz; iz++) {
822 off[iz] = array[iz];
823 scl[iz] = array[iz];
824 }
825
826 /* Scan the remaining samples to get per-level minima and maxima... */
827 for (size_t ixy = 1; ixy < nxy; ixy++)
828 for (size_t iz = 0; iz < nz; iz++) {
829 const double value = array[ixy * nz + iz];
830 if (value < off[iz])
831 off[iz] = value;
832 if (value > scl[iz])
833 scl[iz] = value;
834 }
835
836 /* Convert maxima into ranges and suppress numerically flat levels... */
837 for (size_t iz = 0; iz < nz; iz++) {
838 const double range = scl[iz] - off[iz];
839 const double ref = fmax(1.0, fmax(fabs(off[iz]), fabs(scl[iz])));
840 scl[iz] = !(range > 1e-12 * ref) ? 0.0 : range;
841 }
842
843 /* Scale each level independently to the unit interval... */
844#pragma omp parallel for default(shared)
845 for (size_t ixy = 0; ixy < nxy; ixy++)
846 for (size_t iz = 0; iz < nz; iz++)
847 if (scl[iz] > 0.0)
848 array[ixy * nz + iz] =
849 (float) ((array[ixy * nz + iz] - off[iz]) / scl[iz]);
850 else
851 array[ixy * nz + iz] = 0.0f;
852}
853
854/*****************************************************************************/
855
857 float *array,
858 const size_t nxy,
859 const size_t nz,
860 const double *off,
861 const double *scl) {
862
863 /* Restore each level from unit space back to its original range... */
864#pragma omp parallel for default(shared)
865 for (size_t ixy = 0; ixy < nxy; ixy++)
866 for (size_t iz = 0; iz < nz; iz++)
867 if (scl[iz] > 0.0)
868 array[ixy * nz + iz] =
869 (float) (array[ixy * nz + iz] * scl[iz] + off[iz]);
870 else
871 array[ixy * nz + iz] = (float) off[iz];
872}
873
874/*****************************************************************************/
875
877 FILE *in,
878 const size_t nz,
879 double **off,
880 double **scl) {
881
882 int enabled;
883 *off = NULL;
884 *scl = NULL;
885
886 /* Read input... */
887 FREAD(&enabled, int,
888 1,
889 in);
890 if (enabled < 0 || enabled > 1)
891 ERRMSG("Invalid stored MET_LOSSY_SCALE flag!");
892
893 if (enabled > 0) {
894 ALLOC(*off, double,
895 nz);
896 ALLOC(*scl, double,
897 nz);
898 FREAD(*off, double,
899 nz,
900 in);
901 FREAD(*scl, double,
902 nz,
903 in);
904 }
905
906 return enabled;
907}
908
909/*****************************************************************************/
910
912 FILE *out,
913 const int enabled,
914 float *array,
915 const size_t nxy,
916 const size_t nz,
917 double **off,
918 double **scl) {
919
920 *off = NULL;
921 *scl = NULL;
922
923 /* Write output... */
924 FWRITE(&enabled, int,
925 1,
926 out);
927 if (enabled <= 0)
928 return;
929
930 /* Derive per-level scaling data... */
931 ALLOC(*off, double,
932 nz);
933 ALLOC(*scl, double,
934 nz);
935
936 compress_scale_to_unit(array, nxy, nz, *off, *scl);
937
938 FWRITE(*off, double,
939 nz,
940 out);
941 FWRITE(*scl, double,
942 nz,
943 out);
944}
945
946/*****************************************************************************/
947
948#ifdef ZSTD
949ZSTD_CCtx *compress_zstd_create_cctx(
950 const int level,
951 const int nworkers) {
952
953 /* Create and configure the ZSTD context... */
954 ZSTD_CCtx *cctx = ZSTD_createCCtx();
955 if (!cctx)
956 ERRMSG("Cannot create ZSTD context!");
957
958 if (ZSTD_isError(ZSTD_CCtx_setParameter(cctx, ZSTD_c_compressionLevel,
959 level)))
960 ERRMSG("Cannot set ZSTD compression level!");
961
962 if (ZSTD_isError(ZSTD_CCtx_setParameter(cctx, ZSTD_c_nbWorkers, nworkers)))
963 ERRMSG("Cannot set ZSTD worker count!");
964
965 return cctx;
966}
967#endif
968
969/*****************************************************************************/
970
972 const char *varname) {
973
974 static const char *const names[] = {
975 "Z", "T", "U", "V", "W", "PV", "H2O", "O3",
976 "LWC", "RWC", "IWC", "SWC", "CC"
977 };
978
979 for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); i++)
980 if (strcasecmp(varname, names[i]) == 0)
981 return (int) i;
982
983 ERRMSG("Unknown meteorological variable name!");
984}
985
986/*****************************************************************************/
987
989 const ctl_t *ctl,
990 const met_t *met,
991 const char *varname,
992 float *array,
993 const int decompress,
994 FILE *level_log,
995 FILE *inout) {
996
997 const size_t nxy = (size_t) met->nx * (size_t) met->ny;
998 const size_t nz = (size_t) met->np;
999 const size_t n = nxy * nz;
1000 const size_t nbytes = n * sizeof(float);
1001 const size_t pck_bytes = n * sizeof(uint16_t);
1002
1003 double vmin[EP], vmax[EP], off[EP], scl[EP];
1004 uint16_t *sarray;
1005
1006 /* PCK+ZSTD payload = scale + offset + packed 16-bit samples... */
1007 const size_t payload_len = 2 * nz * sizeof(double) + pck_bytes;
1008 size_t stored_len = payload_len;
1009 const char *codec_info = ctl->met_pck_zstd ? "PCK+ZSTD" : "PCK";
1010
1011 /* Allocate packed sample buffer... */
1012 ALLOC(sarray, uint16_t, n);
1013
1014 /* Read compressed stream and decompress array... */
1015 if (decompress) {
1016 double t_zstd = 0.0;
1017
1018 /* Read packed data... */
1019 if (ctl->met_pck_zstd) {
1020#ifdef ZSTD
1021 uint64_t magic, pck_zstd_magic = UINT64_C(0x50434b5a53544431);
1022 unsigned char *compr = NULL, *payload = NULL;
1023
1024 /* Read input... */
1025 FREAD(&magic, uint64_t, 1, inout);
1026 if (magic != pck_zstd_magic)
1027 ERRMSG("PCK+ZSTD magic mismatch! Check MET_PCK_ZSTD and file type.");
1028
1029 /* Read and decompress the outer ZSTD payload... */
1030 FREAD(&stored_len, size_t,
1031 1,
1032 inout);
1033 ALLOC(compr, unsigned char,
1034 stored_len);
1035 FREAD(compr, unsigned char,
1036 stored_len,
1037 inout);
1038 ALLOC(payload, unsigned char,
1039 payload_len);
1040 const double t0_decomp = omp_get_wtime();
1041 const size_t out_len =
1042 ZSTD_decompress(payload, payload_len, compr, stored_len);
1043 t_zstd = omp_get_wtime() - t0_decomp;
1044 if (ZSTD_isError(out_len) || out_len != payload_len)
1045 ERRMSG("PCK ZSTD decompression failed or size mismatch!");
1046
1047 /* Split payload into scale, offset and packed samples... */
1048 memcpy(scl, payload, nz * sizeof(double));
1049 memcpy(off, payload + nz * sizeof(double), nz * sizeof(double));
1050 memcpy(sarray, payload + 2 * nz * sizeof(double), pck_bytes);
1051
1052 /* Free... */
1053 free(payload);
1054 free(compr);
1055#else
1056 ERRMSG("MPTRAC was compiled without ZSTD compression!");
1057#endif
1058 } else {
1059 /* Read input... */
1060 FREAD(&scl, double,
1061 nz,
1062 inout);
1063 FREAD(&off, double,
1064 nz,
1065 inout);
1066 FREAD(sarray, uint16_t, n, inout);
1067 }
1068
1069 /* Measure the scalar unpacking step separately from optional ZSTD... */
1070 const double t0 = omp_get_wtime();
1071
1072 /* Convert packed 16-bit samples back to floating-point values... */
1073#pragma omp parallel for default(shared)
1074 for (size_t ixy = 0; ixy < nxy; ixy++)
1075 for (size_t iz = 0; iz < nz; iz++)
1076 array[ixy * nz + iz]
1077 = (float) (sarray[ixy * nz + iz] * scl[iz] + off[iz]);
1078
1079 /* Add optional ZSTD time to the scalar unpacking time... */
1080 const double t_decomp = t_zstd + omp_get_wtime() - t0;
1081
1082 /* Logging... */
1083 LOG(2, "Read 3-D variable: %s"
1084 " (%s, RATIO=%g, BPV=%g, T_DECOMP=%g s, V_DECOMP=%g MiB/s)",
1085 varname, codec_info, COMPRESS_RATIO(nbytes, stored_len),
1086 COMPRESS_BPV(n, stored_len), t_decomp, COMPRESS_SPEED(nbytes,
1087 t_decomp));
1088 }
1089
1090 /* Compress array and output compressed stream... */
1091 else {
1092 float *tmp_org, *tmp_pck;
1093 double t_comp_sum = 0, t_decomp_sum = 0, t_zstd = 0, t_zstd_decomp = 0;
1094 double t_comp_level[EP], t_decomp_level[EP];
1095
1096 /* Allocate temporary buffers for one level... */
1097 ALLOC(tmp_org, float,
1098 nxy);
1099 ALLOC(tmp_pck, float,
1100 nxy);
1101
1102 /* Derive per-level minima and maxima for the 16-bit quantization... */
1103 for (size_t iz = 0; iz < nz; iz++) {
1104 vmin[iz] = array[iz];
1105 vmax[iz] = array[iz];
1106 }
1107 for (size_t ixy = 1; ixy < nxy; ixy++)
1108 for (size_t iz = 0; iz < nz; iz++) {
1109 if (array[ixy * nz + iz] < vmin[iz])
1110 vmin[iz] = array[ixy * nz + iz];
1111 if (array[ixy * nz + iz] > vmax[iz])
1112 vmax[iz] = array[ixy * nz + iz];
1113 }
1114
1115 /* Derive per-level offset and scaling factor... */
1116 for (size_t iz = 0; iz < nz; iz++) {
1117 scl[iz] = (vmax[iz] - vmin[iz]) / 65533.;
1118 off[iz] = vmin[iz];
1119 }
1120
1121 /* Quantize each level and time encode/decode diagnostics on the fly... */
1122 for (size_t iz = 0; iz < nz; iz++) {
1123 const double t0 = omp_get_wtime();
1124
1125#pragma omp parallel for default(shared)
1126 for (size_t ixy = 0; ixy < nxy; ixy++)
1127 if (scl[iz] != 0)
1128 sarray[ixy * nz + iz] =
1129 (uint16_t) ((array[ixy * nz + iz] - off[iz]) / scl[iz] + .5);
1130 else
1131 sarray[ixy * nz + iz] = 0;
1132
1133 t_comp_level[iz] = omp_get_wtime() - t0;
1134 t_comp_sum += t_comp_level[iz];
1135
1136 const double t1 = omp_get_wtime();
1137
1138#pragma omp parallel for default(shared)
1139 for (size_t ixy = 0; ixy < nxy; ixy++) {
1140 tmp_org[ixy] = array[ixy * nz + iz];
1141 tmp_pck[ixy] = (float) (sarray[ixy * nz + iz] * scl[iz] + off[iz]);
1142 }
1143
1144 t_decomp_level[iz] = omp_get_wtime() - t1;
1145 t_decomp_sum += t_decomp_level[iz];
1146 }
1147
1148 /* Write packed data, optionally wrapped in ZSTD... */
1149 if (ctl->met_pck_zstd) {
1150#ifdef ZSTD
1151 uint64_t pck_zstd_magic = UINT64_C(0x50434b5a53544431);
1152 unsigned char *payload = NULL, *payload_chk = NULL;
1153 void *stored_data = NULL;
1154
1155 /* Pack plain PCK data into one ZSTD payload... */
1156 ALLOC(payload, unsigned char,
1157 payload_len);
1158 memcpy(payload, scl, nz * sizeof(double));
1159 memcpy(payload + nz * sizeof(double), off, nz * sizeof(double));
1160 memcpy(payload + 2 * nz * sizeof(double), sarray, pck_bytes);
1161 const size_t dst_cap = ZSTD_compressBound(payload_len);
1162 ALLOC(stored_data, char,
1163 dst_cap);
1164 ZSTD_CCtx *cctx = compress_zstd_create_cctx(ctl->met_zstd_level,
1165 ctl->met_zstd_nworkers);
1166 const double t0_comp = omp_get_wtime();
1167 stored_len =
1168 ZSTD_compress2(cctx, stored_data, dst_cap, payload, payload_len);
1169 t_zstd = omp_get_wtime() - t0_comp;
1170 ZSTD_freeCCtx(cctx);
1171 if (ZSTD_isError(stored_len))
1172 ERRMSG("PCK ZSTD compression failed!");
1173
1174 /* Decompress once for validation and timing... */
1175 ALLOC(payload_chk, unsigned char,
1176 payload_len);
1177 const double t0_decomp2 = omp_get_wtime();
1178 const size_t out_len2 =
1179 ZSTD_decompress(payload_chk, payload_len, stored_data, stored_len);
1180 t_zstd_decomp = omp_get_wtime() - t0_decomp2;
1181 if (ZSTD_isError(out_len2) || out_len2 != payload_len)
1182 ERRMSG("PCK ZSTD decompression failed or size mismatch!");
1183
1184 /* Free... */
1185 free(payload_chk);
1186 free(payload);
1187
1188 /* Write output... */
1189 FWRITE(&pck_zstd_magic, uint64_t, 1, inout);
1190 FWRITE(&stored_len, size_t,
1191 1,
1192 inout);
1193 FWRITE(stored_data, unsigned char,
1194 stored_len,
1195 inout);
1196
1197 /* Free... */
1198 free(stored_data);
1199#else
1200 ERRMSG("MPTRAC was compiled without ZSTD compression!");
1201#endif
1202 } else {
1203
1204 /* Write output... */
1205 FWRITE(&scl, double,
1206 nz,
1207 inout);
1208 FWRITE(&off, double,
1209 nz,
1210 inout);
1211 FWRITE(sarray, uint16_t, n, inout);
1212 }
1213
1214 /* Combine PCK and optional ZSTD timing and size metrics... */
1215 const double ratio_out = COMPRESS_RATIO(nbytes, stored_len);
1216 const double bpv_out = COMPRESS_BPV(n, stored_len);
1217 const double t_comp = t_comp_sum + t_zstd;
1218 const double t_decomp = t_decomp_sum + t_zstd_decomp;
1219
1220 /* Logging... */
1221 LOG(2, "Write 3-D variable: %s"
1222 " (%s, RATIO=%g, BPV=%g, T_COMP=%g s, V_COMP=%g MiB/s,"
1223 " T_DECOMP=%g s, V_DECOMP=%g MiB/s)",
1224 varname, codec_info, ratio_out, bpv_out, t_comp,
1225 COMPRESS_SPEED(nbytes, t_comp), t_decomp,
1226 COMPRESS_SPEED(nbytes, t_decomp));
1227
1228 /* Distribute optional ZSTD cost across levels for diagnostics... */
1229 if (level_log) {
1230 const double t_zstd_level = t_zstd / (double) nz;
1231 const double t_zstd_decomp_level = t_zstd_decomp / (double) nz;
1232 const char *codec = ctl->met_pck_zstd ? "PCKZSTD" : "PCK";
1233 for (size_t iz = 0; iz < nz; iz++) {
1234#pragma omp parallel for default(shared)
1235
1236 /* Rebuild one decoded level for diagnostics... */
1237 for (size_t ixy = 0; ixy < nxy; ixy++) {
1238 tmp_org[ixy] = array[ixy * nz + iz];
1239 tmp_pck[ixy] = (float) (sarray[ixy * nz + iz] * scl[iz] + off[iz]);
1240 }
1241
1242 /* Logging... */
1243 compress_log_level(level_log, codec, varname, iz, met->p[iz],
1244 ratio_out, bpv_out,
1245 t_comp_level[iz] + t_zstd_level,
1246 t_decomp_level[iz] + t_zstd_decomp_level,
1247 nxy, nxy * sizeof(float), tmp_org, tmp_pck);
1248 }
1249 }
1250
1251 /* Free... */
1252 free(tmp_org);
1253 free(tmp_pck);
1254 }
1255
1256 /* Free... */
1257 free(sarray);
1258}
1259
1260/*****************************************************************************/
1261
1262#ifdef SZ3
1263void compress_sz3(
1264 const ctl_t *ctl,
1265 const met_t *met,
1266 const char *varname,
1267 float *array,
1268 const int decompress,
1269 FILE *level_log,
1270 FILE *inout) {
1271
1272 /* Get meteo variable... */
1273 const int metvar = compress_metvar_index(varname);
1274
1275 /* Check compression settings... */
1276 if ((ctl->met_sz3_prec[metvar] > 0) == (ctl->met_sz3_tol[metvar] > 0.0))
1277 ERRMSG("Exactly one of precision or tolerance must be set for SZ3!");
1278
1279 size_t r1 = (size_t) met->np, r2 = (size_t) met->ny, r3 = (size_t) met->nx,
1280 outSize = 0, total_elems = r1 * r2 * r3;
1281
1282 unsigned char *bytes = NULL;
1283 double *scale_off = NULL, *scale_scl = NULL;
1284 float *orig_all = NULL;
1285 char codec_info[LEN];
1286
1287 /* Read compressed stream and decompress array... */
1288 if (decompress) {
1289
1290 /* Read optional scaling metadata before the SZ3 byte stream... */
1291 const int stored_lossy_scale =
1292 compress_read_lossy_scale(inout, r1, &scale_off, &scale_scl);
1293
1294 size_t sz3size;
1295
1296 /* Read input... */
1297 FREAD(&sz3size, size_t,
1298 1,
1299 inout);
1300 ALLOC(bytes, char,
1301 sz3size);
1302 FREAD(bytes, unsigned char,
1303 sz3size,
1304 inout);
1305
1306 /* Let SZ3 rebuild the scaled field in its native 3-D layout... */
1307 const double t0 = omp_get_wtime();
1308 void *outData = SZ_decompress(SZ_FLOAT, bytes, sz3size, 0, 0, r3, r2, r1);
1309 if (!outData)
1310 ERRMSG("Decompression failed!");
1311
1312 memcpy(array, outData, total_elems * sizeof(float));
1313
1314 /* Restore the original value range after lossy unit scaling... */
1315 if (stored_lossy_scale > 0)
1316 compress_unscale_from_unit(array, r2 * r3, r1, scale_off, scale_scl);
1317 const double t_decomp = omp_get_wtime() - t0;
1318
1319 /* Free... */
1320 free(outData);
1321 free(bytes);
1322
1323 const double ratio = COMPRESS_RATIO(total_elems * sizeof(float), sz3size);
1324 const double bpv = COMPRESS_BPV(total_elems, sz3size);
1325 snprintf(codec_info, LEN, "SZ3, PREC=%d, TOL=%g, SCALE=%d",
1326 ctl->met_sz3_prec[metvar], ctl->met_sz3_tol[metvar],
1327 stored_lossy_scale);
1328
1329 /* Logging... */
1330 LOG(2, "Read 3-D variable: %s"
1331 " (%s, RATIO=%g, BPV=%g, T_DECOMP=%g s, V_DECOMP=%g MiB/s)",
1332 varname, codec_info, ratio, bpv, t_decomp,
1333 COMPRESS_SPEED(total_elems * sizeof(float), t_decomp));
1334 }
1335
1336 /* Compress array and output compressed stream... */
1337 else {
1338
1339 /* Translate MPTRAC settings into the SZ3 absolute/relative error mode... */
1340 const int errBoundMode = (ctl->met_sz3_prec[metvar] > 0) ? REL : ABS;
1341 const double absBound =
1342 (errBoundMode == ABS) ? ctl->met_sz3_tol[metvar] : 0.0;
1343 const double relBound =
1344 (errBoundMode == REL) ? pow(2.0, -(double) ctl->met_sz3_prec[metvar])
1345 : 0.0;
1346 const size_t raw_size = total_elems * sizeof(float);
1347
1348 /* Preserve the unscaled input only if diagnostics need it later... */
1349 if (ctl->met_lossy_scale[metvar] > 0 && level_log) {
1350 ALLOC(orig_all, float,
1351 total_elems);
1352 memcpy(orig_all, array, total_elems * sizeof(float));
1353 }
1354
1355 /* Optionally scale each vertical level to unit space before SZ3... */
1356 compress_write_lossy_scale(inout, ctl->met_lossy_scale[metvar], array,
1357 r2 * r3, r1, &scale_off, &scale_scl);
1358
1359 /* Compress the possibly scaled field with the selected SZ3 error mode... */
1360 const double t0 = omp_get_wtime();
1361 bytes = SZ_compress_args(SZ_FLOAT, array, &outSize,
1362 errBoundMode, absBound, relBound, 0.0,
1363 0, 0, r3, r2, r1);
1364 const double t_comp = omp_get_wtime() - t0;
1365 if (!bytes || outSize == 0)
1366 ERRMSG("Compression failed!");
1367
1368 /* Write output... */
1369 FWRITE(&outSize, size_t,
1370 1,
1371 inout);
1372 FWRITE(bytes, unsigned char,
1373 outSize,
1374 inout);
1375
1376 snprintf(codec_info, LEN, "SZ3, PREC=%d, TOL=%g, SCALE=%d",
1377 ctl->met_sz3_prec[metvar], ctl->met_sz3_tol[metvar],
1378 ctl->met_lossy_scale[metvar]);
1379
1380 /* Reconstruct once more only when per-level diagnostics are requested... */
1381 double t_decomp = NAN;
1382 if (level_log) {
1383 unsigned char *bytes_copy;
1384 float *tmp_all;
1385 const size_t nxy = r2 * r3;
1386
1387 /* Reconstruct data for per-level diagnostics... */
1388 ALLOC(bytes_copy, unsigned char,
1389 outSize);
1390 memcpy(bytes_copy, bytes, outSize);
1391
1392 const double t1 = omp_get_wtime();
1393 void *decData = SZ_decompress(SZ_FLOAT, bytes_copy, outSize, 0, 0, r3,
1394 r2, r1);
1395 t_decomp = omp_get_wtime() - t1;
1396 if (!decData)
1397 ERRMSG("Decompression failed!");
1398
1399 /* Rebuild the decoded field so diagnostics compare like with like... */
1400 ALLOC(tmp_all, float,
1401 total_elems);
1402 memcpy(tmp_all, decData, total_elems * sizeof(float));
1403 if (ctl->met_lossy_scale[metvar] > 0)
1404 compress_unscale_from_unit(tmp_all, r2 * r3, r1, scale_off,
1405 scale_scl);
1406
1407 const double ratio = COMPRESS_RATIO(raw_size, outSize);
1408 const double bpv = COMPRESS_BPV(total_elems, outSize);
1409 compress_log_levels_3d(level_log, "SZ3", varname, met,
1410 (orig_all ? orig_all : array), tmp_all, nxy, r1,
1411 ratio, bpv, t_comp, t_decomp, raw_size);
1412
1413 /* Free... */
1414 free(tmp_all);
1415 free(decData);
1416 free(bytes_copy);
1417
1418 /* Logging... */
1419 LOG(2, "Write 3-D variable: %s"
1420 " (%s, RATIO=%g, BPV=%g, T_COMP=%g s, V_COMP=%g MiB/s,"
1421 " T_DECOMP=%g s, V_DECOMP=%g MiB/s)",
1422 varname, codec_info, ratio, bpv, t_comp,
1423 COMPRESS_SPEED(raw_size, t_comp), t_decomp,
1424 COMPRESS_SPEED(raw_size, t_decomp));
1425 } else {
1426 const double ratio = COMPRESS_RATIO(raw_size, outSize);
1427 const double bpv = COMPRESS_BPV(total_elems, outSize);
1428
1429 /* Logging... */
1430 LOG(2, "Write 3-D variable: %s"
1431 " (%s, RATIO=%g, BPV=%g, T_COMP=%g s, V_COMP=%g MiB/s)",
1432 varname, codec_info, ratio, bpv, t_comp,
1433 COMPRESS_SPEED(raw_size, t_comp));
1434 }
1435 /* Free... */
1436 free(bytes);
1437 free(orig_all);
1438 }
1439
1440 /* Free... */
1441 free(scale_off);
1442 free(scale_scl);
1443}
1444#endif
1445
1446/*****************************************************************************/
1447
1448#ifdef ZFP
1449void compress_zfp(
1450 const ctl_t *ctl,
1451 const met_t *met,
1452 const char *varname,
1453 float *array,
1454 const int decompress,
1455 FILE *level_log,
1456 FILE *inout) {
1457
1458 /* Get meteo variable... */
1459 const int metvar = compress_metvar_index(varname);
1460
1461 /* Allocate meta data for the 3D array a[nz][ny][nx]... */
1462 const size_t snx = (size_t) met->np;
1463 const size_t sny = (size_t) met->ny;
1464 const size_t snz = (size_t) met->nx;
1465 const size_t n = snx * sny * snz;
1466 const size_t raw_size = n * sizeof(float);
1467 const zfp_type type = zfp_type_float;
1468 zfp_field *field = zfp_field_3d(array, type, snx, sny, snz);
1469
1470 /* Allocate meta data for a compressed stream... */
1471 zfp_stream *zfp = zfp_stream_open(NULL);
1472 if (!field || !zfp)
1473 ERRMSG("Failed to allocate zfp structures!");
1474
1475 /* Set compression mode... */
1476 int actual_prec = 0;
1477 double actual_tol = 0;
1478 if ((ctl->met_zfp_prec[metvar] > 0 && ctl->met_zfp_tol[metvar] > 0)
1479 || (ctl->met_zfp_prec[metvar] <= 0 && ctl->met_zfp_tol[metvar] <= 0)) {
1480 ERRMSG("Exactly one of precision or tolerance must be set for zfp!");
1481 } else if (ctl->met_zfp_prec[metvar] > 0)
1482 actual_prec = (int) zfp_stream_set_precision(zfp, (unsigned int)
1483 ctl->met_zfp_prec[metvar]);
1484 else if (ctl->met_zfp_tol[metvar] > 0)
1485 actual_tol = zfp_stream_set_accuracy(zfp, ctl->met_zfp_tol[metvar]);
1486
1487 /* Allocate buffer for compressed data... */
1488 const size_t bufsize = zfp_stream_maximum_size(zfp, field);
1489 void *buffer;
1490 ALLOC(buffer, char,
1491 bufsize);
1492
1493 /* Associate bit stream with allocated buffer... */
1494 bitstream *stream = stream_open(buffer, bufsize);
1495 zfp_stream_set_bit_stream(zfp, stream);
1496 zfp_stream_rewind(zfp);
1497
1498 /* Read compressed stream and decompress array... */
1499 size_t zfpsize;
1500 double *scale_off = NULL, *scale_scl = NULL;
1501 char codec_info[LEN];
1502 if (decompress) {
1503
1504 /* Read optional scaling metadata before the ZFP bit stream... */
1505 const int stored_lossy_scale =
1506 compress_read_lossy_scale(inout, snx, &scale_off, &scale_scl);
1507 /* Read input... */
1508 FREAD(&zfpsize, size_t,
1509 1,
1510 inout);
1511 if (zfpsize > bufsize)
1512 ERRMSG("Compressed data size exceeds allocated buffer!");
1513 FREAD(buffer, unsigned char,
1514 zfpsize,
1515 inout);
1516
1517 /* Decode the bit stream directly into the caller-provided field... */
1518 const double t0 = omp_get_wtime();
1519 if (!zfp_decompress(zfp, field))
1520 ERRMSG("Decompression failed!");
1521
1522 /* Restore the original value range after lossy unit scaling... */
1523 if (stored_lossy_scale > 0)
1524 compress_unscale_from_unit(array, sny * snz, snx, scale_off, scale_scl);
1525 const double t_decomp = omp_get_wtime() - t0;
1526 const double ratio = COMPRESS_RATIO(raw_size, zfpsize);
1527 const double bpv = COMPRESS_BPV(n, zfpsize);
1528 snprintf(codec_info, LEN, "ZFP, PREC=%d, TOL=%g, SCALE=%d",
1529 actual_prec, actual_tol, stored_lossy_scale);
1530
1531 /* Logging... */
1532 LOG(2, "Read 3-D variable: %s"
1533 " (%s, RATIO=%g, BPV=%g, T_DECOMP=%g s, V_DECOMP=%g MiB/s)",
1534 varname, codec_info, ratio, bpv, t_decomp, COMPRESS_SPEED(raw_size,
1535 t_decomp));
1536 }
1537
1538 /* Compress array and output compressed stream... */
1539 else {
1540 float *tmp_all, *orig_all = NULL;
1541 const size_t nxy = sny * snz;
1542
1543 /* Preserve the unscaled input only if diagnostics need it later... */
1544 if (ctl->met_lossy_scale[metvar] > 0 && level_log) {
1545 ALLOC(orig_all, float,
1546 n);
1547 memcpy(orig_all, array, raw_size);
1548 }
1549
1550 /* Optionally scale each vertical level to unit space before ZFP... */
1551 compress_write_lossy_scale(inout, ctl->met_lossy_scale[metvar], array,
1552 nxy, snx, &scale_off, &scale_scl);
1553
1554 /* Keep a temporary decode buffer for the shared diagnostics path... */
1555 ALLOC(tmp_all, float,
1556 n);
1557
1558 /* Compress the field into the configured ZFP bit stream... */
1559 const double t0 = omp_get_wtime();
1560 zfpsize = zfp_compress(zfp, field);
1561 const double t_comp = omp_get_wtime() - t0;
1562 if (!zfpsize) {
1563 ERRMSG("Compression failed!");
1564 } else {
1565 /* Write output... */
1566 FWRITE(&zfpsize, size_t,
1567 1,
1568 inout);
1569 FWRITE(buffer, unsigned char,
1570 zfpsize,
1571 inout);
1572 }
1573
1574 /* Decode once into a temporary field for the shared diagnostics... */
1575 zfp_field *dec_field = zfp_field_3d(tmp_all, type, snx, sny, snz);
1576 if (!dec_field)
1577 ERRMSG("Failed to allocate zfp structures!");
1578
1579 /* Rewind the bit stream and decode once for diagnostics and timing... */
1580 const double t1 = omp_get_wtime();
1581 zfp_stream_rewind(zfp);
1582 if (!zfp_decompress(zfp, dec_field))
1583 ERRMSG("Decompression failed!");
1584 if (ctl->met_lossy_scale[metvar] > 0)
1585 compress_unscale_from_unit(tmp_all, nxy, snx, scale_off, scale_scl);
1586 const double t_decomp = omp_get_wtime() - t1;
1587
1588 const double ratio = COMPRESS_RATIO(raw_size, zfpsize);
1589 const double bpv = COMPRESS_BPV(n, zfpsize);
1590 compress_log_levels_3d(level_log, "ZFP", varname, met,
1591 (orig_all ? orig_all : array), tmp_all, nxy, snx,
1592 ratio, bpv, t_comp, t_decomp, raw_size);
1593
1594 snprintf(codec_info, LEN, "ZFP, PREC=%d, TOL=%g, SCALE=%d",
1595 actual_prec, actual_tol, ctl->met_lossy_scale[metvar]);
1596
1597 /* Logging... */
1598 LOG(2, "Write 3-D variable: %s"
1599 " (%s, RATIO=%g, BPV=%g, T_COMP=%g s, V_COMP=%g MiB/s,"
1600 " T_DECOMP=%g s, V_DECOMP=%g MiB/s)",
1601 varname, codec_info, ratio, bpv, t_comp, COMPRESS_SPEED(raw_size,
1602 t_comp),
1603 t_decomp, COMPRESS_SPEED(raw_size, t_decomp));
1604
1605 /* Free... */
1606 free(tmp_all);
1607 free(orig_all);
1608 zfp_field_free(dec_field);
1609 }
1610
1611 /* Free... */
1612 free(scale_off);
1613 free(scale_scl);
1614
1615 /* Free... */
1616 zfp_field_free(field);
1617 stream_close(stream);
1618 zfp_stream_close(zfp);
1619 free(buffer);
1620}
1621#endif
1622
1623/*****************************************************************************/
1624
1625#ifdef ZSTD
1626void compress_zstd(
1627 const ctl_t *ctl,
1628 const met_t *met,
1629 const char *varname,
1630 float *array,
1631 const int decompress,
1632 FILE *level_log,
1633 FILE *inout) {
1634
1635 /* Get buffer sizes... */
1636 const size_t nxy = (size_t) met->nx * (size_t) met->ny;
1637 const size_t nz = (size_t) met->np;
1638 const size_t n = nxy * nz, uncomprLen = n * sizeof(float);
1639 size_t compsize, comprLen = ZSTD_compressBound(uncomprLen);
1640
1641 /* Allocate... */
1642 char *compr, *uncompr = (char *) array;
1643 char codec_info[LEN];
1644 ALLOC(compr, char,
1645 comprLen);
1646
1647 /* Read compressed stream and decompress array... */
1648 if (decompress) {
1649
1650 /* Read input... */
1651 FREAD(&comprLen, size_t,
1652 1,
1653 inout);
1654 FREAD(compr, unsigned char,
1655 comprLen,
1656 inout);
1657
1658 /* Decode one full frame and validate the restored byte count... */
1659 const double t0 = omp_get_wtime();
1660 compsize = ZSTD_decompress(uncompr, uncomprLen, compr, comprLen);
1661 const double t_decomp = omp_get_wtime() - t0;
1662 if (ZSTD_isError(compsize) || compsize != uncomprLen)
1663 ERRMSG("Decompression failed or size mismatch!");
1664 const double ratio = COMPRESS_RATIO(uncomprLen, comprLen);
1665 const double bpv = COMPRESS_BPV(n, comprLen);
1666 snprintf(codec_info, LEN, "ZSTD, LEVEL=%d", ctl->met_zstd_level);
1667
1668 /* Logging... */
1669 LOG(2, "Read 3-D variable: %s"
1670 " (%s, RATIO=%g, BPV=%g, T_DECOMP=%g s, V_DECOMP=%g MiB/s)",
1671 varname, codec_info, ratio, bpv, t_decomp, COMPRESS_SPEED(uncomprLen,
1672 t_decomp));
1673 }
1674
1675 /* Compress array and output compressed stream... */
1676 else {
1677 ZSTD_CCtx *cctx;
1678 float *tmp_all;
1679
1680 /* Keep one temporary decode buffer for the shared diagnostics path... */
1681 ALLOC(tmp_all, float,
1682 n);
1683
1684 cctx = compress_zstd_create_cctx(ctl->met_zstd_level,
1685 ctl->met_zstd_nworkers);
1686
1687 /* Compress one full field with the preconfigured ZSTD context... */
1688 const double t0 = omp_get_wtime();
1689 compsize = ZSTD_compress2(cctx, compr, comprLen, uncompr, uncomprLen);
1690 const double t_comp = omp_get_wtime() - t0;
1691 if (ZSTD_isError(compsize)) {
1692 ZSTD_freeCCtx(cctx);
1693 ERRMSG("Compression failed!");
1694 } else {
1695
1696 /* Write output... */
1697 FWRITE(&compsize, size_t,
1698 1,
1699 inout);
1700 FWRITE(compr, unsigned char,
1701 compsize,
1702 inout);
1703 }
1704
1705 /* Decode once more so the common diagnostics see the reconstructed field... */
1706 const double t1 = omp_get_wtime();
1707 const size_t decomp_size = ZSTD_decompress(tmp_all, uncomprLen, compr,
1708 compsize);
1709 const double t_decomp = omp_get_wtime() - t1;
1710 if (ZSTD_isError(decomp_size) || decomp_size != uncomprLen)
1711 ERRMSG("Decompression failed or size mismatch!");
1712
1713 const double ratio = COMPRESS_RATIO(uncomprLen, compsize);
1714 const double bpv = COMPRESS_BPV(n, compsize);
1715 compress_log_levels_3d(level_log, "ZSTD", varname, met, array, tmp_all,
1716 nxy, nz, ratio, bpv, t_comp, t_decomp, uncomprLen);
1717
1718 snprintf(codec_info, LEN, "ZSTD, LEVEL=%d, NWORKERS=%d",
1720
1721 /* Logging... */
1722 LOG(2, "Write 3-D variable: %s"
1723 " (%s, RATIO=%g, BPV=%g, T_COMP=%g s, V_COMP=%g MiB/s,"
1724 " T_DECOMP=%g s, V_DECOMP=%g MiB/s)",
1725 varname, codec_info, ratio, bpv, t_comp, COMPRESS_SPEED(uncomprLen,
1726 t_comp),
1727 t_decomp, COMPRESS_SPEED(uncomprLen, t_decomp));
1728
1729 /* Free... */
1730 ZSTD_freeCCtx(cctx);
1731 free(tmp_all);
1732 }
1733
1734 /* Free... */
1735 free(compr);
1736}
1737#endif
1738
1739/*****************************************************************************/
1740
1741#ifdef LZ4
1742void compress_lz4(
1743 const ctl_t *ctl,
1744 const met_t *met,
1745 const char *varname,
1746 float *array,
1747 const int decompress,
1748 FILE *level_log,
1749 FILE *inout) {
1750
1751 /* Get buffer sizes... */
1752 const size_t nxy = (size_t) met->nx * (size_t) met->ny;
1753 const size_t nz = (size_t) met->np;
1754 const size_t n = nxy * nz;
1755 const size_t uncomprLen = n * sizeof(float);
1756 if (uncomprLen > (size_t) INT_MAX)
1757 ERRMSG("LZ4 input buffer exceeds INT_MAX!");
1758 const int uncomprLenInt = (int) uncomprLen;
1759 const int accel = ctl->met_lz4_accel > 0 ? ctl->met_lz4_accel : 1;
1760 const int comprCapInt = LZ4_compressBound(uncomprLenInt);
1761 if (comprCapInt <= 0)
1762 ERRMSG("Cannot determine LZ4 compression bound!");
1763 size_t comprLen = (size_t) comprCapInt;
1764
1765 /* Allocate... */
1766 char *compr, *uncompr = (char *) array;
1767 char codec_info[LEN];
1768 ALLOC(compr, char,
1769 comprLen);
1770 snprintf(codec_info, LEN, "LZ4, ACCEL=%d", accel);
1771
1772 /* Read compressed stream and decompress array... */
1773 if (decompress) {
1774
1775 /* Read input... */
1776 FREAD(&comprLen, size_t,
1777 1,
1778 inout);
1779 if (comprLen > (size_t) INT_MAX)
1780 ERRMSG("LZ4 compressed buffer exceeds INT_MAX!");
1781 FREAD(compr, unsigned char,
1782 comprLen,
1783 inout);
1784
1785 /* Decode one full frame and validate the restored byte count... */
1786 const double t0 = omp_get_wtime();
1787 const int decomp_size =
1788 LZ4_decompress_safe(compr, uncompr, (int) comprLen, uncomprLenInt);
1789 const double t_decomp = omp_get_wtime() - t0;
1790 if (decomp_size != uncomprLenInt)
1791 ERRMSG("Decompression failed or size mismatch!");
1792 const double ratio = COMPRESS_RATIO(uncomprLen, comprLen);
1793 const double bpv = COMPRESS_BPV(n, comprLen);
1794
1795 /* Logging... */
1796 LOG(2, "Read 3-D variable: %s"
1797 " (%s, RATIO=%g, BPV=%g, T_DECOMP=%g s, V_DECOMP=%g MiB/s)",
1798 varname, codec_info, ratio, bpv, t_decomp, COMPRESS_SPEED(uncomprLen,
1799 t_decomp));
1800 }
1801
1802 /* Compress array and output compressed stream... */
1803 else {
1804 float *tmp_all;
1805
1806 /* Keep one temporary decode buffer for the shared diagnostics path... */
1807 ALLOC(tmp_all, float,
1808 n);
1809
1810 /* Compress one full field with the configured LZ4 acceleration... */
1811 const double t0 = omp_get_wtime();
1812 const int compsizeInt =
1813 LZ4_compress_fast(uncompr, compr, uncomprLenInt, comprCapInt, accel);
1814 const double t_comp = omp_get_wtime() - t0;
1815 if (compsizeInt <= 0)
1816 ERRMSG("Compression failed!");
1817 const size_t compsize = (size_t) compsizeInt;
1818
1819 /* Write output... */
1820 FWRITE(&compsize, size_t,
1821 1,
1822 inout);
1823 FWRITE(compr, unsigned char,
1824 compsize,
1825 inout);
1826
1827 /* Decode once more so the common diagnostics see the reconstructed field... */
1828 const double t1 = omp_get_wtime();
1829 const int decomp_size =
1830 LZ4_decompress_safe(compr, (char *) tmp_all, compsizeInt,
1831 uncomprLenInt);
1832 const double t_decomp = omp_get_wtime() - t1;
1833 if (decomp_size != uncomprLenInt)
1834 ERRMSG("Decompression failed or size mismatch!");
1835
1836 const double ratio = COMPRESS_RATIO(uncomprLen, compsize);
1837 const double bpv = COMPRESS_BPV(n, compsize);
1838 compress_log_levels_3d(level_log, "LZ4", varname, met, array, tmp_all,
1839 nxy, nz, ratio, bpv, t_comp, t_decomp, uncomprLen);
1840
1841 /* Logging... */
1842 LOG(2, "Write 3-D variable: %s"
1843 " (%s, RATIO=%g, BPV=%g, T_COMP=%g s, V_COMP=%g MiB/s,"
1844 " T_DECOMP=%g s, V_DECOMP=%g MiB/s)",
1845 varname, codec_info, ratio, bpv, t_comp, COMPRESS_SPEED(uncomprLen,
1846 t_comp),
1847 t_decomp, COMPRESS_SPEED(uncomprLen, t_decomp));
1848
1849 free(tmp_all);
1850 }
1851
1852 /* Free... */
1853 free(compr);
1854}
1855#endif
1856
1857/*****************************************************************************/
1858
1859double cos_sza(
1860 const double sec,
1861 const double lon,
1862 const double lat) {
1863
1864 /* Number of days and fraction with respect to 2000-01-01T12:00Z... */
1865 const double D = sec / 86400 - 0.5;
1866
1867 /* Geocentric apparent ecliptic longitude [rad]... */
1868 const double g = DEG2RAD(357.529 + 0.98560028 * D);
1869 const double q = 280.459 + 0.98564736 * D;
1870 const double L = DEG2RAD(q + 1.915 * sin(g) + 0.020 * sin(2 * g));
1871
1872 /* Mean obliquity of the ecliptic [rad]... */
1873 const double e = DEG2RAD(23.439 - 0.00000036 * D);
1874
1875 /* Declination [rad]... */
1876 const double sindec = sin(e) * sin(L);
1877
1878 /* Right ascension [rad]... */
1879 const double ra = atan2(cos(e) * sin(L), cos(L));
1880
1881 /* Greenwich Mean Sidereal Time [h]... */
1882 const double GMST = 18.697374558 + 24.06570982441908 * D;
1883
1884 /* Local Sidereal Time [h]... */
1885 const double LST = GMST + lon / 15;
1886
1887 /* Hour angle [rad]... */
1888 const double h = LST / 12 * M_PI - ra;
1889
1890 /* Convert latitude... */
1891 const double lat_help = DEG2RAD(lat);
1892
1893 /* Return cosine of solar zenith angle... */
1894 return sin(lat_help) * sindec + cos(lat_help) * sqrt(1 -
1895 SQR(sindec)) * cos(h);
1896}
1897
1898/*****************************************************************************/
1899
1901 const int year,
1902 const int mon,
1903 const int day,
1904 int *doy) {
1905
1906 const int
1907 d0[12] = { 1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 },
1908 d0l[12] = { 1, 32, 61, 92, 122, 153, 183, 214, 245, 275, 306, 336 };
1909
1910 /* Get day of year... */
1911 if (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0))
1912 *doy = d0l[mon - 1] + day - 1;
1913 else
1914 *doy = d0[mon - 1] + day - 1;
1915}
1916
1917/*****************************************************************************/
1918
1919#ifdef DD
1920/* Per-rank communication statistics use the existing MPI send counts. */
1921#if LOGLEV >= 2
1922static int *dd_node;
1923static int dd_particle_bytes;
1924static unsigned long long dd_sent, dd_intra, dd_inter;
1925#endif
1926
1928 const ctl_t *ctl,
1929 const dd_t *dd,
1930 atm_t *atm,
1931 const int init) {
1932
1933 /* Set timer... */
1934 SELECT_TIMER("DD_ASSIGN_SUBDOMAINS", "DD");
1935
1936 /* Get MPI rank... */
1937 int rank;
1938 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
1939
1940 /* Determine owned core subdomain in index space... */
1941 const int zonal_rank = rank / ctl->dd_subdomains_meridional;
1942 const int merid_rank = rank % ctl->dd_subdomains_meridional;
1943
1944 const int nx_block = dd->nx_glob / ctl->dd_subdomains_zonal;
1945 const int ny_block = dd->ny_glob / ctl->dd_subdomains_meridional;
1946
1947 const int ix0 = zonal_rank * nx_block;
1948 const int iy0 = merid_rank * ny_block;
1949
1950 const int ix1 = ix0 + nx_block
1951 + (zonal_rank == ctl->dd_subdomains_zonal - 1 ?
1952 dd->nx_glob - ctl->dd_subdomains_zonal * nx_block : 0);
1953
1954 const int iy1 = iy0 + ny_block
1955 + (merid_rank == ctl->dd_subdomains_meridional - 1 ?
1956 dd->ny_glob - ctl->dd_subdomains_meridional * ny_block : 0);
1957
1958 /* Loop over particles... */
1959#ifdef _OPENACC
1960#pragma acc update device(dd->nx_glob, dd->ny_glob, \
1961 dd->lon_glob[:dd->nx_glob], \
1962 dd->lat_glob[:dd->ny_glob])
1963#pragma acc data present(atm, ctl, dd)
1964#pragma acc parallel loop independent gang vector
1965#endif
1966 for (int ip = 0; ip < atm->np; ip++) {
1967
1968 /* Skip already invalid particles... */
1969 if (!init && (int) atm->q[ctl->qnt_current_subdomain][ip] == -1)
1970 continue;
1971
1972 /* Normalize coordinates... */
1973 double lon = atm->lon[ip];
1974 double lat = atm->lat[ip];
1975 dd_normalize_lon_lat(dd, &lon, &lat);
1976
1977 /* Check whether particle is inside owned core subdomain... */
1978 const int ix = locate_reg(dd->lon_glob, dd->nx_glob, lon);
1979 const int iy = locate_irr(dd->lat_glob, dd->ny_glob, lat);
1980 const int inside = (ix >= ix0 && ix < ix1 && iy >= iy0 && iy < iy1);
1981
1982 /* Init... */
1983 if (init) {
1984 atm->q[ctl->qnt_current_subdomain][ip] = inside ? rank : -1;
1985 atm->q[ctl->qnt_target_subdomain][ip] = inside ? rank : -1;
1986 }
1987
1988 /* Reassign... */
1989 else {
1990 atm->q[ctl->qnt_target_subdomain][ip] =
1991 inside ? rank : dd_calc_subdomain_from_coords(ctl, dd, lon, lat);
1992 }
1993 }
1994}
1995#endif
1996
1997/*****************************************************************************/
1998
1999#ifdef DD
2001 const ctl_t *ctl,
2002 cache_t *cache,
2003 atm_t *atm,
2004 particle_t *particles,
2005 const int npart) {
2006
2007 /* Set timer... */
2008 SELECT_TIMER("DD_ATM2PARTICLES", "DD");
2009
2010 /* Check if particles are present... */
2011 if (npart == 0)
2012 return;
2013
2014 int rank;
2015 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
2016
2017 /* Select the particles that will be send... */
2018#ifdef _OPENACC
2019#pragma acc enter data create(npart, particles[:npart])
2020#pragma acc update device(npart, particles[:npart])
2021#pragma acc parallel loop present(atm, ctl, particles, cache, npart)
2022#endif
2023 for (int ip = atm->np; ip < atm->np + npart; ip++)
2024 if (((int) (atm->q[ctl->qnt_target_subdomain][ip]) != rank)
2025 && ((int) (atm->q[ctl->qnt_target_subdomain][ip]) >= 0)
2026 && ((int) atm->q[ctl->qnt_current_subdomain][ip] >= 0)) {
2027
2028 particles[ip - atm->np].time = atm->time[ip];
2029 particles[ip - atm->np].lon = atm->lon[ip];
2030 particles[ip - atm->np].lat = atm->lat[ip];
2031 particles[ip - atm->np].p = atm->p[ip];
2032 for (int iq = 0; iq < ctl->nq; iq++)
2033 particles[ip - atm->np].q[iq] = atm->q[iq][ip];
2034 for (int i = 0; i < 3; i++)
2035 particles[ip - atm->np].uvwp[i] = cache->uvwp[ip][i];
2036
2037 atm->q[ctl->qnt_current_subdomain][ip] = -1;
2038 cache->dt[ip] = 0;
2039 }
2040#ifdef _OPENACC
2041#pragma acc update host( particles[:npart])
2042#pragma acc exit data delete(npart, particles)
2043#endif
2044}
2045#endif
2046
2047/*****************************************************************************/
2048
2049#ifdef DD
2051 const ctl_t *ctl,
2052 cache_t *cache,
2053 atm_t *atm,
2054 dd_t *dd,
2055 particle_t *particles,
2056 const int nkeep,
2057 const int npart) {
2058
2059 /* Set timer... */
2060 SELECT_TIMER("DD_PACK_COMPACT", "DD");
2061
2062 int rank;
2063 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
2064
2065 const int np = atm->np;
2066#ifdef _OPENACC
2067 /* The permutation was built by dd_count_outgoing(): kept particles at
2068 the front and outgoing particles at the back. Pack before compacting,
2069 since the latter overwrites source slots. */
2070 if (npart > 0) {
2071 SELECT_TIMER("DD_GPU_PACK", "DD");
2072#pragma acc enter data create(particles[0:npart])
2073#pragma acc parallel loop present(ctl, atm, cache, dd, particles)
2074 for (int i = 0; i < npart; i++) {
2075 const int ip = dd->perm[np - 1 - i];
2076 particles[i].time = atm->time[ip];
2077 particles[i].p = atm->p[ip];
2078 particles[i].lon = atm->lon[ip];
2079 particles[i].lat = atm->lat[ip];
2080 for (int iq = 0; iq < ctl->nq; iq++)
2081 particles[i].q[iq] = atm->q[iq][ip];
2082 for (int j = 0; j < 3; j++)
2083 particles[i].uvwp[j] = cache->uvwp[ip][j];
2084 }
2085 SELECT_TIMER("DD_GPU_SEND_D2H", "DD");
2086#pragma acc update self(particles[0:npart])
2087#pragma acc exit data delete(particles[0:npart])
2088 }
2089
2090 /* sort_array stages one field in dd->tmp before writing it back.
2091 Thus no parallel read can race with a write to the original field. */
2092 if (nkeep > 0 && nkeep != np) {
2093 SELECT_TIMER("DD_GPU_COMPACT", "DD");
2094 sort_array(atm->time, dd->perm, dd->tmp, nkeep);
2095 sort_array(atm->p, dd->perm, dd->tmp, nkeep);
2096 sort_array(atm->lon, dd->perm, dd->tmp, nkeep);
2097 sort_array(atm->lat, dd->perm, dd->tmp, nkeep);
2098 for (int iq = 0; iq < ctl->nq; iq++)
2099 sort_array(atm->q[iq], dd->perm, dd->tmp, nkeep);
2100 sort_array(cache->dt, dd->perm, dd->tmp, nkeep);
2101 sort_uvwp(cache, dd->perm, dd->tmp, nkeep);
2102 }
2103
2104 const int nlost = np - nkeep - npart;
2105 if (nlost > 0)
2106 WARN("Rank %d: %d particles have invalid subdomain and will be lost",
2107 rank, nlost);
2108 atm->np = nkeep;
2109#pragma acc update device(atm->np)
2110#else
2111 (void) dd;
2112 (void) nkeep;
2113
2114 /* Pack outgoing particles and fill gaps with the remaining local ones... */
2115 int nkeep_cpu = 0, nsend = 0, nlost = 0;
2116 for (int ip = 0; ip < np; ip++) {
2117 const int current = (int) atm->q[ctl->qnt_current_subdomain][ip];
2118 const int target = (int) atm->q[ctl->qnt_target_subdomain][ip];
2119 if (current < 0 || target < 0) {
2120 nlost++;
2121 continue;
2122 }
2123
2124 if (target != rank) {
2125 particles[nsend].time = atm->time[ip];
2126 particles[nsend].p = atm->p[ip];
2127 particles[nsend].lon = atm->lon[ip];
2128 particles[nsend].lat = atm->lat[ip];
2129 for (int iq = 0; iq < ctl->nq; iq++)
2130 particles[nsend].q[iq] = atm->q[iq][ip];
2131 for (int i = 0; i < 3; i++)
2132 particles[nsend].uvwp[i] = cache->uvwp[ip][i];
2133 nsend++;
2134 continue;
2135 }
2136
2137 if (nkeep_cpu != ip) {
2138 atm->time[nkeep_cpu] = atm->time[ip];
2139 atm->p[nkeep_cpu] = atm->p[ip];
2140 atm->lon[nkeep_cpu] = atm->lon[ip];
2141 atm->lat[nkeep_cpu] = atm->lat[ip];
2142 for (int iq = 0; iq < ctl->nq; iq++)
2143 atm->q[iq][nkeep_cpu] = atm->q[iq][ip];
2144 cache->dt[nkeep_cpu] = cache->dt[ip];
2145 for (int i = 0; i < 3; i++)
2146 cache->uvwp[nkeep_cpu][i] = cache->uvwp[ip][i];
2147 }
2148 nkeep_cpu++;
2149 }
2150
2151 if (nsend != npart)
2152 ERRMSG("Inconsistent number of outgoing particles!");
2153 if (nlost > 0)
2154 WARN("Rank %d: %d particles have invalid subdomain and will be lost",
2155 rank, nlost);
2156 atm->np = nkeep_cpu;
2157#endif
2158}
2159#endif
2160
2161/*****************************************************************************/
2162
2163#ifdef DD
2165 const ctl_t *ctl,
2166 const dd_t *dd,
2167 double lon,
2168 double lat) {
2169
2170 /* Normalize coordinates... */
2171 dd_normalize_lon_lat(dd, &lon, &lat);
2172
2173 /* Get grid index... */
2174 const int ix = locate_reg(dd->lon_glob, dd->nx_glob, lon);
2175 const int iy = locate_irr(dd->lat_glob, dd->ny_glob, lat);
2176
2177 /* Get zonal subdomain index... */
2178 const int nx_block = dd->nx_glob / ctl->dd_subdomains_zonal;
2179 int zonal_rank = ix / nx_block;
2180 if (zonal_rank >= ctl->dd_subdomains_zonal)
2181 zonal_rank = ctl->dd_subdomains_zonal - 1;
2182
2183 /* Get meridional subdomain index... */
2184 const int ny_block = dd->ny_glob / ctl->dd_subdomains_meridional;
2185 int merid_rank = iy / ny_block;
2186 if (merid_rank >= ctl->dd_subdomains_meridional)
2187 merid_rank = ctl->dd_subdomains_meridional - 1;
2188
2189 /* Return rank... */
2190 return zonal_rank * ctl->dd_subdomains_meridional + merid_rank;
2191}
2192#endif
2193
2194/*****************************************************************************/
2195
2196#ifdef DD
2198 const ctl_t *ctl,
2199 const dd_t *dd,
2200 particle_t **particles,
2201 int *npart,
2202 int *capacity) {
2203
2204 /* Set timer... */
2205 SELECT_TIMER("DD_COMMUNICATE_PARTICLES", "DD");
2206
2207 int rank, size;
2208 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
2209 MPI_Comm_size(MPI_COMM_WORLD, &size);
2210
2211 int *send_counts, *recv_counts;
2212 int *send_displs, *recv_displs;
2213 int *offsets;
2214
2215 particle_t *sendbuf = NULL;
2216 particle_t *recvbuf = NULL;
2217
2218 ALLOC(send_counts, int,
2219 size);
2220 ALLOC(recv_counts, int,
2221 size);
2222 ALLOC(send_displs, int,
2223 size);
2224 ALLOC(recv_displs, int,
2225 size);
2226 ALLOC(offsets, int,
2227 size);
2228
2229 /* Count particles per destination rank... */
2230#pragma omp parallel for
2231 for (int ip = 0; ip < *npart; ip++) {
2232 const int dest = (int) (*particles)[ip].q[ctl->qnt_target_subdomain];
2233 if (dest == rank)
2234 continue;
2235 if (dest < 0 || dest >= size)
2236 ERRMSG("Invalid destination rank!");
2237#pragma omp atomic
2238 send_counts[dest]++;
2239 }
2240
2241 /* Compute send displacements... */
2242 int nsend = 0;
2243 for (int i = 0; i < size; i++) {
2244 send_displs[i] = nsend;
2245 nsend += send_counts[i];
2246 }
2247#if LOGLEV >= 3
2248 LOG(3, "DD_SEND rank=%d particles=%d", rank, nsend);
2249#endif
2250
2251 /* Classify the already available MPI counts without scanning particles. */
2252#if LOGLEV >= 2
2253 if (dd_node != NULL) {
2254 dd_sent += (unsigned long long) nsend;
2255 for (int i = 0; i < size; i++) {
2256 if (dd_node[i] == dd_node[rank])
2257 dd_intra += (unsigned long long) send_counts[i];
2258 else
2259 dd_inter += (unsigned long long) send_counts[i];
2260 }
2261 }
2262#endif
2263
2264 /* Exchange particle counts... */
2265 MPI_Alltoall(send_counts, 1, MPI_INT,
2266 recv_counts, 1, MPI_INT, MPI_COMM_WORLD);
2267
2268 /* Compute receive displacements... */
2269 int nrecv = 0;
2270 for (int i = 0; i < size; i++) {
2271 recv_displs[i] = nrecv;
2272 nrecv += recv_counts[i];
2273 }
2274 if (nsend > 0)
2275 ALLOC(sendbuf, particle_t, nsend);
2276 if (nrecv > 0)
2277 ALLOC(recvbuf, particle_t, nrecv);
2278 for (int i = 0; i < size; i++)
2279 offsets[i] = send_displs[i];
2280
2281 /* Pack particles into send buffer... */
2282 for (int ip = 0; ip < *npart; ip++) {
2283 const int dest = (int) (*particles)[ip].q[ctl->qnt_target_subdomain];
2284 if (dest == rank)
2285 continue;
2286 memcpy(&sendbuf[offsets[dest]], &(*particles)[ip], sizeof(particle_t));
2287 offsets[dest]++;
2288 }
2289
2290 /* Exchange particle data... */
2291 MPI_Alltoallv(sendbuf,
2292 send_counts,
2293 send_displs,
2294 dd->MPI_Particle,
2295 recvbuf,
2296 recv_counts, recv_displs, dd->MPI_Particle, MPI_COMM_WORLD);
2297
2298 /* Resize particle buffer if necessary... */
2299 if (nrecv > *capacity) {
2300 const int newcap = nrecv + nrecv / 2 + 1;
2301 particle_t *tmp =
2302 realloc(*particles, (size_t) newcap * sizeof(particle_t));
2303 if (!tmp)
2304 ERRMSG("Out of memory!");
2305 *particles = tmp;
2306 *capacity = newcap;
2307 }
2308
2309 /* Copy received particles... */
2310#pragma omp parallel for
2311 for (int ip = 0; ip < nrecv; ip++) {
2312 (*particles)[ip] = recvbuf[ip];
2313 (*particles)[ip].q[ctl->qnt_target_subdomain] = rank;
2314 (*particles)[ip].q[ctl->qnt_current_subdomain] = rank;
2315 }
2316 *npart = nrecv;
2317
2318 /* Free temporary buffers... */
2319 if (sendbuf)
2320 free(sendbuf);
2321 if (recvbuf)
2322 free(recvbuf);
2323 free(send_counts);
2324 free(recv_counts);
2325 free(send_displs);
2326 free(recv_displs);
2327 free(offsets);
2328}
2329#endif
2330
2331/*****************************************************************************/
2332
2333#ifdef DD
2335 const ctl_t *ctl,
2336 dd_t *dd,
2337 atm_t *atm) {
2338
2339 /* Check configuration... */
2340 if ((ctl->dd) &&
2342 ERRMSG("Please provide zonal and meridional subdomain numbers!");
2343 if (!ctl->dd)
2344 ERRMSG("Activate control flag DD!");
2345
2346 /* Check if enough tasks are requested... */
2347 int size;
2348 MPI_Comm_size(MPI_COMM_WORLD, &size);
2350 ERRMSG("Number of tasks and subdomains is not identical!");
2351
2352 /* Register the MPI_Particle data type... */
2353 const MPI_Datatype types[6] =
2354 { MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE,
2355 MPI_FLOAT };
2356 const int blocklengths[6] = { 1, 1, 1, 1, NQ, 3 };
2357 const MPI_Aint displacements[6] =
2358 { offsetof(particle_t, time), offsetof(particle_t, p),
2359 offsetof(particle_t, lon), offsetof(particle_t, lat),
2360 offsetof(particle_t, q), offsetof(particle_t, uvwp)
2361 };
2362 MPI_Datatype fields;
2363 MPI_Type_create_struct(6, blocklengths, displacements, types, &fields);
2364 /* The trailing float array can leave padding in particle_t. Alltoallv
2365 counts and displacements must advance by the full C structure size. */
2366 MPI_Type_create_resized(fields, 0, (MPI_Aint) sizeof(particle_t),
2367 &dd->MPI_Particle);
2368 MPI_Type_free(&fields);
2369 MPI_Type_commit(&dd->MPI_Particle);
2370 MPI_Aint lower_bound, extent;
2371 MPI_Type_get_extent(dd->MPI_Particle, &lower_bound, &extent);
2372 if (lower_bound != 0 || extent != (MPI_Aint) sizeof(particle_t))
2373 ERRMSG("Invalid MPI particle datatype extent!");
2374
2375 LOG(1, "DD: %d x %d subdomains | exchange mode %d | initial particles %d",
2377 ctl->dd_exchange_mode, atm->np);
2378
2379 /* Discover node placement once when detailed logging is enabled. */
2380#if LOGLEV >= 2
2381 {
2382 int rank, local_rank, leader;
2383 MPI_Comm shared;
2384 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
2385 MPI_Comm_split_type(MPI_COMM_WORLD, MPI_COMM_TYPE_SHARED, rank,
2386 MPI_INFO_NULL, &shared);
2387 MPI_Comm_rank(shared, &local_rank);
2388 leader = local_rank == 0 ? rank : -1;
2389 MPI_Bcast(&leader, 1, MPI_INT, 0, shared);
2390 ALLOC(dd_node, int, size);
2391 MPI_Allgather(&leader, 1, MPI_INT, dd_node, 1, MPI_INT,
2392 MPI_COMM_WORLD);
2393 MPI_Type_size(dd->MPI_Particle, &dd_particle_bytes);
2394 dd_sent = dd_intra = dd_inter = 0;
2395 char hostname[MPI_MAX_PROCESSOR_NAME];
2396 int hostname_len;
2397 MPI_Get_processor_name(hostname, &hostname_len);
2398 const char *gpu = getenv("CUDA_VISIBLE_DEVICES");
2399 LOG(2, "DD_NODE rank=%d leader=%d local_rank=%d host=%s gpu=%s",
2400 rank, leader, local_rank, hostname, gpu ? gpu : "-");
2401 MPI_Comm_free(&shared);
2402 }
2403#endif
2404
2405 /* Check if particles are in subdomain... */
2406 dd_assign_subdomains(ctl, dd, atm, 1);
2407}
2408#endif
2409
2410/*****************************************************************************/
2411
2412#ifdef DD
2414 const dd_t *dd,
2415 double *lon,
2416 double *lat) {
2417
2418 const double lon_min = (dd->lon_glob[0] < 0) ? -180.0 : 0.0;
2419
2420 /* Normalize longitude to match global grid convention... */
2421 *lon = FMOD(*lon - lon_min, 360.0);
2422 if (*lon < 0.0)
2423 *lon += 360.0;
2424 *lon += lon_min;
2425
2426 /* Wrap latitude across the poles and shift longitude... */
2427 if (*lat > 90.0) {
2428 *lat = 180.0 - *lat;
2429 *lon += 180.0;
2430 } else if (*lat < -90.0) {
2431 *lat = -180.0 - *lat;
2432 *lon += 180.0;
2433 }
2434
2435 /* Renormalize longitude after pole crossing... */
2436 *lon = FMOD(*lon - lon_min, 360.0);
2437 if (*lon < 0.0)
2438 *lon += 360.0;
2439 *lon += lon_min;
2440}
2441#endif
2442
2443/*****************************************************************************/
2444
2445#ifdef DD
2447 const ctl_t *ctl,
2448 cache_t *cache,
2449 const particle_t *particles,
2450 const int npart,
2451 atm_t *atm) {
2452
2453 /* Set timer... */
2454 SELECT_TIMER("DD_PARTICLES2ATM", "DD");
2455
2456 /* Check if particles are present... */
2457 if (npart == 0)
2458 return;
2459
2460 /* Check number of particles... */
2461 if (atm->np + npart > NP)
2462 ERRMSG("Too many particles. Increase NP!");
2463
2464#ifdef _OPENACC
2465#pragma acc enter data create(npart, particles[:npart])
2466#pragma acc update device(particles[:npart], npart)
2467#pragma acc data present(atm, ctl, cache, particles, npart)
2468#pragma acc parallel loop
2469#endif
2470 for (int ip = atm->np; ip < atm->np + npart; ip++) {
2471 atm->time[ip] = particles[ip - atm->np].time;
2472 atm->lon[ip] = particles[ip - atm->np].lon;
2473 atm->lat[ip] = particles[ip - atm->np].lat;
2474 atm->p[ip] = particles[ip - atm->np].p;
2475 for (int iq = 0; iq < ctl->nq; iq++)
2476 atm->q[iq][ip] = particles[ip - atm->np].q[iq];
2477 for (int i = 0; i < 3; i++)
2478 cache->uvwp[ip][i] = particles[ip - atm->np].uvwp[i];
2479 cache->dt[ip] = ctl->dt_mod;
2480 }
2481#ifdef _OPENACC
2482#pragma acc exit data delete(npart, particles)
2483#endif
2484
2485 /* Reset size... */
2486 atm->np += npart;
2487#ifdef _OPENACC
2488#pragma acc update device(atm->np)
2489#endif
2490}
2491#endif
2492
2493/*****************************************************************************/
2494
2496 double *array,
2497 const int *perm,
2498 double *tmp,
2499 const int np) {
2500
2501 if (np == 0)
2502 return;
2503
2504 /* The caller keeps all three buffers resident on the device. */
2505#ifdef _OPENACC
2506#pragma acc data present(array[0:np],perm[0:np],tmp[0:np])
2507#endif
2508 {
2509
2510 /* perm maps each destination index to its original source index. Gather
2511 every source value before any destination can overwrite it. */
2512#ifdef _OPENACC
2513#pragma acc parallel loop independent gang vector
2514#else
2515#pragma omp parallel for default(shared)
2516#endif
2517 for (int ip = 0; ip < np; ip++)
2518 tmp[ip] = array[perm[ip]];
2519
2520 /* The staging pass is complete, so writes to array are independent. */
2521#ifdef _OPENACC
2522#pragma acc parallel loop independent gang vector
2523#else
2524#pragma omp parallel for default(shared)
2525#endif
2526 for (int ip = 0; ip < np; ip++)
2527 array[ip] = tmp[ip];
2528 }
2529}
2530
2531/*****************************************************************************/
2532
2534 cache_t *cache,
2535 const int *perm,
2536 double *tmp,
2537 const int np) {
2538
2539 if (np == 0)
2540 return;
2541
2542 /* Reuse the caller's device-resident permutation and staging buffer. */
2543#ifdef _OPENACC
2544#pragma acc data present(cache,perm[0:np],tmp[0:np])
2545#endif
2546 {
2547
2548 /* The three float components share one double staging buffer. Finish
2549 both passes for a component before reusing the buffer for the next. */
2550 for (int j = 0; j < 3; j++) {
2551 /* Gather first so in-place writes cannot race with source reads. */
2552#ifdef _OPENACC
2553#pragma acc parallel loop
2554#else
2555#pragma omp parallel for
2556#endif
2557 for (int ip = 0; ip < np; ip++)
2558 tmp[ip] = cache->uvwp[perm[ip]][j];
2559
2560 /* Every float value is represented exactly while staged as double. */
2561#ifdef _OPENACC
2562#pragma acc parallel loop
2563#else
2564#pragma omp parallel for
2565#endif
2566 for (int ip = 0; ip < np; ip++)
2567 cache->uvwp[ip][j] = (float) tmp[ip];
2568 }
2569 }
2570}
2571
2572/*****************************************************************************/
2573
2574#ifdef DD
2576 const ctl_t *ctl,
2577 atm_t *atm,
2578 cache_t *cache,
2579 int *npart) {
2580 SELECT_TIMER("DD_PUSH", "DD");
2581 int rank;
2582 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
2583
2584 int counter = 0;
2585
2586#ifdef _OPENACC
2587 int *counter_ptr = (int *) acc_malloc(sizeof(int));
2588 if (counter_ptr == NULL) {
2589 ERRMSG("Failed to allocate device counter memory!");
2590 }
2591 acc_memcpy_to_device(counter_ptr, &counter, sizeof(int));
2592#else
2593 int *counter_ptr = &counter;
2594#endif
2595
2596 /* Push non local particles behind the end of the atm... */
2597#ifdef _OPENACC
2598#pragma acc parallel loop present(atm, cache, ctl) deviceptr(counter_ptr)
2599#else
2600#pragma omp parallel for
2601#endif
2602 for (int ip = 0; ip < atm->np; ip++) {
2603 if ((int) atm->q[ctl->qnt_current_subdomain][ip] != -1 &&
2604 (int) atm->q[ctl->qnt_target_subdomain][ip] != rank) {
2605 int local_idx;
2606#ifdef _OPENACC
2607#pragma acc atomic capture
2608#else
2609#pragma omp atomic capture
2610#endif
2611 {
2612 local_idx = *counter_ptr;
2613 (*counter_ptr)++;
2614 }
2615 int global_index = atm->np + local_idx;
2616 if (global_index >= NP)
2617 continue;
2618 /* Copy time, pressure, longitude, and latitude... */
2619 atm->time[global_index] = atm->time[ip];
2620 atm->p[global_index] = atm->p[ip];
2621 atm->lon[global_index] = atm->lon[ip];
2622 atm->lat[global_index] = atm->lat[ip];
2623 /* Copy all quantity data (q array)... */
2624 for (int iq = 0; iq < ctl->nq; iq++) {
2625 atm->q[iq][global_index] = atm->q[iq][ip];
2626 }
2627 /* Mark the original parcel as processed... */
2628 atm->q[ctl->qnt_target_subdomain][ip] = -1;
2629 atm->q[ctl->qnt_current_subdomain][ip] = -1;
2630 /* Reset cache->dt for the shifted particle... */
2631 cache->dt[global_index] = cache->dt[ip];
2632 cache->dt[ip] = 0;
2633 for (int i = 0; i < 3; i++)
2634 cache->uvwp[global_index][i] = cache->uvwp[ip][i];
2635 }
2636 }
2637
2638#ifdef _OPENACC
2639 acc_memcpy_from_device(npart, counter_ptr, sizeof(int));
2640 acc_free(counter_ptr);
2641#else
2642 *npart = counter;
2643#endif
2644
2645 if (atm->np + *npart > NP)
2646 ERRMSG("Too many particles during DD push. Increase NP!");
2647}
2648#endif
2649
2650/*****************************************************************************/
2651
2652#ifdef DD
2654 const ctl_t *ctl,
2655 const met_t *met0,
2656 cache_t *cache,
2657 atm_t *atm,
2658 dd_t *dd,
2659 int *npart) {
2660
2661 /* Set timer... */
2662 SELECT_TIMER("DD_SORT", "DD");
2663
2664 int rank;
2665 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
2666
2667 /* Allocate... */
2668 const int np = atm->np;
2669 double amax = (met0->nx * met0->ny + met0->ny) * met0->np + met0->np;
2670#ifdef _OPENACC
2671#pragma acc enter data create(amax)
2672#pragma acc update device(amax)
2673#pragma acc data present(ctl,met0,atm,dd,amax)
2674#endif
2675
2676 /* Get box index... */
2677#ifdef _OPENACC
2678#pragma acc parallel loop independent gang vector
2679#else
2680#pragma omp parallel for default(shared)
2681#endif
2682 for (int ip = 0; ip < np; ip++) {
2683 if ((int) atm->q[ctl->qnt_current_subdomain][ip] != -1) {
2684 if ((int) atm->q[ctl->qnt_target_subdomain][ip] == rank)
2685 dd->sort_key[ip] =
2686 (double) ((locate_reg(met0->lon, met0->nx, atm->lon[ip]) *
2687 met0->ny + locate_irr(met0->lat, met0->ny, atm->lat[ip]))
2688 * met0->np + locate_irr(met0->p, met0->np, atm->p[ip]));
2689 else
2690 dd->sort_key[ip] = amax + 1;
2691 } else {
2692 dd->sort_key[ip] = amax + 2;
2693 }
2694 dd->perm[ip] = ip;
2695 }
2696
2697 /* Sorting... */
2698#ifdef THRUST
2699#ifdef _OPENACC
2700#pragma acc host_data use_device(dd->sort_key, dd->perm)
2701#endif
2702 thrustSortWrapper(dd->sort_key, np, dd->perm);
2703#else
2704 size_t *perm_sz = (size_t *) malloc((size_t) np * sizeof(size_t));
2705 if (perm_sz == NULL)
2706 ERRMSG("Out of memory!");
2707#ifdef _OPENACC
2708#pragma acc update self(dd->sort_key[0:np])
2709#endif
2710 gsl_sort_index(perm_sz, dd->sort_key, 1, (size_t) np);
2711 for (int ip = 0; ip < np; ++ip)
2712 dd->perm[ip] = (int) perm_sz[ip];
2713 free(perm_sz);
2714#ifdef _OPENACC
2715#pragma acc update device(dd->perm[0:np])
2716#endif
2717#endif
2718
2719 /* Sort data... */
2720 sort_array(atm->time, dd->perm, dd->tmp, np);
2721 sort_array(atm->p, dd->perm, dd->tmp, np);
2722 sort_array(atm->lon, dd->perm, dd->tmp, np);
2723 sort_array(atm->lat, dd->perm, dd->tmp, np);
2724 for (int iq = 0; iq < ctl->nq; iq++)
2725 sort_array(atm->q[iq], dd->perm, dd->tmp, np);
2726 sort_array(cache->dt, dd->perm, dd->tmp, np);
2727 sort_uvwp(cache, dd->perm, dd->tmp, np);
2728
2729 /* Count kept, outgoing, and invalid particles in one pass... */
2730 int nkeep = 0, nsend = 0, nlost = 0;
2731#ifdef _OPENACC
2732#pragma acc parallel loop reduction(+:nkeep,nsend,nlost) present(atm, ctl)
2733#endif
2734 for (int ip = 0; ip < np; ip++) {
2735 const int current = (int) atm->q[ctl->qnt_current_subdomain][ip];
2736 const int target = (int) atm->q[ctl->qnt_target_subdomain][ip];
2737 if (current == -1)
2738 nlost++;
2739 else if (target == rank)
2740 nkeep++;
2741 else
2742 nsend++;
2743 }
2744
2745 /* Reset sizes... */
2746 *npart = nsend;
2747
2748 if (nlost > 0)
2749 WARN
2750 ("Rank %d: %d particles have subdomain index -1 and will be lost (kept: %d, to_send: %d, total_before: %d)",
2751 rank, nlost, nkeep, nsend, np);
2752
2753 atm->np = nkeep;
2754#ifdef _OPENACC
2755#pragma acc update device(atm->np)
2756#endif
2757
2758 /* Free... */
2759#ifdef _OPENACC
2760#pragma acc exit data delete(amax)
2761#endif
2762}
2763#endif
2764
2765/*****************************************************************************/
2766
2767#ifdef DD
2769 const ctl_t *ctl,
2770 const atm_t *atm,
2771 dd_t *dd,
2772 int *nkeep,
2773 int *npart) {
2774
2775 /* Count particles that must be sent to another rank... */
2776 SELECT_TIMER("DD_COUNT_OUTGOING", "DD");
2777 int rank;
2778 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
2779 const int np = atm->np;
2780#ifdef _OPENACC
2781 int counts[2] = { 0, 0 };
2782 if (np > 0) {
2783 SELECT_TIMER("DD_GPU_CLASSIFY", "DD");
2784#pragma acc data copy(counts[0:2]) present(ctl, atm, dd)
2785 {
2786#pragma acc parallel loop
2787 for (int ip = 0; ip < np; ip++) {
2788 const int current = (int) atm->q[ctl->qnt_current_subdomain][ip];
2789 const int target = (int) atm->q[ctl->qnt_target_subdomain][ip];
2790 int i;
2791 if (current < 0 || target < 0)
2792 continue;
2793 if (target == rank) {
2794#pragma acc atomic capture
2795 {
2796 i = counts[0];
2797 counts[0]++;
2798 }
2799 dd->perm[i] = ip;
2800 } else {
2801#pragma acc atomic capture
2802 {
2803 i = counts[1];
2804 counts[1]++;
2805 }
2806 dd->perm[np - 1 - i] = ip;
2807 }
2808 }
2809 }
2810 }
2811 *nkeep = counts[0];
2812 *npart = counts[1];
2813#else
2814 (void) dd;
2815 int kept = 0;
2816 int nsend = 0;
2817 for (int ip = 0; ip < np; ip++) {
2818 if ((int) atm->q[ctl->qnt_current_subdomain][ip] < 0
2819 || (int) atm->q[ctl->qnt_target_subdomain][ip] < 0)
2820 continue;
2821 if ((int) atm->q[ctl->qnt_target_subdomain][ip] == rank)
2822 kept++;
2823 else
2824 nsend++;
2825 }
2826 *nkeep = kept;
2827 *npart = nsend;
2828#endif
2829}
2830#endif
2831
2832/*****************************************************************************/
2833
2835 const int year,
2836 const int doy,
2837 int *mon,
2838 int *day) {
2839
2840 const int
2841 d0[12] = { 1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 },
2842 d0l[12] = { 1, 32, 61, 92, 122, 153, 183, 214, 245, 275, 306, 336 };
2843
2844 int i;
2845
2846 /* Get month and day... */
2847 if (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)) {
2848 for (i = 11; i > 0; i--)
2849 if (d0l[i] <= doy)
2850 break;
2851 *mon = i + 1;
2852 *day = doy - d0l[i] + 1;
2853 } else {
2854 for (i = 11; i > 0; i--)
2855 if (d0[i] <= doy)
2856 break;
2857 *mon = i + 1;
2858 *day = doy - d0[i] + 1;
2859 }
2860}
2861
2862/*****************************************************************************/
2863
2865 double *fcReal,
2866 double *fcImag,
2867 const int n) {
2868
2869 double data[2 * EX];
2870
2871 /* Check size... */
2872 if (n > EX)
2873 ERRMSG("Too many data points!");
2874
2875 /* Allocate... */
2876 gsl_fft_complex_wavetable *wavetable =
2877 gsl_fft_complex_wavetable_alloc((size_t) n);
2878 gsl_fft_complex_workspace *workspace =
2879 gsl_fft_complex_workspace_alloc((size_t) n);
2880
2881 /* Set data (real, complex)... */
2882 for (int i = 0; i < n; i++) {
2883 data[2 * i] = fcReal[i];
2884 data[2 * i + 1] = fcImag[i];
2885 }
2886
2887 /* Calculate FFT... */
2888 gsl_fft_complex_forward(data, 1, (size_t) n, wavetable, workspace);
2889
2890 /* Copy data... */
2891 for (int i = 0; i < n; i++) {
2892 fcReal[i] = data[2 * i];
2893 fcImag[i] = data[2 * i + 1];
2894 }
2895
2896 /* Free... */
2897 gsl_fft_complex_wavetable_free(wavetable);
2898 gsl_fft_complex_workspace_free(workspace);
2899}
2900
2901/*****************************************************************************/
2902
2904 const double z,
2905 const double lon,
2906 const double lat,
2907 double *x) {
2908
2909 const double radius = z + RE;
2910 const double latrad = DEG2RAD(lat);
2911 const double lonrad = DEG2RAD(lon);
2912 const double coslat = cos(latrad);
2913
2914 x[0] = radius * coslat * cos(lonrad);
2915 x[1] = radius * coslat * sin(lonrad);
2916 x[2] = radius * sin(latrad);
2917}
2918
2919/*****************************************************************************/
2920
2922 const ctl_t *ctl,
2923 const double t,
2924 const int direct,
2925 const char *metbase,
2926 const double dt_met,
2927 char *filename) {
2928
2929 char repl[LEN];
2930
2931 double t6, r;
2932
2933 int year, mon, day, hour, min, sec;
2934
2935 /* Round time to fixed intervals... */
2936 if (direct == -1)
2937 t6 = floor(t / dt_met) * dt_met;
2938 else
2939 t6 = ceil(t / dt_met) * dt_met;
2940
2941 /* Decode time... */
2942 jsec2time(t6, &year, &mon, &day, &hour, &min, &sec, &r);
2943
2944 /* Set filename of MPTRAC meteo files... */
2945 if (ctl->met_clams == 0) {
2946 if (ctl->met_type == 0)
2947 sprintf(filename, "%s_YYYY_MM_DD_HH.nc", metbase);
2948 else if (ctl->met_type == 1)
2949 sprintf(filename, "%s_YYYY_MM_DD_HH.bin", metbase);
2950 else if (ctl->met_type == 2)
2951 sprintf(filename, "%s_YYYY_MM_DD_HH.pck", metbase);
2952 else if (ctl->met_type == 3)
2953 sprintf(filename, "%s_YYYY_MM_DD_HH.zfp", metbase);
2954 else if (ctl->met_type == 4)
2955 sprintf(filename, "%s_YYYY_MM_DD_HH.zstd", metbase);
2956 else if (ctl->met_type == 5)
2957 sprintf(filename, "%s_YYYY_MM_DD_HH.cms", metbase);
2958 else if (ctl->met_type == 7)
2959 sprintf(filename, "%s_YYYY_MM_DD_HH.sz3", metbase);
2960 else if (ctl->met_type == 8)
2961 sprintf(filename, "%s_YYYY_MM_DD_HH.lz4", metbase);
2962 sprintf(repl, "%d", year);
2963 get_met_replace(filename, "YYYY", repl);
2964 sprintf(repl, "%02d", mon);
2965 get_met_replace(filename, "MM", repl);
2966 sprintf(repl, "%02d", day);
2967 get_met_replace(filename, "DD", repl);
2968 sprintf(repl, "%02d", hour);
2969 get_met_replace(filename, "HH", repl);
2970 }
2971
2972 /* Set filename of CLaMS meteo files... */
2973 else {
2974 sprintf(filename, "%s_YYMMDDHH.nc", metbase);
2975 sprintf(repl, "%d", year);
2976 get_met_replace(filename, "YYYY", repl);
2977 sprintf(repl, "%02d", year % 100);
2978 get_met_replace(filename, "YY", repl);
2979 sprintf(repl, "%02d", mon);
2980 get_met_replace(filename, "MM", repl);
2981 sprintf(repl, "%02d", day);
2982 get_met_replace(filename, "DD", repl);
2983 sprintf(repl, "%02d", hour);
2984 get_met_replace(filename, "HH", repl);
2985 }
2986}
2987
2988/*****************************************************************************/
2989
2991 char *orig,
2992 const char *search,
2993 const char *repl) {
2994
2995 char buffer[LEN];
2996
2997 /* Iterate... */
2998 for (int i = 0; i < 3; i++) {
2999
3000 /* Replace sub-string... */
3001 char *ch;
3002 if (!(ch = strstr(orig, search)))
3003 return;
3004 strncpy(buffer, orig, (size_t) (ch - orig));
3005 buffer[ch - orig] = 0;
3006 sprintf(buffer + (ch - orig), "%s%s", repl, ch + strlen(search));
3007 orig[0] = 0;
3008 strcpy(orig, buffer);
3009 }
3010}
3011
3012/*****************************************************************************/
3013
3015 const int met_tropo,
3016 ctl_t *ctl,
3017 const clim_t *clim,
3018 met_t *met,
3019 const double *lons,
3020 const int nx,
3021 const double *lats,
3022 const int ny,
3023 double *pt,
3024 double *zt,
3025 double *tt,
3026 double *qt,
3027 double *o3t,
3028 double *ps,
3029 double *zs) {
3030
3032
3033 ctl->met_tropo = met_tropo;
3034 read_met_tropo(ctl, clim, met);
3035#pragma omp parallel for default(shared) private(ci,cw)
3036 for (int ix = 0; ix < nx; ix++)
3037 for (int iy = 0; iy < ny; iy++) {
3038 intpol_met_space_2d(met, met->pt, lons[ix], lats[iy],
3039 &pt[iy * nx + ix], ci, cw, 1);
3040 intpol_met_space_2d(met, met->ps, lons[ix], lats[iy],
3041 &ps[iy * nx + ix], ci, cw, 0);
3042 intpol_met_space_2d(met, met->zs, lons[ix], lats[iy],
3043 &zs[iy * nx + ix], ci, cw, 0);
3044 intpol_met_space_3d(met, met->z, pt[iy * nx + ix], lons[ix],
3045 lats[iy], &zt[iy * nx + ix], ci, cw, 1);
3046 intpol_met_space_3d(met, met->t, pt[iy * nx + ix], lons[ix],
3047 lats[iy], &tt[iy * nx + ix], ci, cw, 0);
3048 intpol_met_space_3d(met, met->h2o, pt[iy * nx + ix], lons[ix],
3049 lats[iy], &qt[iy * nx + ix], ci, cw, 0);
3050 intpol_met_space_3d(met, met->o3, pt[iy * nx + ix], lons[ix],
3051 lats[iy], &o3t[iy * nx + ix], ci, cw, 0);
3052 }
3053}
3054
3055/*****************************************************************************/
3056
3058 const double *lons,
3059 const int nlon,
3060 const double *lats,
3061 const int nlat,
3062 const double lon,
3063 const double lat,
3064 double *lon2,
3065 double *lat2) {
3066
3067 /* Check longitude... */
3068 *lon2 = FMOD(lon, 360.);
3069 if (*lon2 < lons[0])
3070 *lon2 += 360;
3071 else if (*lon2 > lons[nlon - 1])
3072 *lon2 -= 360;
3073
3074 /* Check latitude... */
3075 *lat2 = lat;
3076 if (lats[0] < lats[nlat - 1])
3077 *lat2 = MIN(MAX(*lat2, lats[0]), lats[nlat - 1]);
3078 else
3079 *lat2 = MIN(MAX(*lat2, lats[nlat - 1]), lats[0]);
3080}
3081
3082/*****************************************************************************/
3083
3085 const double *lons,
3086 const int nlon,
3087 const double *lats,
3088 const int nlat,
3089 const double lon,
3090 const double lat,
3091 double *lon2,
3092 double *lat2) {
3093
3094 *lon2 = lon;
3095 if (lons[0] < lons[nlon - 1])
3096 *lon2 = MIN(MAX(lon, lons[0]), lons[nlon - 1]);
3097 else
3098 *lon2 = MIN(MAX(lon, lons[nlon - 1]), lons[0]);
3099
3100 *lat2 = lat;
3101 if (lats[0] < lats[nlat - 1])
3102 *lat2 = MIN(MAX(lat, lats[0]), lats[nlat - 1]);
3103 else
3104 *lat2 = MIN(MAX(lat, lats[nlat - 1]), lats[0]);
3105}
3106
3107
3108/*****************************************************************************/
3109
3111 const met_t *met0,
3112 float heights0[EX][EY][EP],
3113 float array0[EX][EY][EP],
3114 const met_t *met1,
3115 float heights1[EX][EY][EP],
3116 float array1[EX][EY][EP],
3117 const double ts,
3118 const double height,
3119 const double lon,
3120 const double lat,
3121 double *var,
3122 int *ci,
3123 double *cw,
3124 const int init) {
3125
3126 if (init) {
3127
3128 /* Check longitude and latitude... */
3129 double lon2, lat2;
3130
3131 if (met0->coord_type == 0)
3132 intpol_check_lon_lat(met0->lon, met0->nx, met0->lat, met0->ny, lon, lat,
3133 &lon2, &lat2);
3134 else
3135 intpol_check_cartesian(met0->lon, met0->nx, met0->lat, met0->ny, lon,
3136 lat, &lon2, &lat2);
3137
3138 /* Get horizontal indizes... */
3139 ci[0] = locate_reg(met0->lon, met0->nx, lon2);
3140 ci[1] = locate_irr(met0->lat, met0->ny, lat2);
3141
3142 /* Locate the vertical indizes for each edge of the column... */
3143 int ind[2][4];
3144 locate_vert(heights0, met0->npl, ci[0], ci[1], height, ind[0]);
3145 locate_vert(heights1, met1->npl, ci[0], ci[1], height, ind[1]);
3146
3147 /* Find minimum and maximum indizes... */
3148 ci[2] = ind[0][0];
3149 int k_max = ind[0][0];
3150 for (int i = 0; i < 2; i++)
3151 for (int j = 0; j < 4; j++) {
3152 if (ci[2] > ind[i][j])
3153 ci[2] = ind[i][j];
3154 if (k_max < ind[i][j])
3155 k_max = ind[i][j];
3156 }
3157
3158 /* Get weighting factors for time, longitude and latitude... */
3159 cw[3] = (ts - met0->time) / (met1->time - met0->time);
3160 cw[0] = (lon2 - met0->lon[ci[0]]) /
3161 (met0->lon[ci[0] + 1] - met0->lon[ci[0]]);
3162 cw[1] = (lat2 - met0->lat[ci[1]]) /
3163 (met0->lat[ci[1] + 1] - met0->lat[ci[1]]);
3164
3165 /* Interpolate in time at the lowest level... */
3166 double height00 = cw[3] * (heights1[ci[0]][ci[1]][ci[2]]
3167 - heights0[ci[0]][ci[1]][ci[2]])
3168 + heights0[ci[0]][ci[1]][ci[2]];
3169 double height01 = cw[3] * (heights1[ci[0]][ci[1] + 1][ci[2]]
3170 - heights0[ci[0]][ci[1] + 1][ci[2]])
3171 + heights0[ci[0]][ci[1] + 1][ci[2]];
3172 double height10 = cw[3] * (heights1[ci[0] + 1][ci[1]][ci[2]]
3173 - heights0[ci[0] + 1][ci[1]][ci[2]])
3174 + heights0[ci[0] + 1][ci[1]][ci[2]];
3175 double height11 = cw[3] * (heights1[ci[0] + 1][ci[1] + 1][ci[2]]
3176 - heights0[ci[0] + 1][ci[1] + 1][ci[2]])
3177 + heights0[ci[0] + 1][ci[1] + 1][ci[2]];
3178
3179 /* Interpolate in latitude direction... */
3180 double height0 = cw[1] * (height01 - height00) + height00;
3181 double height1 = cw[1] * (height11 - height10) + height10;
3182
3183 /* Interpolate in longitude direction... */
3184 double height_bot = cw[0] * (height1 - height0) + height0;
3185
3186 /* Interpolate in time at the upper level... */
3187 height00 = cw[3] * (heights1[ci[0]][ci[1]][ci[2] + 1]
3188 - heights0[ci[0]][ci[1]][ci[2] + 1])
3189 + heights0[ci[0]][ci[1]][ci[2] + 1];
3190 height01 = cw[3] * (heights1[ci[0]][ci[1] + 1][ci[2] + 1]
3191 - heights0[ci[0]][ci[1] + 1][ci[2] + 1])
3192 + heights0[ci[0]][ci[1] + 1][ci[2] + 1];
3193 height10 = cw[3] * (heights1[ci[0] + 1][ci[1]][ci[2] + 1]
3194 - heights0[ci[0] + 1][ci[1]][ci[2] + 1])
3195 + heights0[ci[0] + 1][ci[1]][ci[2] + 1];
3196 height11 = cw[3] * (heights1[ci[0] + 1][ci[1] + 1][ci[2] + 1]
3197 - heights0[ci[0] + 1][ci[1] + 1][ci[2] + 1])
3198 + heights0[ci[0] + 1][ci[1] + 1][ci[2] + 1];
3199
3200 /* Interpolate in latitude direction... */
3201 height0 = cw[1] * (height01 - height00) + height00;
3202 height1 = cw[1] * (height11 - height10) + height10;
3203
3204 /* Interpolate in longitude direction... */
3205 double height_top = cw[0] * (height1 - height0) + height0;
3206
3207 /* Search at higher levels if height is not in box... */
3208 while (((heights0[0][0][0] > heights0[0][0][1]) &&
3209 ((height_bot <= height) || (height_top > height))
3210 && (height_bot >= height) && (ci[2] < k_max))
3211 ||
3212 ((heights0[0][0][0] < heights0[0][0][1]) &&
3213 ((height_bot >= height) || (height_top < height))
3214 && (height_bot <= height) && (ci[2] < k_max))
3215 ) {
3216
3217 ci[2]++;
3218 height_bot = height_top;
3219
3220 /* Interpolate in time at the next level... */
3221 height00 = cw[3] * (heights1[ci[0]][ci[1]][ci[2] + 1]
3222 - heights0[ci[0]][ci[1]][ci[2] + 1])
3223 + heights0[ci[0]][ci[1]][ci[2] + 1];
3224 height01 = cw[3] * (heights1[ci[0]][ci[1] + 1][ci[2] + 1]
3225 - heights0[ci[0]][ci[1] + 1][ci[2] + 1])
3226 + heights0[ci[0]][ci[1] + 1][ci[2] + 1];
3227 height10 = cw[3] * (heights1[ci[0] + 1][ci[1]][ci[2] + 1]
3228 - heights0[ci[0] + 1][ci[1]][ci[2] + 1])
3229 + heights0[ci[0] + 1][ci[1]][ci[2] + 1];
3230 height11 = cw[3] * (heights1[ci[0] + 1][ci[1] + 1][ci[2] + 1]
3231 - heights0[ci[0] + 1][ci[1] + 1][ci[2] + 1])
3232 + heights0[ci[0] + 1][ci[1] + 1][ci[2] + 1];
3233
3234 /* Interpolate in latitude direction... */
3235 height0 = cw[1] * (height01 - height00) + height00;
3236 height1 = cw[1] * (height11 - height10) + height10;
3237
3238 /* Interpolate in longitude direction... */
3239 height_top = cw[0] * (height1 - height0) + height0;
3240 }
3241
3242 /* Get vertical weighting factors... */
3243 cw[2] = (height - height_bot)
3244 / (height_top - height_bot);
3245 }
3246
3247 /* Calculate the needed array values... */
3248 const double array000 = cw[3] * (array1[ci[0]][ci[1]][ci[2]]
3249 - array0[ci[0]][ci[1]][ci[2]])
3250 + array0[ci[0]][ci[1]][ci[2]];
3251 const double array100 = cw[3] * (array1[ci[0] + 1][ci[1]][ci[2]]
3252 - array0[ci[0] + 1][ci[1]][ci[2]])
3253 + array0[ci[0] + 1][ci[1]][ci[2]];
3254 const double array010 = cw[3] * (array1[ci[0]][ci[1] + 1][ci[2]]
3255 - array0[ci[0]][ci[1] + 1][ci[2]])
3256 + array0[ci[0]][ci[1] + 1][ci[2]];
3257 const double array110 = cw[3] * (array1[ci[0] + 1][ci[1] + 1][ci[2]]
3258 - array0[ci[0] + 1][ci[1] + 1][ci[2]])
3259 + array0[ci[0] + 1][ci[1] + 1][ci[2]];
3260 const double array001 = cw[3] * (array1[ci[0]][ci[1]][ci[2] + 1]
3261 - array0[ci[0]][ci[1]][ci[2] + 1])
3262 + array0[ci[0]][ci[1]][ci[2] + 1];
3263 const double array101 = cw[3] * (array1[ci[0] + 1][ci[1]][ci[2] + 1]
3264 - array0[ci[0] + 1][ci[1]][ci[2] + 1])
3265 + array0[ci[0] + 1][ci[1]][ci[2] + 1];
3266 const double array011 = cw[3] * (array1[ci[0]][ci[1] + 1][ci[2] + 1]
3267 - array0[ci[0]][ci[1] + 1][ci[2] + 1])
3268 + array0[ci[0]][ci[1] + 1][ci[2] + 1];
3269 const double array111 = cw[3] * (array1[ci[0] + 1][ci[1] + 1][ci[2] + 1]
3270 - array0[ci[0] + 1][ci[1] + 1][ci[2] + 1])
3271 + array0[ci[0] + 1][ci[1] + 1][ci[2] + 1];
3272
3273 const double array00 = cw[0] * (array100 - array000) + array000;
3274 const double array10 = cw[0] * (array110 - array010) + array010;
3275 const double array01 = cw[0] * (array101 - array001) + array001;
3276 const double array11 = cw[0] * (array111 - array011) + array011;
3277
3278 const double aux0 = cw[1] * (array10 - array00) + array00;
3279 const double aux1 = cw[1] * (array11 - array01) + array01;
3280
3281 /* Interpolate vertically... */
3282 *var = cw[2] * (aux1 - aux0) + aux0;
3283}
3284
3285/*****************************************************************************/
3286
3288 const met_t *met,
3289 float array[EX][EY][EP],
3290 const double p,
3291 const double lon,
3292 const double lat,
3293 double *var,
3294 int *ci,
3295 double *cw,
3296 const int init) {
3297
3298 /* Initialize interpolation... */
3299 if (init) {
3300
3301 /* Check longitude and latitude... */
3302 double lon2, lat2;
3303
3304 if (met->coord_type == 0)
3305 intpol_check_lon_lat(met->lon, met->nx, met->lat, met->ny, lon, lat,
3306 &lon2, &lat2);
3307 else
3308 intpol_check_cartesian(met->lon, met->nx, met->lat, met->ny, lon, lat,
3309 &lon2, &lat2);
3310
3311 /* Get interpolation indices... */
3312 ci[0] = locate_irr(met->p, met->np, p);
3313 ci[1] = locate_reg(met->lon, met->nx, lon2);
3314 ci[2] = locate_irr(met->lat, met->ny, lat2);
3315
3316 /* Get interpolation weights... */
3317 cw[0] = (met->p[ci[0] + 1] - p)
3318 / (met->p[ci[0] + 1] - met->p[ci[0]]);
3319 cw[1] = (met->lon[ci[1] + 1] - lon2)
3320 / (met->lon[ci[1] + 1] - met->lon[ci[1]]);
3321 cw[2] = (met->lat[ci[2] + 1] - lat2)
3322 / (met->lat[ci[2] + 1] - met->lat[ci[2]]);
3323 }
3324
3325 /* Interpolate vertically... */
3326 const double aux00 =
3327 cw[0] * (array[ci[1]][ci[2]][ci[0]] - array[ci[1]][ci[2]][ci[0] + 1])
3328 + array[ci[1]][ci[2]][ci[0] + 1];
3329 const double aux01 =
3330 cw[0] * (array[ci[1]][ci[2] + 1][ci[0]] -
3331 array[ci[1]][ci[2] + 1][ci[0] + 1])
3332 + array[ci[1]][ci[2] + 1][ci[0] + 1];
3333 const double aux10 =
3334 cw[0] * (array[ci[1] + 1][ci[2]][ci[0]] -
3335 array[ci[1] + 1][ci[2]][ci[0] + 1])
3336 + array[ci[1] + 1][ci[2]][ci[0] + 1];
3337 const double aux11 =
3338 cw[0] * (array[ci[1] + 1][ci[2] + 1][ci[0]] -
3339 array[ci[1] + 1][ci[2] + 1][ci[0] + 1])
3340 + array[ci[1] + 1][ci[2] + 1][ci[0] + 1];
3341
3342 /* Interpolate horizontally... */
3343 const double aux0 = cw[2] * (aux00 - aux01) + aux01;
3344 const double aux1 = cw[2] * (aux10 - aux11) + aux11;
3345 *var = cw[1] * (aux0 - aux1) + aux1;
3346}
3347
3348/*****************************************************************************/
3349
3351 const met_t *met,
3352 float array[EX][EY],
3353 const double lon,
3354 const double lat,
3355 double *var,
3356 int *ci,
3357 double *cw,
3358 const int init) {
3359
3360 /* Initialize interpolation... */
3361 if (init) {
3362
3363 /* Check longitude and latitude... */
3364 double lon2, lat2;
3365
3366 if (met->coord_type == 0)
3367 intpol_check_lon_lat(met->lon, met->nx, met->lat, met->ny, lon, lat,
3368 &lon2, &lat2);
3369 else
3370 intpol_check_cartesian(met->lon, met->nx, met->lat, met->ny, lon, lat,
3371 &lon2, &lat2);
3372
3373
3374 /* Get interpolation indices... */
3375 ci[1] = locate_reg(met->lon, met->nx, lon2);
3376 ci[2] = locate_irr(met->lat, met->ny, lat2);
3377
3378 /* Get interpolation weights... */
3379 cw[1] = (met->lon[ci[1] + 1] - lon2)
3380 / (met->lon[ci[1] + 1] - met->lon[ci[1]]);
3381 cw[2] = (met->lat[ci[2] + 1] - lat2)
3382 / (met->lat[ci[2] + 1] - met->lat[ci[2]]);
3383 }
3384
3385 /* Set variables... */
3386 const double aux00 = array[ci[1]][ci[2]];
3387 const double aux01 = array[ci[1]][ci[2] + 1];
3388 const double aux10 = array[ci[1] + 1][ci[2]];
3389 const double aux11 = array[ci[1] + 1][ci[2] + 1];
3390
3391 /* Interpolate horizontally... */
3392 if (isfinite(aux00) && isfinite(aux01)
3393 && isfinite(aux10) && isfinite(aux11)) {
3394 const double aux0 = cw[2] * (aux00 - aux01) + aux01;
3395 const double aux1 = cw[2] * (aux10 - aux11) + aux11;
3396 *var = cw[1] * (aux0 - aux1) + aux1;
3397 } else {
3398 if (cw[2] < 0.5) {
3399 if (cw[1] < 0.5)
3400 *var = aux11;
3401 else
3402 *var = aux01;
3403 } else {
3404 if (cw[1] < 0.5)
3405 *var = aux10;
3406 else
3407 *var = aux00;
3408 }
3409 }
3410}
3411
3412/*****************************************************************************/
3413
3415 const met_t *met0,
3416 float array0[EX][EY][EP],
3417 const met_t *met1,
3418 float array1[EX][EY][EP],
3419 const double ts,
3420 const double p,
3421 const double lon,
3422 const double lat,
3423 double *var,
3424 int *ci,
3425 double *cw,
3426 const int init) {
3427
3428 double var0, var1;
3429
3430 /* Spatial interpolation... */
3431 intpol_met_space_3d(met0, array0, p, lon, lat, &var0, ci, cw, init);
3432 intpol_met_space_3d(met1, array1, p, lon, lat, &var1, ci, cw, 0);
3433
3434 /* Get weighting factor... */
3435 const double wt = (met1->time - ts) / (met1->time - met0->time);
3436
3437 /* Interpolate... */
3438 *var = wt * (var0 - var1) + var1;
3439}
3440
3441/*****************************************************************************/
3442
3444 const met_t *met0,
3445 float array0[EX][EY],
3446 const met_t *met1,
3447 float array1[EX][EY],
3448 const double ts,
3449 const double lon,
3450 const double lat,
3451 double *var,
3452 int *ci,
3453 double *cw,
3454 const int init) {
3455
3456 double var0, var1;
3457
3458 /* Spatial interpolation... */
3459 intpol_met_space_2d(met0, array0, lon, lat, &var0, ci, cw, init);
3460 intpol_met_space_2d(met1, array1, lon, lat, &var1, ci, cw, 0);
3461
3462 /* Get weighting factor... */
3463 const double wt = (met1->time - ts) / (met1->time - met0->time);
3464
3465 /* Interpolate... */
3466 if (isfinite(var0) && isfinite(var1))
3467 *var = wt * (var0 - var1) + var1;
3468 else if (wt < 0.5)
3469 *var = var1;
3470 else
3471 *var = var0;
3472}
3473
3474/*****************************************************************************/
3475
3477 const double time0,
3478 float array0[EX][EY],
3479 const double time1,
3480 float array1[EX][EY],
3481 const double lons[EX],
3482 const double lats[EY],
3483 const int nlon,
3484 const int nlat,
3485 const double time,
3486 const double lon,
3487 const double lat,
3488 const int method,
3489 double *var,
3490 double *sigma) {
3491
3492 double mean = 0;
3493
3494 int n = 0;
3495
3496 /* Check longitude and latitude... */
3497 double lon2, lat2;
3498 intpol_check_lon_lat(lons, nlon, lats, nlat, lon, lat, &lon2, &lat2);
3499
3500 /* Get indices... */
3501 const int ix = locate_reg(lons, (int) nlon, lon2);
3502 const int iy = locate_irr(lats, (int) nlat, lat2);
3503
3504 /* Calculate standard deviation... */
3505 *sigma = 0;
3506 for (int dx = 0; dx < 2; dx++)
3507 for (int dy = 0; dy < 2; dy++) {
3508 if (isfinite(array0[ix + dx][iy + dy])) {
3509 mean += array0[ix + dx][iy + dy];
3510 *sigma += SQR(array0[ix + dx][iy + dy]);
3511 n++;
3512 }
3513 if (isfinite(array1[ix + dx][iy + dy])) {
3514 mean += array1[ix + dx][iy + dy];
3515 *sigma += SQR(array1[ix + dx][iy + dy]);
3516 n++;
3517 }
3518 }
3519 if (n > 0)
3520 *sigma = sqrt(MAX(*sigma / n - SQR(mean / n), 0.0));
3521
3522 /* Linear interpolation... */
3523 if (method == 1 && isfinite(array0[ix][iy])
3524 && isfinite(array0[ix][iy + 1])
3525 && isfinite(array0[ix + 1][iy])
3526 && isfinite(array0[ix + 1][iy + 1])
3527 && isfinite(array1[ix][iy])
3528 && isfinite(array1[ix][iy + 1])
3529 && isfinite(array1[ix + 1][iy])
3530 && isfinite(array1[ix + 1][iy + 1])) {
3531
3532 const double aux00 = LIN(lons[ix], array0[ix][iy],
3533 lons[ix + 1], array0[ix + 1][iy], lon2);
3534 const double aux01 = LIN(lons[ix], array0[ix][iy + 1],
3535 lons[ix + 1], array0[ix + 1][iy + 1], lon2);
3536 const double aux0 = LIN(lats[iy], aux00, lats[iy + 1], aux01, lat2);
3537
3538 const double aux10 = LIN(lons[ix], array1[ix][iy],
3539 lons[ix + 1], array1[ix + 1][iy], lon2);
3540 const double aux11 = LIN(lons[ix], array1[ix][iy + 1],
3541 lons[ix + 1], array1[ix + 1][iy + 1], lon2);
3542 const double aux1 = LIN(lats[iy], aux10, lats[iy + 1], aux11, lat2);
3543
3544 *var = LIN(time0, aux0, time1, aux1, time);
3545 }
3546
3547 /* Nearest neighbor interpolation... */
3548 else {
3549 const double aux00 = NN(lons[ix], array0[ix][iy],
3550 lons[ix + 1], array0[ix + 1][iy], lon2);
3551 const double aux01 = NN(lons[ix], array0[ix][iy + 1],
3552 lons[ix + 1], array0[ix + 1][iy + 1], lon2);
3553 const double aux0 = NN(lats[iy], aux00, lats[iy + 1], aux01, lat2);
3554
3555 const double aux10 = NN(lons[ix], array1[ix][iy],
3556 lons[ix + 1], array1[ix + 1][iy], lon2);
3557 const double aux11 = NN(lons[ix], array1[ix][iy + 1],
3558 lons[ix + 1], array1[ix + 1][iy + 1], lon2);
3559 const double aux1 = NN(lats[iy], aux10, lats[iy + 1], aux11, lat2);
3560
3561 *var = NN(time0, aux0, time1, aux1, time);
3562 }
3563}
3564
3565/*****************************************************************************/
3566
3568 const double jsec,
3569 int *year,
3570 int *mon,
3571 int *day,
3572 int *hour,
3573 int *min,
3574 int *sec,
3575 double *remain) {
3576
3577 struct tm t0, *t1;
3578
3579 t0.tm_year = 100;
3580 t0.tm_mon = 0;
3581 t0.tm_mday = 1;
3582 t0.tm_hour = 0;
3583 t0.tm_min = 0;
3584 t0.tm_sec = 0;
3585
3586 const time_t jsec0 = (time_t) jsec + timegm(&t0);
3587 t1 = gmtime(&jsec0);
3588
3589 *year = t1->tm_year + 1900;
3590 *mon = t1->tm_mon + 1;
3591 *day = t1->tm_mday;
3592 *hour = t1->tm_hour;
3593 *min = t1->tm_min;
3594 *sec = t1->tm_sec;
3595 *remain = jsec - floor(jsec);
3596}
3597
3598/*****************************************************************************/
3599
3601 const double kz[EP],
3602 const double kw[EP],
3603 const int nk,
3604 const double p) {
3605
3606 /* Check number of data points... */
3607 if (nk < 2)
3608 return 1.0;
3609
3610 /* Get altitude... */
3611 const double z = Z(p);
3612
3613 /* Get weighting factor... */
3614 if (z < kz[0])
3615 return kw[0];
3616 else if (z > kz[nk - 1])
3617 return kw[nk - 1];
3618 else {
3619 const int idx = locate_irr(kz, nk, z);
3620 return LIN(kz[idx], kw[idx], kz[idx + 1], kw[idx + 1], z);
3621 }
3622}
3623
3624/*****************************************************************************/
3625
3627 const double t,
3628 const double h2o) {
3629
3630 /*
3631 Calculate moist adiabatic lapse rate [K/km] from temperature [K]
3632 and water vapor volume mixing ratio [1].
3633
3634 Reference: https://en.wikipedia.org/wiki/Lapse_rate
3635 */
3636
3637 const double a = RA * SQR(t), r = SH(h2o) / (1. - SH(h2o));
3638
3639 return 1e3 * G0 * (a + LV * r * t) / (CPD * a + SQR(LV) * r * EPS);
3640}
3641
3642/*****************************************************************************/
3643
3645 ctl_t *ctl) {
3646
3647 if (0 == ctl->met_press_level_def) {
3648
3649 ERRMSG
3650 ("MET_PRESS_LEVEL_DEF=0 is disabled. Use 3 for the extended L137 set.");
3651
3652 } else if (1 == ctl->met_press_level_def) {
3653
3654 ERRMSG
3655 ("MET_PRESS_LEVEL_DEF=1 is disabled. Use 4 for the extended L91 set.");
3656
3657 } else if (2 == ctl->met_press_level_def) {
3658
3659 ERRMSG
3660 ("MET_PRESS_LEVEL_DEF=2 is disabled. Use 5 for the extended L60 set.");
3661
3662 } else if (3 == ctl->met_press_level_def) {
3663
3664 ctl->met_np = 147;
3665
3666 const double press[147] = {
3667 0.0200, 0.0310, 0.0467, 0.0683, 0.0975, 0.1361, 0.1861, 0.2499,
3668 0.3299, 0.4288, 0.5496, 0.6952, 0.8690, 1.0742, 1.3143, 1.5928, 1.9134,
3669 2.2797, 2.6954, 3.1642, 3.6898, 4.2759, 4.9262, 5.6441, 6.4334, 7.2974,
3670 8.2397, 9.2634, 10.3720, 11.5685, 12.8561, 14.2377, 15.7162, 17.2945,
3671 18.9752, 20.7610, 22.6543, 24.6577, 26.7735, 29.0039, 31.3512, 33.8174,
3672 36.4047, 39.1149, 41.9493, 44.9082, 47.9915, 51.1990, 54.5299, 57.9834,
3673 61.5607, 65.2695, 69.1187, 73.1187, 77.2810, 81.6182, 86.1450, 90.8774,
3674 95.8280, 101.0047, 106.4153, 112.0681, 117.9714, 124.1337, 130.5637,
3675 137.2703, 144.2624, 151.5493, 159.1403, 167.0450, 175.2731, 183.8344,
3676 192.7389, 201.9969, 211.6186, 221.6146, 231.9954, 242.7719, 253.9549,
3677 265.5556, 277.5852, 290.0548, 302.9762, 316.3607, 330.2202, 344.5663,
3678 359.4111, 374.7666, 390.6450, 407.0583, 424.0190, 441.5395, 459.6321,
3679 478.3096, 497.5845, 517.4198, 537.7195, 558.3430, 579.1926, 600.1668,
3680 621.1624, 642.0764, 662.8084, 683.2620, 703.3467, 722.9795, 742.0855,
3681 760.5996, 778.4661, 795.6396, 812.0847, 827.7756, 842.6959, 856.8376,
3682 870.2004, 882.7910, 894.6222, 905.7116, 916.0815, 925.7571, 934.7666,
3683 943.1399, 950.9082, 958.1037, 964.7584, 970.9046, 976.5737, 981.7968,
3684 986.6036, 991.0230, 995.0824, 998.8081, 1002.2250, 1005.3562, 1008.2239,
3685 1010.8487, 1013.25, 1016.37, 1019.49, 1022.61, 1025.73, 1028.85,
3686 1031.97,
3687 1035.09, 1038.21, 1041.33, 1044.45
3688 };
3689
3690 for (int ip = 0; ip < ctl->met_np; ip++)
3691 ctl->met_p[ctl->met_np - ip - 1] = press[ip];
3692
3693 } else if (4 == ctl->met_press_level_def) {
3694
3695 ctl->met_np = 101;
3696
3697 const double press[101] = {
3698 0.0200, 0.0398, 0.0739, 0.1291, 0.2141, 0.3395, 0.5175, 0.7617,
3699 1.0872, 1.5099, 2.0464, 2.7136, 3.5282, 4.5069, 5.6652, 7.0181,
3700 8.5795, 10.3617, 12.3759, 14.6316, 17.1371, 19.8987, 22.9216, 26.2090,
3701 29.7630, 33.5843, 37.6720, 42.0242, 46.6378, 51.5086, 56.6316, 61.9984,
3702 67.5973, 73.4150, 79.4434, 85.7016, 92.2162, 99.0182, 106.1445,
3703 113.6382,
3704 121.5502, 129.9403, 138.8558, 148.3260, 158.3816, 169.0545, 180.3786,
3705 192.3889, 205.1222, 218.6172, 232.9140, 248.0547, 264.0833, 281.0456,
3706 298.9895, 317.9651, 338.0245, 359.2221, 381.6144, 405.2606, 430.2069,
3707 456.4813, 483.8505, 512.0662, 540.8577, 569.9401, 599.0310, 627.9668,
3708 656.6129, 684.8491, 712.5573, 739.5739, 765.7697, 791.0376, 815.2774,
3709 838.3507, 860.1516, 880.6080, 899.6602, 917.2205, 933.2247, 947.6584,
3710 960.5245, 971.8169, 981.5301, 989.7322, 996.8732, 1002.8013,
3711 1007.4431, 1010.8487, 1013.25, 1016.37, 1019.49, 1022.61, 1025.73,
3712 1028.85, 1031.97,
3713 1035.09, 1038.21, 1041.33, 1044.45
3714 };
3715
3716 for (int ip = 0; ip < ctl->met_np; ip++)
3717 ctl->met_p[ctl->met_np - ip - 1] = press[ip];
3718
3719 } else if (5 == ctl->met_press_level_def) {
3720
3721 ctl->met_np = 62;
3722
3723 const double press[62] = {
3724 0.01, 0.1361, 0.2499, 0.4288, 0.6952, 1.0742,
3725 2.2797, 3.1642, 4.2759, 7.2974, 9.2634, 11.5685, 14.2377, 20.761,
3726 24.6577, 33.8174, 39.1149, 51.199, 57.9834, 73.1187, 81.6182,
3727 90.8774, 101.005, 112.068, 124.134, 137.27, 151.549, 167.045, 183.834,
3728 201.997, 221.615, 242.772, 265.556, 290.055, 316.361, 344.566, 374.767,
3729 407.058, 441.539, 478.31, 517.42, 558.343, 600.167, 683.262, 722.979,
3730 760.6, 795.64, 827.776, 856.838, 882.791, 905.712, 925.757, 943.14,
3731 958.104, 972.495, 986.886, 1001.28, 1015.67, 1030.06, 1034.86, 1039.65,
3732 1044.45
3733 };
3734
3735 for (int ip = 0; ip < ctl->met_np; ip++)
3736 ctl->met_p[ctl->met_np - ip - 1] = press[ip];
3737
3738 } else if (6 == ctl->met_press_level_def) {
3739
3740 ctl->met_np = 137;
3741
3742 const double press[137] = {
3743 0.01, 0.02, 0.031, 0.0467, 0.0683, 0.0975, 0.1361, 0.1861,
3744 0.2499, 0.3299, 0.4288, 0.5496, 0.6952, 0.869, 1.0742,
3745 1.3143, 1.5928, 1.9134, 2.2797, 2.6954, 3.1642, 3.6898,
3746 4.2759, 4.9262, 5.6441, 6.4334, 7.2974, 8.2397, 9.2634,
3747 10.372, 11.5685, 12.8561, 14.2377, 15.7162, 17.2945, 18.9752,
3748 20.761, 22.6543, 24.6577, 26.7735, 29.0039, 31.3512, 33.8174,
3749 36.4047, 39.1149, 41.9493, 44.9082, 47.9915, 51.199, 54.5299,
3750 57.9834, 61.5607, 65.2695, 69.1187, 73.1187, 77.281, 81.6182,
3751 86.145, 90.8774, 95.828, 101.005, 106.415, 112.068, 117.971,
3752 124.134, 130.564, 137.27, 144.262, 151.549, 159.14, 167.045,
3753 175.273, 183.834, 192.739, 201.997, 211.619, 221.615, 231.995,
3754 242.772, 253.955, 265.556, 277.585, 290.055, 302.976, 316.361,
3755 330.22, 344.566, 359.411, 374.767, 390.645, 407.058, 424.019,
3756 441.539, 459.632, 478.31, 497.584, 517.42, 537.72, 558.343,
3757 579.193, 600.167, 621.162, 642.076, 662.808, 683.262, 703.347,
3758 722.979, 742.086, 760.6, 778.466, 795.64, 812.085, 827.776,
3759 842.696, 856.838, 870.2, 882.791, 894.622, 905.712, 916.081,
3760 925.757, 934.767, 943.14, 950.908, 958.104, 965.299, 972.495,
3761 979.69, 986.886, 994.081, 1001.28, 1008.47, 1015.67, 1022.86,
3762 1030.06, 1037.25, 1044.45
3763 };
3764
3765 for (int ip = 0; ip < ctl->met_np; ip++)
3766 ctl->met_p[ctl->met_np - ip - 1] = press[ip];
3767
3768 } else if (7 == ctl->met_press_level_def) {
3769
3770 ctl->met_np = 59;
3771
3772 const double press[59] = {
3773 0.1, 0.2, 0.3843, 0.6365, 0.9564, 1.3448, 1.8058, 2.3478,
3774 2.985, 3.7397, 4.6462, 5.7565, 7.1322, 8.8366, 10.9483,
3775 13.5647, 16.8064, 20.8227, 25.7989, 31.9642, 39.6029, 49.0671,
3776 60.1802, 73.0663, 87.7274, 104.229, 122.614, 142.902, 165.089,
3777 189.147, 215.025, 242.652, 272.059, 303.217, 336.044, 370.407,
3778 406.133, 443.009, 480.791, 519.209, 557.973, 596.777, 635.306,
3779 673.24, 710.263, 746.063, 780.346, 812.83, 843.263, 871.42,
3780 897.112, 920.189, 940.551, 958.148, 975.744, 993.341, 1010.94,
3781 1028.53, 1046.13
3782 };
3783
3784 for (int ip = 0; ip < ctl->met_np; ip++)
3785 ctl->met_p[ctl->met_np - ip - 1] = press[ip];
3786
3787 } else {
3788 ERRMSG("Use values between 3 and 7.");
3789 }
3790
3791 if (ctl->met_np > EP)
3792 ERRMSG("Recompile with larger EP to use this pressure level definition!");
3793}
3794
3795/*****************************************************************************/
3796
3798 const double *xx,
3799 const int n,
3800 const double x) {
3801
3802 int ilo = 0;
3803 int ihi = n - 1;
3804 int i = (ihi + ilo) >> 1;
3805
3806 if (xx[i] < xx[i + 1])
3807 while (ihi > ilo + 1) {
3808 i = (ihi + ilo) >> 1;
3809 if (xx[i] > x)
3810 ihi = i;
3811 else
3812 ilo = i;
3813 } else
3814 while (ihi > ilo + 1) {
3815 i = (ihi + ilo) >> 1;
3816 if (xx[i] <= x)
3817 ihi = i;
3818 else
3819 ilo = i;
3820 }
3821
3822 return ilo;
3823}
3824
3825/*****************************************************************************/
3826
3828 const float *xx,
3829 const int n,
3830 const double x,
3831 const int ig) {
3832
3833 int ilo = 0;
3834 int ihi = n - 1;
3835 int i = (ihi + ilo) >> 1;
3836
3837 if ((xx[ig] <= x && x < xx[ig + 1]) || (xx[ig] >= x && x > xx[ig + 1]))
3838 return ig;
3839
3840 if (xx[i] < xx[i + 1])
3841 while (ihi > ilo + 1) {
3842 i = (ihi + ilo) >> 1;
3843 if (xx[i] > x)
3844 ihi = i;
3845 else
3846 ilo = i;
3847 } else
3848 while (ihi > ilo + 1) {
3849 i = (ihi + ilo) >> 1;
3850 if (xx[i] <= x)
3851 ihi = i;
3852 else
3853 ilo = i;
3854 }
3855
3856 return ilo;
3857}
3858
3859/*****************************************************************************/
3860
3862 const double *xx,
3863 const int n,
3864 const double x) {
3865
3866 /* Calculate index... */
3867 const int i = (int) ((x - xx[0]) / (xx[1] - xx[0]));
3868
3869 /* Check range... */
3870 if (i < 0)
3871 return 0;
3872 else if (i > n - 2)
3873 return n - 2;
3874 else
3875 return i;
3876}
3877
3878/*****************************************************************************/
3879
3881 float profiles[EX][EY][EP],
3882 const int np,
3883 const int lon_ap_ind,
3884 const int lat_ap_ind,
3885 const double height_ap,
3886 int *ind) {
3887
3888 ind[0] = locate_irr_float(profiles[lon_ap_ind][lat_ap_ind],
3889 np, height_ap, 0);
3890 ind[1] = locate_irr_float(profiles[lon_ap_ind + 1][lat_ap_ind],
3891 np, height_ap, ind[0]);
3892 ind[2] = locate_irr_float(profiles[lon_ap_ind][lat_ap_ind + 1],
3893 np, height_ap, ind[1]);
3894 ind[3] = locate_irr_float(profiles[lon_ap_ind + 1][lat_ap_ind + 1],
3895 np, height_ap, ind[2]);
3896}
3897
3898/*****************************************************************************/
3899
3901 const ctl_t *ctl,
3902 const cache_t *cache,
3903 met_t *met0,
3904 met_t *met1,
3905 atm_t *atm) {
3906
3907 /* Set timer... */
3908 SELECT_TIMER("MODULE_ADVECT", "PHYSICS");
3909
3910 /* Use omega vertical velocity... */
3911 if (ctl->advect_vert_coord == 0 || ctl->advect_vert_coord == 2) {
3912
3913 /* Loop over particles... */
3914 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
3915
3916 /* Init... */
3918 double dts, u[4], um = 0, v[4], vm = 0, w[4], wm = 0,
3919 x[3] = { 0, 0, 0 };
3920
3921 /* Loop over integration nodes... */
3922 for (int i = 0; i < ctl->advect; i++) {
3923
3924 /* Set position... */
3925 if (i == 0) {
3926 dts = 0.0;
3927 x[0] = atm->lon[ip];
3928 x[1] = atm->lat[ip];
3929 x[2] = atm->p[ip];
3930 } else {
3931 dts = (i == 3 ? 1.0 : 0.5) * cache->dt[ip];
3932 x[0] = atm->lon[ip] + DX2COORD(met0, dts * u[i - 1], atm->lat[ip]);
3933 x[1] = atm->lat[ip] + DY2COORD(met0, dts * v[i - 1]);
3934 x[2] = atm->p[ip] + dts * w[i - 1];
3935 }
3936 const double tm = atm->time[ip] + dts;
3937
3938 /* Interpolate meteo data on pressure levels... */
3939 if (ctl->advect_vert_coord == 0) {
3940 intpol_met_time_3d(met0, met0->u, met1, met1->u,
3941 tm, x[2], x[0], x[1], &u[i], ci, cw, 1);
3942 intpol_met_time_3d(met0, met0->v, met1, met1->v,
3943 tm, x[2], x[0], x[1], &v[i], ci, cw, 0);
3944 intpol_met_time_3d(met0, met0->w, met1, met1->w,
3945 tm, x[2], x[0], x[1], &w[i], ci, cw, 0);
3946 }
3947
3948 /* Interpolate meteo data on model levels... */
3949 else {
3950 intpol_met_4d_zeta(met0, met0->pl, met0->ul,
3951 met1, met1->pl, met1->ul,
3952 tm, x[2], x[0], x[1], &u[i], ci, cw, 1);
3953 intpol_met_4d_zeta(met0, met0->pl, met0->vl,
3954 met1, met1->pl, met1->vl,
3955 tm, x[2], x[0], x[1], &v[i], ci, cw, 0);
3956 intpol_met_4d_zeta(met0, met0->pl, met0->wl,
3957 met1, met1->pl, met1->wl,
3958 tm, x[2], x[0], x[1], &w[i], ci, cw, 0);
3959 }
3960
3961 /* Get mean wind... */
3962 double k = 1.0;
3963 if (ctl->advect == 2)
3964 k = (i == 0 ? 0.0 : 1.0);
3965 else if (ctl->advect == 4)
3966 k = (i == 0 || i == 3 ? 1.0 / 6.0 : 2.0 / 6.0);
3967 um += k * u[i];
3968 vm += k * v[i];
3969 wm += k * w[i];
3970 }
3971
3972 /* Set new position... */
3973 atm->time[ip] += cache->dt[ip];
3974 atm->lon[ip] += DX2COORD(met0, cache->dt[ip] * um,
3975 (ctl->advect == 2 ? x[1] : atm->lat[ip]));
3976 atm->lat[ip] += DY2COORD(met0, cache->dt[ip] * vm);
3977 atm->p[ip] += cache->dt[ip] * wm;
3978
3979 }
3980 }
3981
3982 /* Use zeta or eta vertical velocity... */
3983 else if (ctl->advect_vert_coord == 1 || ctl->advect_vert_coord == 3) {
3984
3985 /* Select quantity index depending on coordinate... */
3986 const int qnt = (ctl->advect_vert_coord == 1
3987 ? ctl->qnt_zeta : ctl->qnt_eta);
3988
3989 /* Loop over particles... */
3990 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
3991
3992 /* Convert pressure to vertical coordinate (zeta or eta)... */
3994 intpol_met_4d_zeta(met0, met0->pl, met0->zetal,
3995 met1, met1->pl, met1->zetal,
3996 atm->time[ip], atm->p[ip],
3997 atm->lon[ip], atm->lat[ip],
3998 &atm->q[qnt][ip], ci, cw, 1);
3999
4000 /* Init... */
4001 double dts, u[4], um = 0, v[4], vm = 0, wdot[4],
4002 wdotm = 0, x[3] = { 0, 0, 0 };
4003
4004 /* Loop over integration nodes (Runge–Kutta steps)... */
4005 for (int i = 0; i < ctl->advect; i++) {
4006
4007 /* Set position... */
4008 if (i == 0) {
4009 dts = 0.0;
4010 x[0] = atm->lon[ip];
4011 x[1] = atm->lat[ip];
4012 x[2] = atm->q[qnt][ip];
4013 } else {
4014 dts = (i == 3 ? 1.0 : 0.5) * cache->dt[ip];
4015 x[0] = atm->lon[ip] + DX2COORD(met0, dts * u[i - 1], atm->lat[ip]);
4016 x[1] = atm->lat[ip] + DY2COORD(met0, dts * v[i - 1]);
4017 x[2] = atm->q[qnt][ip] + dts * wdot[i - 1];
4018 }
4019
4020 const double tm = atm->time[ip] + dts;
4021
4022 /* Interpolate meteo data... */
4023 intpol_met_4d_zeta(met0, met0->zetal, met0->ul,
4024 met1, met1->zetal, met1->ul,
4025 tm, x[2], x[0], x[1], &u[i], ci, cw, 1);
4026 intpol_met_4d_zeta(met0, met0->zetal, met0->vl,
4027 met1, met1->zetal, met1->vl,
4028 tm, x[2], x[0], x[1], &v[i], ci, cw, 0);
4029 intpol_met_4d_zeta(met0, met0->zetal, met0->zeta_dotl,
4030 met1, met1->zetal, met1->zeta_dotl,
4031 tm, x[2], x[0], x[1], &wdot[i], ci, cw, 0);
4032
4033 /* Compute Runge–Kutta weights... */
4034 double k = 1.0;
4035 if (ctl->advect == 2)
4036 k = (i == 0 ? 0.0 : 1.0);
4037 else if (ctl->advect == 4)
4038 k = (i == 0 || i == 3 ? 1.0 / 6.0 : 2.0 / 6.0);
4039
4040 um += k * u[i];
4041 vm += k * v[i];
4042 wdotm += k * wdot[i];
4043 }
4044
4045 /* Update particle position... */
4046 atm->time[ip] += cache->dt[ip];
4047 atm->lon[ip] += DX2COORD(met0, cache->dt[ip] * um,
4048 (ctl->advect == 2 ? x[1] : atm->lat[ip]));
4049 atm->lat[ip] += DY2COORD(met0, cache->dt[ip] * vm);
4050 atm->q[qnt][ip] += cache->dt[ip] * wdotm;
4051
4052 /* Convert vertical coordinate (zeta or eta) back to pressure... */
4053 intpol_met_4d_zeta(met0, met0->zetal, met0->pl,
4054 met1, met1->zetal, met1->pl,
4055 atm->time[ip],
4056 atm->q[qnt][ip], atm->lon[ip], atm->lat[ip],
4057 &atm->p[ip], ci, cw, 1);
4058 }
4059 }
4060}
4061
4062/*****************************************************************************/
4063
4065 const ctl_t *ctl,
4066 const cache_t *cache,
4067 met_t *met0,
4068 met_t *met1,
4069 atm_t *atm) {
4070
4071 /* Check parameters... */
4072 if (ctl->advect_vert_coord != 1)
4073 return;
4074
4075 /* Set timer... */
4076 SELECT_TIMER("MODULE_ADVECT_INIT", "PHYSICS");
4077
4078 /* Loop over particles... */
4079 PARTICLE_LOOP(0, atm->np, 0, "acc data present(ctl,met0,met1,atm)") {
4080
4081 /* Initialize pressure consistent with zeta... */
4083 intpol_met_4d_zeta(met0, met0->zetal, met0->pl, met1, met1->zetal,
4084 met1->pl, atm->time[ip], atm->q[ctl->qnt_zeta][ip],
4085 atm->lon[ip], atm->lat[ip], &atm->p[ip], ci, cw, 1);
4086 }
4087}
4088
4089/*****************************************************************************/
4090
4092 const ctl_t *ctl,
4093 const cache_t *cache,
4094 const clim_t *clim,
4095 met_t *met0,
4096 met_t *met1,
4097 atm_t *atm) {
4098
4099 /* Set timer... */
4100 SELECT_TIMER("MODULE_BOUND_COND", "PHYSICS");
4101
4102 /* Check quantity flags... */
4103 if (ctl->qnt_m < 0 && ctl->qnt_vmr < 0 && ctl->qnt_Cccl4
4104 && ctl->qnt_Cccl3f < 0 && ctl->qnt_Cccl2f2 < 0
4105 && ctl->qnt_Cn2o < 0 && ctl->qnt_Csf6 < 0 && ctl->qnt_aoa < 0)
4106 return;
4107
4108 /* Loop over particles... */
4109 PARTICLE_LOOP(0, atm->np, 1,
4110 "acc data present(ctl,cache,clim,met0,met1,atm)") {
4111
4112 /* Check latitude and pressure range... */
4113 if (atm->lat[ip] < ctl->bound_lat0 || atm->lat[ip] > ctl->bound_lat1
4114 || atm->p[ip] > ctl->bound_p0 || atm->p[ip] < ctl->bound_p1)
4115 continue;
4116
4117 /* Check surface layer... */
4118 if (ctl->bound_dps > 0 || ctl->bound_dzs > 0
4119 || ctl->bound_zetas > 0 || ctl->bound_pbl) {
4120
4121 /* Get surface pressure... */
4122 double ps;
4124 INTPOL_2D(ps, 1);
4125
4126 /* Check pressure... */
4127 if (ctl->bound_dps > 0 && atm->p[ip] < ps - ctl->bound_dps)
4128 continue;
4129
4130 /* Check height... */
4131 if (ctl->bound_dzs > 0 && Z(atm->p[ip]) > Z(ps) + ctl->bound_dzs)
4132 continue;
4133
4134 /* Check zeta range... */
4135 if (ctl->bound_zetas > 0) {
4136 double t;
4137 INTPOL_3D(t, 1);
4138 if (ZETA(ps, atm->p[ip], t) > ctl->bound_zetas)
4139 continue;
4140 }
4141
4142 /* Check planetary boundary layer... */
4143 if (ctl->bound_pbl) {
4144 double pbl;
4145 INTPOL_2D(pbl, 0);
4146 if (atm->p[ip] < pbl)
4147 continue;
4148 }
4149 }
4150
4151 /* Set mass and volume mixing ratio... */
4152 if (ctl->qnt_m >= 0 && ctl->bound_mass >= 0)
4153 atm->q[ctl->qnt_m][ip] =
4154 ctl->bound_mass + ctl->bound_mass_trend * atm->time[ip];
4155 if (ctl->qnt_vmr >= 0 && ctl->bound_vmr >= 0)
4156 atm->q[ctl->qnt_vmr][ip] =
4157 ctl->bound_vmr + ctl->bound_vmr_trend * atm->time[ip];
4158
4159 /* Set CFC-10 volume mixing ratio... */
4160 if (ctl->qnt_Cccl4 >= 0 && ctl->clim_ccl4_timeseries[0] != '-')
4161 atm->q[ctl->qnt_Cccl4][ip] = clim_ts(&clim->ccl4, atm->time[ip]);
4162
4163 /* Set CFC-11 volume mixing ratio... */
4164 if (ctl->qnt_Cccl3f >= 0 && ctl->clim_ccl3f_timeseries[0] != '-')
4165 atm->q[ctl->qnt_Cccl3f][ip] = clim_ts(&clim->ccl3f, atm->time[ip]);
4166
4167 /* Set CFC-12 volume mixing ratio... */
4168 if (ctl->qnt_Cccl2f2 >= 0 && ctl->clim_ccl2f2_timeseries[0] != '-')
4169 atm->q[ctl->qnt_Cccl2f2][ip] = clim_ts(&clim->ccl2f2, atm->time[ip]);
4170
4171 /* Set N2O volume mixing ratio... */
4172 if (ctl->qnt_Cn2o >= 0 && ctl->clim_n2o_timeseries[0] != '-')
4173 atm->q[ctl->qnt_Cn2o][ip] = clim_ts(&clim->n2o, atm->time[ip]);
4174
4175 /* Set SF6 volume mixing ratio... */
4176 if (ctl->qnt_Csf6 >= 0 && ctl->clim_sf6_timeseries[0] != '-')
4177 atm->q[ctl->qnt_Csf6][ip] = clim_ts(&clim->sf6, atm->time[ip]);
4178
4179 /* Set age of air... */
4180 if (ctl->qnt_aoa >= 0)
4181 atm->q[ctl->qnt_aoa][ip] = atm->time[ip];
4182 }
4183}
4184
4185/*****************************************************************************/
4186
4188 const ctl_t *ctl,
4189 met_t *met0,
4190 met_t *met1,
4191 atm_t *atm,
4192 const double tt) {
4193
4194 if (met0->coord_type != 0)
4195 ERRMSG("Only lat/lon grid supported");
4196
4197 /* Check quantities... */
4198 if (ctl->qnt_m < 0 || ctl->qnt_Cx < 0)
4199 return;
4200 if (ctl->molmass <= 0)
4201 ERRMSG("Molar mass is not defined!");
4202
4203 /* Set timer... */
4204 SELECT_TIMER("MODULE_CHEM_GRID", "PHYSICS");
4205
4206 /* Allocate... */
4207 const int ensemble_mode = (ctl->nens > 0);
4208 const int np = atm->np;
4209 const int nz = ctl->chemgrid_nz;
4210 const int nx = ctl->chemgrid_nx;
4211 const int ny = ctl->chemgrid_ny;
4212 const int ngrid = nx * ny * nz;
4213 const int nens = ensemble_mode ? ctl->nens : 1;
4214
4215 double *restrict const z = (double *) malloc((size_t) nz * sizeof(double));
4216 double *restrict const press =
4217 (double *) malloc((size_t) nz * sizeof(double));
4218 double *restrict const mass =
4219 (double *) calloc((size_t) ngrid * (size_t) nens, sizeof(double));
4220 double *restrict const area =
4221 (double *) malloc((size_t) ny * sizeof(double));
4222 double *restrict const lon =
4223 (double *) malloc((size_t) nx * sizeof(double));
4224 double *restrict const lat =
4225 (double *) malloc((size_t) ny * sizeof(double));
4226
4227 int *restrict const ixs = (int *) malloc((size_t) np * sizeof(int));
4228 int *restrict const iys = (int *) malloc((size_t) np * sizeof(int));
4229 int *restrict const izs = (int *) malloc((size_t) np * sizeof(int));
4230
4231 /* Set grid box size... */
4232 const double dz = (ctl->chemgrid_z1 - ctl->chemgrid_z0) / nz;
4233 const double dlon = (ctl->chemgrid_lon1 - ctl->chemgrid_lon0) / nx;
4234 const double dlat = (ctl->chemgrid_lat1 - ctl->chemgrid_lat0) / ny;
4235
4236 /* Set vertical coordinates... */
4237#ifdef _OPENACC
4238#pragma acc enter data create(ixs[0:np],iys[0:np],izs[0:np],z[0:nz],press[0:nz],mass[0:ngrid*nens],area[0:ny],lon[0:nx],lat[0:ny])
4239#pragma acc data present(ctl,met0,met1,atm,ixs,iys,izs,z,press,mass,area,lon,lat)
4240#pragma acc parallel loop independent gang vector
4241#else
4242#pragma omp parallel for default(shared)
4243#endif
4244 for (int iz = 0; iz < nz; iz++) {
4245 z[iz] = ctl->chemgrid_z0 + dz * (iz + 0.5);
4246 press[iz] = P(z[iz]);
4247 }
4248
4249 /* Initialize mass grid... */
4250#ifdef _OPENACC
4251#pragma acc parallel loop independent gang vector
4252#endif
4253 for (int i = 0; i < ngrid * nens; i++)
4254 mass[i] = 0;
4255
4256 /* Set time interval for output... */
4257 const double t0 = tt - 0.5 * ctl->dt_mod;
4258 const double t1 = tt + 0.5 * ctl->dt_mod;
4259
4260 /* Get indices... */
4261#ifdef _OPENACC
4262#pragma acc parallel loop independent gang vector
4263#else
4264#pragma omp parallel for default(shared)
4265#endif
4266 for (int ip = 0; ip < np; ip++) {
4267 const double zpart = Z(atm->p[ip]);
4268 if (atm->time[ip] < t0 || atm->time[ip] > t1
4269 || atm->lon[ip] < ctl->chemgrid_lon0
4270 || atm->lon[ip] >= ctl->chemgrid_lon1
4271 || atm->lat[ip] < ctl->chemgrid_lat0
4272 || atm->lat[ip] >= ctl->chemgrid_lat1
4273 || zpart < ctl->chemgrid_z0 || zpart >= ctl->chemgrid_z1) {
4274 izs[ip] = -1;
4275 continue;
4276 }
4277 ixs[ip] = (int) ((atm->lon[ip] - ctl->chemgrid_lon0) / dlon);
4278 iys[ip] = (int) ((atm->lat[ip] - ctl->chemgrid_lat0) / dlat);
4279 izs[ip] = (int) ((zpart - ctl->chemgrid_z0) / dz);
4280 if (ixs[ip] >= nx || iys[ip] >= ny || izs[ip] >= nz)
4281 izs[ip] = -1;
4282 }
4283
4284 /* Set horizontal coordinates... */
4285#ifdef _OPENACC
4286#pragma acc parallel loop independent gang vector
4287#else
4288#pragma omp parallel for default(shared)
4289#endif
4290 for (int ix = 0; ix < nx; ix++)
4291 lon[ix] = ctl->chemgrid_lon0 + dlon * (ix + 0.5);
4292
4293#ifdef _OPENACC
4294#pragma acc parallel loop independent gang vector
4295#else
4296#pragma omp parallel for default(shared)
4297#endif
4298 for (int iy = 0; iy < ny; iy++) {
4299 lat[iy] = ctl->chemgrid_lat0 + dlat * (iy + 0.5);
4300 area[iy] = dlat * dlon * SQR(RE * M_PI / 180.) * cos(DEG2RAD(lat[iy]));
4301 }
4302
4303 /* Get mass per grid box... */
4304#ifdef _OPENACC
4305#pragma acc parallel loop independent gang vector
4306#endif
4307 for (int ip = 0; ip < np; ip++) {
4308 if (izs[ip] >= 0) {
4309 int mass_idx = ARRAY_3D(ixs[ip], iys[ip], ny, izs[ip], nz);
4310 if (ensemble_mode) {
4311 const int ens = (int) atm->q[ctl->qnt_ens][ip];
4312 mass_idx += ens * ngrid;
4313 }
4314#ifdef _OPENACC
4315#pragma acc atomic update
4316#endif
4317 mass[mass_idx] += atm->q[ctl->qnt_m][ip];
4318 }
4319 }
4320
4321 /* Assign grid data to air parcels ... */
4322#ifdef _OPENACC
4323#pragma acc parallel loop independent gang vector
4324#else
4325#pragma omp parallel for default(shared)
4326#endif
4327 for (int ip = 0; ip < np; ip++)
4328 if (izs[ip] >= 0) {
4329
4330 /* Interpolate temperature... */
4331 double temp;
4333 intpol_met_time_3d(met0, met0->t, met1, met1->t, tt,
4334 press[izs[ip]],
4335 lon[ixs[ip]], lat[iys[ip]], &temp, ci, cw, 1);
4336
4337 /* Set mass... */
4338 int mass_idx = ARRAY_3D(ixs[ip], iys[ip], ny, izs[ip], nz);
4339 if (ensemble_mode) {
4340 const int ens = (int) atm->q[ctl->qnt_ens][ip];
4341 mass_idx += ens * ngrid;
4342 }
4343
4344 /* Calculate volume mixing ratio... */
4345 const double m = mass[mass_idx];
4346 atm->q[ctl->qnt_Cx][ip] = MA / ctl->molmass * m
4347 / (RHO(press[izs[ip]], temp) * area[iys[ip]] * dz * 1e9);
4348 }
4349
4350 /* Free... */
4351#ifdef _OPENACC
4352#pragma acc exit data delete(ixs,iys,izs,z,press,mass,area,lon,lat)
4353#endif
4354 free(mass);
4355 free(lon);
4356 free(lat);
4357 free(area);
4358 free(z);
4359 free(press);
4360 free(ixs);
4361 free(iys);
4362 free(izs);
4363}
4364
4365/*****************************************************************************/
4366
4368 const ctl_t *ctl,
4369 const cache_t *cache,
4370 const clim_t *clim,
4371 met_t *met0,
4372 met_t *met1,
4373 atm_t *atm) {
4374
4375 /* Set timer... */
4376 SELECT_TIMER("MODULE_CHEM_INIT", "PHYSICS");
4377
4378 /* Loop over particles... */
4379 PARTICLE_LOOP(0, atm->np, 0,
4380 "acc data present(ctl,cache,clim,met0,met1,atm)") {
4381
4382 /* Set H2O and O3 using meteo data... */
4384 if (ctl->qnt_Ch2o >= 0) {
4385 double h2o;
4386 INTPOL_3D(h2o, 1);
4387 SET_ATM(qnt_Ch2o, h2o);
4388 }
4389 if (ctl->qnt_Co3 >= 0) {
4390 double o3;
4391 INTPOL_3D(o3, 1);
4392 SET_ATM(qnt_Co3, o3);
4393 }
4394
4395 /* Set radical species... */
4396 const double lat_ref =
4397 ctl->met_coord_type == 0 ? atm->lat[ip] : ctl->met_utm_ref_lat;
4398 SET_ATM(qnt_Coh, clim_oh(ctl, clim, atm->time[ip],
4399 atm->lon[ip], atm->lat[ip], atm->p[ip]));
4400 SET_ATM(qnt_Cho2, clim_zm(&clim->ho2, atm->time[ip],
4401 lat_ref, atm->p[ip]));
4402 SET_ATM(qnt_Ch2o2, clim_zm(&clim->h2o2, atm->time[ip],
4403 lat_ref, atm->p[ip]));
4404 SET_ATM(qnt_Co1d, clim_zm(&clim->o1d, atm->time[ip],
4405 lat_ref, atm->p[ip]));
4406 }
4407}
4408
4409/*****************************************************************************/
4410
4412 const ctl_t *ctl,
4413 cache_t *cache,
4414 met_t *met0,
4415 met_t *met1,
4416 atm_t *atm) {
4417
4418 /* Set timer... */
4419 SELECT_TIMER("MODULE_CONVECTION", "PHYSICS");
4420
4421 /* Create random numbers... */
4422 module_rng(ctl, cache->rs, (size_t) atm->np, 0);
4423
4424 /* Loop over particles... */
4425 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
4426
4427 /* Interpolate surface pressure... */
4428 double ps;
4430 INTPOL_2D(ps, 1);
4431
4432 /* Initialize pressure range for vertical mixing... */
4433 double pbot = ps, ptop = ps;
4434
4435 /* Mixing in the PBL... */
4436 if (ctl->conv_mix_pbl) {
4437
4438 /* Interpolate PBL... */
4439 double pbl;
4440 INTPOL_2D(pbl, 0);
4441
4442 /* Set pressure range... */
4443 ptop = pbl - ctl->conv_pbl_trans * (ps - pbl);
4444 }
4445
4446 /* Convective mixing... */
4447 if (ctl->conv_cape >= 0) {
4448
4449 /* Interpolate CAPE, CIN, and equilibrium level... */
4450 double cape, cin, pel;
4451 INTPOL_2D(cape, 0);
4452 INTPOL_2D(cin, 0);
4453 INTPOL_2D(pel, 0);
4454
4455 /* Set pressure range... */
4456 if (isfinite(cape) && cape >= ctl->conv_cape
4457 && (ctl->conv_cin <= 0 || (isfinite(cin) && cin >= ctl->conv_cin)))
4458 ptop = MIN(ptop, pel);
4459 }
4460
4461 /* Apply vertical mixing... */
4462 if (ptop != pbot && atm->p[ip] >= ptop) {
4463
4464 /* Get density range... */
4465 double tbot, ttop;
4466 intpol_met_time_3d(met0, met0->t, met1, met1->t, atm->time[ip],
4467 pbot, atm->lon[ip], atm->lat[ip], &tbot, ci, cw, 1);
4468 intpol_met_time_3d(met0, met0->t, met1, met1->t, atm->time[ip], ptop,
4469 atm->lon[ip], atm->lat[ip], &ttop, ci, cw, 1);
4470 const double rhobot = pbot / tbot;
4471 const double rhotop = ptop / ttop;
4472
4473 /* Get new density... */
4474 const double rho = rhobot + (rhotop - rhobot) * cache->rs[ip];
4475
4476 /* Get pressure... */
4477 atm->p[ip] = LIN(rhobot, pbot, rhotop, ptop, rho);
4478 }
4479 }
4480}
4481
4482/*****************************************************************************/
4483
4484#ifdef DD
4486 double t,
4487 const ctl_t *ctl,
4488 cache_t *cache,
4489 dd_t *dd,
4490 atm_t *atm,
4491 met_t **met) {
4492
4493 /* Initialize particles locally... */
4494 int npart = 0, nkeep = 0, capacity = 0;
4495 particle_t *particles = NULL;
4496
4497 /* Assign particles to new subdomains... */
4498 dd_assign_subdomains(ctl, dd, atm, 0);
4499
4500 if (ctl->dd_exchange_mode == 0) {
4501 /* Original sort/push exchange path... */
4502 if (fmod(t, ctl->dd_sort_dt) == 0)
4503 dd_sort(ctl, *met, cache, atm, dd, &npart);
4504 else
4505 dd_push(ctl, atm, cache, &npart);
4506 } else {
4507 /* Count outgoing particles for direct packing and compaction... */
4508 dd_count_outgoing(ctl, atm, dd, &nkeep, &npart);
4509 }
4510
4511 /* Ensure particle buffer is large enough for outgoing particles... */
4512 if (npart > capacity) {
4513 const int newcap = npart + npart / 2 + 1;
4514 particle_t *tmp =
4515 realloc(particles, (size_t) newcap * sizeof(particle_t));
4516 if (!tmp)
4517 ERRMSG("Out of memory!");
4518 particles = tmp;
4519 capacity = newcap;
4520 }
4521
4522 /* Transform from struct of array to array of struct... */
4523 if (ctl->dd_exchange_mode == 0)
4524 dd_atm2particles(ctl, cache, atm, particles, npart);
4525 else
4526 dd_pack_compact(ctl, cache, atm, dd, particles, nkeep, npart);
4527
4528 SELECT_TIMER("SYNC", "DD");
4529 MPI_Barrier(MPI_COMM_WORLD);
4530
4531 /* Perform the communication... */
4532 dd_communicate_particles(ctl, dd, &particles, &npart, &capacity);
4533
4534 /* Transform from array of struct to struct of array... */
4535 dd_particles2atm(ctl, cache, particles, npart, atm);
4536
4537 /* Free local particle array... */
4538 free(particles);
4539}
4540#endif
4541
4542/*****************************************************************************/
4543
4545 const ctl_t *ctl,
4546 const cache_t *cache,
4547 const clim_t *clim,
4548 atm_t *atm) {
4549
4550 /* Set timer... */
4551 SELECT_TIMER("MODULE_DECAY", "PHYSICS");
4552
4553 /* Check quantity flags... */
4554 if (ctl->qnt_m < 0 && ctl->qnt_vmr < 0)
4555 ERRMSG("Module needs quantity mass or volume mixing ratio!");
4556
4557 /* Loop over particles... */
4558 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,clim,atm)") {
4559
4560 /* Get weighting factor... */
4561 const double w = tropo_weight(ctl, clim, atm, ip);
4562
4563 /* Set lifetime... */
4564 const double tdec = w * ctl->tdec_trop + (1 - w) * ctl->tdec_strat;
4565
4566 /* Calculate exponential decay... */
4567 const double aux = exp(-cache->dt[ip] / tdec);
4568 if (ctl->qnt_m >= 0) {
4569 if (ctl->qnt_mloss_decay >= 0)
4570 atm->q[ctl->qnt_mloss_decay][ip]
4571 += atm->q[ctl->qnt_m][ip] * (1 - aux);
4572 atm->q[ctl->qnt_m][ip] *= aux;
4573 if (ctl->qnt_loss_rate >= 0)
4574 atm->q[ctl->qnt_loss_rate][ip] += 1. / tdec;
4575 }
4576 if (ctl->qnt_vmr >= 0)
4577 atm->q[ctl->qnt_vmr][ip] *= aux;
4578 }
4579}
4580
4581/*****************************************************************************/
4582
4584 const ctl_t *ctl,
4585 cache_t *cache,
4586 const met_t *met0,
4587 const met_t *met1,
4588 atm_t *atm) {
4589
4590 /* Set timer... */
4591 SELECT_TIMER("MODULE_DIFF_MESO", "PHYSICS");
4592
4593 /* Create random numbers... */
4594 module_rng(ctl, cache->rs, 3 * (size_t) atm->np, 1);
4595
4596 /* Loop over particles... */
4597 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
4598
4599 /* Get indices... */
4600 const int ix = locate_reg(met0->lon, met0->nx, atm->lon[ip]);
4601 const int iy = locate_irr(met0->lat, met0->ny, atm->lat[ip]);
4602 const int iz = locate_irr(met0->p, met0->np, atm->p[ip]);
4603
4604 /* Get standard deviations of local wind data... */
4605 float umean = 0, usig = 0, vmean = 0, vsig = 0, wmean = 0, wsig = 0;
4606 for (int i = 0; i < 2; i++)
4607 for (int j = 0; j < 2; j++)
4608 for (int k = 0; k < 2; k++) {
4609 umean += met0->u[ix + i][iy + j][iz + k];
4610 usig += SQR(met0->u[ix + i][iy + j][iz + k]);
4611 vmean += met0->v[ix + i][iy + j][iz + k];
4612 vsig += SQR(met0->v[ix + i][iy + j][iz + k]);
4613 wmean += met0->w[ix + i][iy + j][iz + k];
4614 wsig += SQR(met0->w[ix + i][iy + j][iz + k]);
4615
4616 umean += met1->u[ix + i][iy + j][iz + k];
4617 usig += SQR(met1->u[ix + i][iy + j][iz + k]);
4618 vmean += met1->v[ix + i][iy + j][iz + k];
4619 vsig += SQR(met1->v[ix + i][iy + j][iz + k]);
4620 wmean += met1->w[ix + i][iy + j][iz + k];
4621 wsig += SQR(met1->w[ix + i][iy + j][iz + k]);
4622 }
4623 usig = usig / 16.f - SQR(umean / 16.f);
4624 usig = (usig > 0 ? sqrtf(usig) : 0);
4625 vsig = vsig / 16.f - SQR(vmean / 16.f);
4626 vsig = (vsig > 0 ? sqrtf(vsig) : 0);
4627 wsig = wsig / 16.f - SQR(wmean / 16.f);
4628 wsig = (wsig > 0 ? sqrtf(wsig) : 0);
4629
4630 /* Set temporal correlations for mesoscale fluctuations... */
4631 const double r = 1 - 2 * fabs(cache->dt[ip]) / ctl->dt_met;
4632 const double r2 = sqrt(1 - r * r);
4633
4634 /* Calculate horizontal mesoscale wind fluctuations... */
4635 if (ctl->turb_mesox > 0) {
4636 cache->uvwp[ip][0] =
4637 (float) (r * cache->uvwp[ip][0] +
4638 r2 * cache->rs[3 * ip] * ctl->turb_mesox * usig);
4639 atm->lon[ip] +=
4640 DX2COORD(met0, cache->uvwp[ip][0] * cache->dt[ip], atm->lat[ip]);
4641
4642 cache->uvwp[ip][1] =
4643 (float) (r * cache->uvwp[ip][1] +
4644 r2 * cache->rs[3 * ip + 1] * ctl->turb_mesox * vsig);
4645 atm->lat[ip] += DY2COORD(met0, cache->uvwp[ip][1] * cache->dt[ip]);
4646 }
4647
4648 /* Calculate vertical mesoscale wind fluctuations... */
4649 if (ctl->turb_mesoz > 0) {
4650 cache->uvwp[ip][2] =
4651 (float) (r * cache->uvwp[ip][2] +
4652 r2 * cache->rs[3 * ip + 2] * ctl->turb_mesoz * wsig);
4653 atm->p[ip] += cache->uvwp[ip][2] * cache->dt[ip];
4654 }
4655 }
4656}
4657
4658/*****************************************************************************/
4659
4661 const ctl_t *ctl,
4662 cache_t *cache,
4663 met_t *met0,
4664 met_t *met1,
4665 atm_t *atm) {
4666
4667 /* Set timer... */
4668 SELECT_TIMER("MODULE_DIFF_PBL", "PHYSICS");
4669
4670 /* Create random numbers... */
4671 module_rng(ctl, cache->rs, 3 * (size_t) atm->np, 1);
4672
4673 /* Loop over particles... */
4674 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
4675
4676 double pbl, ps, dsigw_dz = 0.0, sig_u = 0.0, sig_v = 0.0, sig_w = 0.0;
4677 double tau_u = 0.0, tau_v = 0.0, tau_w = 0.0;
4678
4679 /* Get PBL pressure... */
4681 INTPOL_2D(pbl, 1);
4682
4683 /* Let the background diffusion scheme handle particles above the PBL. */
4684 if (atm->p[ip] < pbl)
4685 continue;
4686
4687 /* Get surface pressure... */
4688 INTPOL_2D(ps, 0);
4689
4690 /* Skip invalid or vanishing PBL layers. */
4691 if (!(ps > 0.0 && pbl > 0.0 && ps > pbl))
4692 continue;
4693
4694 /* Calculate heights [m] above ground.
4695 Z() returns altitude in km, so multiply differences by 1e3. */
4696 const double p = MIN(atm->p[ip], ps);
4697 const double zs = Z(ps);
4698 const double z_raw = 1e3 * (Z(p) - zs);
4699 const double zi = 1e3 * (Z(pbl) - zs);
4700
4701 /* Require a physically meaningful PBL depth. */
4702 if (!(zi > 1.0))
4703 continue;
4704
4705 /* Clamp height to the PBL interval for closure evaluation. */
4706 const double z = CLAMP(z_raw, 0.0, zi);
4707 const double zeta = CLAMP(z / zi, 1e-6, 1.0 - 1e-6);
4708 const double z_m = MAX(z, 1.0);
4709
4710 /* Temporarily use clamped pressure for thermodynamic interpolation.
4711 This avoids inconsistent interpolation if a particle has slipped
4712 below the surface pressure. */
4713 const double p_save = atm->p[ip];
4714 atm->p[ip] = p;
4715
4716 /* Calculate friction velocity... */
4717 double ess, nss, h2o, t;
4718 INTPOL_2D(ess, 0);
4719 INTPOL_2D(nss, 0);
4720 INTPOL_3D(t, 1);
4721 INTPOL_3D(h2o, 0);
4722
4723 /* Restore particle pressure before any continue/update path. */
4724 atm->p[ip] = p_save;
4725
4726 const double tv = TVIRT(t, h2o);
4727 const double thetav = THETAVIRT(p, t, h2o);
4728 const double rho = RHO(p, tv);
4729 const double tau = sqrt(SQR(ess) + SQR(nss));
4730
4731 if (!(rho > 0.0))
4732 continue;
4733
4734 const double ustar = sqrt(MAX(tau / rho, 0.0));
4735 const double ust = MAX(1e-4, ustar);
4736
4737 /* Get surface sensible heat flux.
4738 Sign convention assumed here: unstable surface heating gives shf < 0,
4739 as in the existing implementation. */
4740 double shf;
4741 INTPOL_2D(shf, 1);
4742
4743 /* Estimate Monin-Obukhov length [m] to distinguish
4744 neutral, stable, and unstable cases. */
4745 double ol = 1e12;
4746 if (fabs(shf) > 1e-6)
4747 ol = thetav * rho * CPD * SQR(ust) * ust / (KARMAN * G0 * shf);
4748
4749 /* Neutral conditions... */
4750 if (zi / fabs(ol) < 1.0) {
4751
4752 /* corr has units of seconds, hence the exponential coefficients
4753 have units of s^-1. The derivative d(sig_w)/dz therefore needs
4754 the extra factor 1/ust. */
4755 const double corr = z_m / ust;
4756 const double sigw0 = 1.3 * ust * exp(-2e-4 * corr);
4757
4758 sig_u = MAX(2.0 * ust * exp(-3e-4 * corr), 1e-5);
4759 sig_v = MAX(sigw0, 1e-5);
4760 sig_w = MAX(sigw0, 1e-5);
4761 dsigw_dz = -2e-4 * sigw0 / ust;
4762
4763 tau_u = 0.5 * z_m / sig_w / (1.0 + 1.5e-3 * corr);
4764 tau_v = tau_u;
4765 tau_w = tau_u;
4766 }
4767
4768 /* Unstable conditions... */
4769 else if (ol < 0.0) {
4770
4771 /* Convective velocity scale [m/s]. */
4772 const double wstar_arg = -G0 / thetav * shf / (rho * CPD) * zi;
4773 const double wstar = pow(MAX(wstar_arg, 0.0), 1.0 / 3.0);
4774 double dsigw2_dz = 0.0;
4775
4776 /* Hanna1/FLEXPART turbulent velocity standard deviations [m/s]. */
4777 sig_u = MAX(ust * pow(MAX(12.0 - 0.5 * zi / ol, 0.0), 1.0 / 3.0), 1e-6);
4778 sig_v = sig_u;
4779
4780 if (zeta < 0.03) {
4781 const double arg = MAX(3.0 * zeta - ol / zi, 1e-12);
4782 sig_w = 0.96 * wstar * pow(arg, 1.0 / 3.0);
4783 dsigw2_dz = 1.8432 * SQR(wstar) / zi * pow(arg, -1.0 / 3.0);
4784 } else if (zeta < 0.4) {
4785 const double arg = MAX(3.0 * zeta - ol / zi, 1e-12);
4786 const double s1 = 0.96 * pow(arg, 1.0 / 3.0);
4787 const double s2 = 0.763 * pow(zeta, 0.175);
4788 if (s1 < s2) {
4789 sig_w = wstar * s1;
4790 dsigw2_dz = 1.8432 * SQR(wstar) / zi * pow(arg, -1.0 / 3.0);
4791 } else {
4792 sig_w = wstar * s2;
4793 dsigw2_dz = 0.203759 * SQR(wstar) / zi * pow(zeta, -0.65);
4794 }
4795 } else if (zeta < 0.96) {
4796 sig_w = 0.722 * wstar * pow(1.0 - zeta, 0.207);
4797 dsigw2_dz = -0.215812 * SQR(wstar) / zi * pow(1.0 - zeta, -0.586);
4798 } else {
4799 sig_w = 0.37 * wstar;
4800 dsigw2_dz = 0.0;
4801 }
4802
4803 sig_w = MAX(sig_w, 1e-6);
4804 dsigw_dz = sig_w > 1e-12 ? 0.5 * dsigw2_dz / sig_w : 0.0;
4805
4806 /* Hanna/FLEXPART Lagrangian timescales [s]. */
4807 tau_u = 0.15 * zi / MAX(sig_u, 1e-12);
4808 tau_v = tau_u;
4809
4810 if (z_m < fabs(ol)) {
4811 const double denom = 0.55 - 0.38 * fabs(z_m / ol);
4812 tau_w = 0.1 * z_m / (sig_w * MAX(denom, 0.05));
4813 } else if (zeta < 0.1)
4814 tau_w = 0.59 * z_m / sig_w;
4815 else
4816 tau_w = 0.15 * zi / sig_w * (1.0 - exp(-5.0 * zeta));
4817 }
4818
4819 /* Stable conditions... */
4820 else {
4821
4822 sig_u = MAX(2.0 * ust * (1.0 - zeta), 1e-6);
4823 sig_v = MAX(1.3 * ust * (1.0 - zeta), 1e-6);
4824 sig_w = MAX(1.3 * ust * (1.0 - zeta), 1e-6);
4825 dsigw_dz = -1.3 * ust / zi;
4826
4827 tau_u = 0.15 * zi / sig_u * sqrt(zeta);
4828 tau_v = 0.467 * tau_u;
4829 tau_w = 0.1 * zi / sig_w * pow(zeta, 0.8);
4830 }
4831
4832 /* Apply FLEXPART-consistent lower bounds for timescales. */
4833 tau_u = MAX(tau_u, 10.0);
4834 tau_v = MAX(tau_v, 10.0);
4835 tau_w = MAX(tau_w, 30.0);
4836
4837 /* Skip pathological states. */
4838 if (!(sig_u > 0.0 && sig_v > 0.0
4839 && sig_w > 0.0 && tau_u > 0.0 && tau_v > 0.0 && tau_w > 0.0))
4840 continue;
4841
4842 /* Update horizontal perturbation [m/s]. */
4843 const double dt = cache->dt[ip];
4844 const double dt_abs = fabs(dt);
4845
4846 const double ru = exp(-dt_abs / tau_u);
4847 const double ru2 = sqrt(MAX(0.0, 1.0 - SQR(ru)));
4848 const double rv = exp(-dt_abs / tau_v);
4849 const double rv2 = sqrt(MAX(0.0, 1.0 - SQR(rv)));
4850
4851 cache->uvwp[ip][0]
4852 = (float) (cache->uvwp[ip][0] * ru + sig_u * ru2 * cache->rs[3 * ip]);
4853
4854 cache->uvwp[ip][1]
4855 = (float) (cache->uvwp[ip][1] * rv
4856 + sig_v * rv2 * cache->rs[3 * ip + 1]);
4857
4858 /* Update vertical perturbation [m/s].
4859 The drift term is d(sig_w^2)/dz + sig_w^2/rho * d(rho)/dz.
4860 With exponential scale height H0 [km], dln(rho)/dz ~= -1/(1000 H0). */
4861 const double rw = exp(-dt_abs / tau_w);
4862 const double rw2 = sqrt(MAX(0.0, 1.0 - SQR(rw)));
4863 const double rhoaux = -1.0 / (1e3 * H0);
4864
4865 cache->uvwp[ip][2]
4866 = (float) (cache->uvwp[ip][2] * rw + sig_w * rw2 * cache->rs[3 * ip + 2]
4867 + tau_w * (1.0 - rw)
4868 * (2.0 * sig_w * dsigw_dz + rhoaux * SQR(sig_w)));
4869
4870 /* Calculate new horizontal air parcel position. */
4871 atm->lon[ip] += DX2COORD(met0, cache->uvwp[ip][0] * dt, atm->lat[ip]);
4872 atm->lat[ip] += DY2COORD(met0, cache->uvwp[ip][1] * dt);
4873
4874 /* Calculate new height and reflect robustly at surface and PBL top.
4875 Each boundary reflection reverses the vertical turbulent velocity.
4876 This loop is intentionally simple and explicit: it remains correct
4877 even if a large step crosses multiple boundaries. */
4878 double znew = z + cache->uvwp[ip][2] * dt;
4879
4880 while (znew < 0.0 || znew > zi) {
4881
4882 if (znew < 0.0) {
4883 znew = -znew;
4884 cache->uvwp[ip][2] = -cache->uvwp[ip][2];
4885 }
4886
4887 if (znew > zi) {
4888 znew = 2.0 * zi - znew;
4889 cache->uvwp[ip][2] = -cache->uvwp[ip][2];
4890 }
4891 }
4892
4893 /* Set pressure from reflected geometric height.
4894 This is more consistent than a linearized DZ2DP update for
4895 potentially large turbulent steps. */
4896 atm->p[ip] = P(zs + znew / 1000.0);
4897
4898 /* Enforce exact pressure limits of the local PBL column. */
4899 atm->p[ip] = CLAMP(atm->p[ip], pbl, ps);
4900 }
4901}
4902
4903/*****************************************************************************/
4904
4906 const ctl_t *ctl,
4907 cache_t *cache,
4908 const clim_t *clim,
4909 met_t *met0,
4910 met_t *met1,
4911 atm_t *atm) {
4912
4913 /* Set timer... */
4914 SELECT_TIMER("MODULE_DIFF_TURB", "PHYSICS");
4915
4916 /* Create random numbers... */
4917 module_rng(ctl, cache->rs, 3 * (size_t) atm->np, 1);
4918
4919 /* Loop over particles... */
4920 PARTICLE_LOOP(0, atm->np, 1,
4921 "acc data present(ctl,cache,clim,met0,met1,atm)") {
4922
4923 /* Get PBL pressure... */
4924 double pbl;
4926 INTPOL_2D(pbl, 1);
4927
4928 /* Let optional PBL closure schemes handle turbulent diffusion inside the PBL. */
4929 if (ctl->turb_pbl_scheme > 0 && atm->p[ip] >= pbl)
4930 continue;
4931
4932 /* Get surface pressure... */
4933 double ps;
4934 INTPOL_2D(ps, 0);
4935
4936 /* Pressure at model top [hPa]. */
4937 const double ptop = met0->p[met0->np - 1];
4938
4939 /* Get weighting factors at current particle position... */
4940 const double wpbl = pbl_weight(ctl, atm, ip, pbl, ps);
4941 const double wtrop = tropo_weight(ctl, clim, atm, ip) * (1.0 - wpbl);
4942 const double wstrat = 1.0 - wpbl - wtrop;
4943
4944 /* Set diffusivities [m2/s]... */
4945 const double Kx =
4946 wpbl * ctl->turb_dx_pbl
4947 + wtrop * ctl->turb_dx_trop + wstrat * ctl->turb_dx_strat;
4948
4949 const double Kz =
4950 wpbl * ctl->turb_dz_pbl
4951 + wtrop * ctl->turb_dz_trop + wstrat * ctl->turb_dz_strat;
4952
4953 /* Set time step... */
4954 const double dt_abs = fabs(cache->dt[ip]);
4955
4956 /* Horizontal turbulent diffusion...
4957 Kx [m2/s], dt [s] => sigma_h [m]. */
4958 if (Kx > 0) {
4959 const double sigma_h = sqrt(2.0 * Kx * dt_abs);
4960
4961 atm->lon[ip] +=
4962 DX2COORD(met0, cache->rs[3 * ip] * sigma_h, atm->lat[ip]);
4963
4964 atm->lat[ip] += DY2COORD(met0, cache->rs[3 * ip + 1] * sigma_h);
4965 }
4966
4967 /* Vertical turbulent diffusion... */
4968 if (Kz > 0) {
4969
4970 /* Random displacement:
4971 Kz [m2/s], dt [s] => sigma_z [m], converted to [km]. */
4972 const double sigma_z = sqrt(2.0 * Kz * dt_abs) * 1e-3;
4973
4974 /* Save current pressure because pbl_weight() and tropo_weight()
4975 use atm->p[ip]. */
4976 const double p_save = atm->p[ip];
4977
4978 /* Estimate dKz/dz by centered finite difference.
4979 eps_km = 0.01 km = 10 m.
4980 Positive z is upward; therefore p_up < p_save and p_dn > p_save. */
4981 const double eps_km = 0.01;
4982 const double p_up = p_save + DZ2DP(eps_km, p_save);
4983 const double p_dn = p_save + DZ2DP(-eps_km, p_save);
4984
4985 /* Kz above... */
4986 atm->p[ip] = MAX(ptop, MIN(ps, p_up));
4987 const double wpbl_up = pbl_weight(ctl, atm, ip, pbl, ps);
4988 const double wtrop_up =
4989 tropo_weight(ctl, clim, atm, ip) * (1.0 - wpbl_up);
4990 const double wstrat_up = 1.0 - wpbl_up - wtrop_up;
4991
4992 const double Kz_up =
4993 wpbl_up * ctl->turb_dz_pbl
4994 + wtrop_up * ctl->turb_dz_trop + wstrat_up * ctl->turb_dz_strat;
4995
4996 /* Kz below... */
4997 atm->p[ip] = MAX(ptop, MIN(ps, p_dn));
4998 const double wpbl_dn = pbl_weight(ctl, atm, ip, pbl, ps);
4999 const double wtrop_dn =
5000 tropo_weight(ctl, clim, atm, ip) * (1.0 - wpbl_dn);
5001 const double wstrat_dn = 1.0 - wpbl_dn - wtrop_dn;
5002
5003 const double Kz_dn =
5004 wpbl_dn * ctl->turb_dz_pbl
5005 + wtrop_dn * ctl->turb_dz_trop + wstrat_dn * ctl->turb_dz_strat;
5006
5007 /* Restore current pressure... */
5008 atm->p[ip] = p_save;
5009
5010 /* Well-mixed drift:
5011 w_drift = dKz/dz + Kz * dlnrho/dz
5012
5013 Units:
5014 dKz_dz [m2/s] / [m] = [m/s]
5015 dlnrho_dz [1/m]
5016 Kz*dlnrho [m2/s] * [1/m] = [m/s]
5017 dz_drift [m/s] * [s] * 1e-3 = [km]
5018
5019 With exponential atmosphere rho ~ exp(-z/H0):
5020 dlnrho/dz = -1 / (1000 * H0)
5021 because H0 is in [km]. */
5022 const double dKz_dz = (Kz_up - Kz_dn) / (2.0 * eps_km * 1e3);
5023 const double dlnrho_dz = -1.0 / (1e3 * H0);
5024 const double w_drift = dKz_dz + Kz * dlnrho_dz;
5025 const double dz_drift = w_drift * dt_abs * 1e-3;
5026
5027 /* Total vertical displacement [km]. */
5028 const double dz_tot = cache->rs[3 * ip + 2] * sigma_z + dz_drift;
5029
5030 /* Update particle pressure... */
5031 double ptrial = p_save + DZ2DP(dz_tot, p_save);
5032
5033 /* Reflect at surface and model top.
5034 The transformation p -> pb^2 / p corresponds to reflection in
5035 logarithmic pressure / approximate height coordinates. Use repeated
5036 reflection for robustness in case a large random step crosses more
5037 than one boundary. */
5038 for (int iter = 0; iter < 10; iter++) {
5039 if (ptrial > ps)
5040 ptrial = ps * ps / ptrial;
5041 else if (ptrial < ptop)
5042 ptrial = ptop * ptop / ptrial;
5043 else
5044 break;
5045 }
5046
5047 /* Final safety clamp in case of an exceptionally large displacement... */
5048 atm->p[ip] = MAX(ptop, MIN(ps, ptrial));
5049 }
5050 }
5051}
5052
5053/*****************************************************************************/
5054
5056 const ctl_t *ctl,
5057 const cache_t *cache,
5058 met_t *met0,
5059 met_t *met1,
5060 atm_t *atm) {
5061
5062 /* Set timer... */
5063 SELECT_TIMER("MODULE_DRY_DEPO", "PHYSICS");
5064
5065 /* Check quantity flags... */
5066 if (ctl->qnt_m < 0 && ctl->qnt_vmr < 0)
5067 ERRMSG("Module needs quantity mass or volume mixing ratio!");
5068
5069 /* Loop over particles... */
5070 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
5071
5072 /* Get surface pressure... */
5073 double ps;
5075 INTPOL_2D(ps, 1);
5076
5077 /* Check whether particle is above the surface layer... */
5078 if (atm->p[ip] < ps - ctl->dry_depo_dp)
5079 continue;
5080
5081 /* Set depth of surface layer... */
5082 const double dz = 1000. * (Z(ps - ctl->dry_depo_dp) - Z(ps));
5083
5084 /* Calculate sedimentation velocity for particles... */
5085 double v_dep;
5086 if (ctl->qnt_rp > 0 && ctl->qnt_rhop > 0) {
5087
5088 /* Get temperature... */
5089 double t;
5090 INTPOL_3D(t, 1);
5091
5092 /* Set deposition velocity... */
5093 v_dep = sedi(atm->p[ip], t, atm->q[ctl->qnt_rp][ip],
5094 atm->q[ctl->qnt_rhop][ip]);
5095 }
5096
5097 /* Use explicit sedimentation velocity for gases... */
5098 else
5099 v_dep = ctl->dry_depo_vdep;
5100
5101 /* Calculate loss of mass based on deposition velocity... */
5102 const double aux = exp(-cache->dt[ip] * v_dep / dz);
5103 if (ctl->qnt_m >= 0) {
5104 if (ctl->qnt_mloss_dry >= 0)
5105 atm->q[ctl->qnt_mloss_dry][ip]
5106 += atm->q[ctl->qnt_m][ip] * (1 - aux);
5107 atm->q[ctl->qnt_m][ip] *= aux;
5108 if (ctl->qnt_loss_rate >= 0)
5109 atm->q[ctl->qnt_loss_rate][ip] += v_dep / dz;
5110 }
5111 if (ctl->qnt_vmr >= 0)
5112 atm->q[ctl->qnt_vmr][ip] *= aux;
5113 }
5114}
5115
5116/*****************************************************************************/
5117
5119 const ctl_t *ctl,
5120 const cache_t *cache,
5121 const clim_t *clim,
5122 met_t *met0,
5123 met_t *met1,
5124 atm_t *atm) {
5125
5126 if (ctl->met_coord_type != 0)
5127 ERRMSG("Only lat/lon grid supported");
5128
5129 /* Set timer... */
5130 SELECT_TIMER("MODULE_H2O2_CHEM", "PHYSICS");
5131
5132 /* Check quantity flags... */
5133 if (ctl->qnt_m < 0 && ctl->qnt_vmr < 0)
5134 ERRMSG("Module needs quantity mass or volume mixing ratio!");
5135
5136 /* Parameter of SO2 correction... */
5137 const double low = pow(1. / SO2_CORR_A, 1. / SO2_CORR_B);
5138
5139 /* Loop over particles... */
5140 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
5141
5142 /* Check whether particle is inside cloud... */
5143 double lwc, rwc;
5145 INTPOL_3D(lwc, 1);
5146 INTPOL_3D(rwc, 0);
5147 if (!(lwc > 0 || rwc > 0))
5148 continue;
5149
5150 /* Get temperature... */
5151 double t;
5152 INTPOL_3D(t, 0);
5153
5154 /* Get molecular density... */
5155 const double M = MOLEC_DENS(atm->p[ip], t);
5156
5157 /* Reaction rate (Berglen et al., 2004)... */
5158 const double k = H2O2_SO2_RATE_REF * exp(-H2O2_SO2_RATE_TEMP / RI * (1. / t - 1. / CHEM_REF_TEMP)); /* Maass (1999), M^(-2) */
5159
5160 /* Henry constant of SO2... */
5161 const double H_SO2 =
5163 * (1. / t - 1. / CHEM_REF_TEMP)) * RI * t;
5164 const double K_1S = SO2_DISS_K1_REF * exp(SO2_DISS_K1_TEMP * (1. / t - 1. / CHEM_REF_TEMP)); /* unit: mol/L */
5165
5166 /* Henry constant of H2O2... */
5167 const double H_h2o2 =
5169 * (1. / t - 1. / CHEM_REF_TEMP)) * RI * t;
5170
5171 /* Correction factor for high SO2 concentration
5172 (if qnt_Cx is defined, the correction is switched on)... */
5173 double cor = 1.0;
5174 if (ctl->qnt_Cx >= 0)
5175 cor = atm->q[ctl->qnt_Cx][ip] >
5176 low ? SO2_CORR_A * pow(atm->q[ctl->qnt_Cx][ip], SO2_CORR_B) : 1;
5177
5178 const double h2o2 = H_h2o2
5179 * clim_zm(&clim->h2o2, atm->time[ip], atm->lat[ip], atm->p[ip])
5180 * M * cor * 1000. / AVO; /* unit: mol/L */
5181
5182 /* Volume water content in cloud [m^3 m^(-3)]... */
5183 const double rho_air = atm->p[ip] / (RI * t) * MA / 10.;
5184 const double CWC = (lwc + rwc) * rho_air / 1e3;
5185
5186 /* Calculate exponential decay (Rolph et al., 1992)... */
5187 const double rate_coef = k * K_1S * h2o2 * H_SO2 * CWC;
5188 const double aux = exp(-cache->dt[ip] * rate_coef);
5189 if (ctl->qnt_m >= 0) {
5190 if (ctl->qnt_mloss_h2o2 >= 0)
5191 atm->q[ctl->qnt_mloss_h2o2][ip] += atm->q[ctl->qnt_m][ip] * (1 - aux);
5192 atm->q[ctl->qnt_m][ip] *= aux;
5193 if (ctl->qnt_loss_rate >= 0)
5194 atm->q[ctl->qnt_loss_rate][ip] += rate_coef;
5195 }
5196 if (ctl->qnt_vmr >= 0)
5197 atm->q[ctl->qnt_vmr][ip] *= aux;
5198 }
5199}
5200
5201/*****************************************************************************/
5202
5204 const ctl_t *ctl,
5205 cache_t *cache,
5206 met_t *met0,
5207 met_t *met1,
5208 atm_t *atm) {
5209
5210 double t;
5211
5212 /* Set timer... */
5213 SELECT_TIMER("MODULE_ISOSURF_INIT", "PHYSICS");
5214
5215 /* Save pressure... */
5216 if (ctl->isosurf == 1) {
5217 PARTICLE_LOOP(0, atm->np, 0, "acc data present(ctl,atm)") {
5218 atm->q[ctl->qnt_iso_var][ip] = atm->p[ip];
5219 }
5220 }
5221
5222 /* Save density... */
5223 else if (ctl->isosurf == 2) {
5224 PARTICLE_LOOP(0, atm->np, 0, "acc data present(ctl,met0,met1,atm)") {
5226 INTPOL_3D(t, 1);
5227 atm->q[ctl->qnt_iso_var][ip] = atm->p[ip] / t;
5228 }
5229 }
5230
5231 /* Save potential temperature... */
5232 else if (ctl->isosurf == 3) {
5233 PARTICLE_LOOP(0, atm->np, 0, "acc data present(ctl,met0,met1,atm)") {
5235 INTPOL_3D(t, 1);
5236 atm->q[ctl->qnt_iso_var][ip] = THETA(atm->p[ip], t);
5237 }
5238 }
5239
5240 /* Read balloon pressure data... */
5241 else if (ctl->isosurf == 4) {
5242
5243 /* Write info... */
5244 LOG(1, "Read balloon pressure data: %s", ctl->balloon);
5245
5246 /* Open file... */
5247 FILE *in;
5248 if (!(in = fopen(ctl->balloon, "r")))
5249 ERRMSG("Cannot open file!");
5250
5251 /* Read pressure time series... */
5252 char line[LEN];
5253 while (fgets(line, LEN, in))
5254 if (sscanf(line, "%lg %lg", &(cache->iso_ts[cache->iso_n]),
5255 &(cache->iso_ps[cache->iso_n])) == 2)
5256 if ((++cache->iso_n) > NP)
5257 ERRMSG("Too many data points!");
5258
5259 /* Check number of points... */
5260 if (cache->iso_n < 1)
5261 ERRMSG("Could not read any data!");
5262
5263 /* Close file... */
5264 fclose(in);
5265
5266 /* Update of cache data on device... */
5267 mptrac_update_device(NULL, cache, NULL, NULL, NULL, NULL);
5268 }
5269}
5270
5271/*****************************************************************************/
5272
5274 const ctl_t *ctl,
5275 const cache_t *cache,
5276 met_t *met0,
5277 met_t *met1,
5278 atm_t *atm) {
5279
5280 /* Set timer... */
5281 SELECT_TIMER("MODULE_ISOSURF", "PHYSICS");
5282
5283 /* Loop over particles... */
5284 PARTICLE_LOOP(0, atm->np, 0, "acc data present(ctl,cache,met0,met1,atm)") {
5285
5286 /* Init... */
5287 double t;
5289
5290 /* Restore pressure... */
5291 if (ctl->isosurf == 1)
5292 atm->p[ip] = atm->q[ctl->qnt_iso_var][ip];
5293
5294 /* Restore density... */
5295 else if (ctl->isosurf == 2) {
5296 INTPOL_3D(t, 1);
5297 atm->p[ip] = atm->q[ctl->qnt_iso_var][ip] * t;
5298 }
5299
5300 /* Restore potential temperature... */
5301 else if (ctl->isosurf == 3) {
5302 INTPOL_3D(t, 1);
5303 atm->p[ip] = 1000. * pow(atm->q[ctl->qnt_iso_var][ip] / t, -1. / KAPPA);
5304 }
5305
5306 /* Interpolate pressure... */
5307 else if (ctl->isosurf == 4) {
5308 if (atm->time[ip] <= cache->iso_ts[0])
5309 atm->p[ip] = cache->iso_ps[0];
5310 else if (atm->time[ip] >= cache->iso_ts[cache->iso_n - 1])
5311 atm->p[ip] = cache->iso_ps[cache->iso_n - 1];
5312 else {
5313 const int idx =
5314 locate_irr(cache->iso_ts, cache->iso_n, atm->time[ip]);
5315 atm->p[ip] =
5316 LIN(cache->iso_ts[idx], cache->iso_ps[idx], cache->iso_ts[idx + 1],
5317 cache->iso_ps[idx + 1], atm->time[ip]);
5318 }
5319 }
5320 }
5321}
5322
5323/*****************************************************************************/
5324
5325#ifdef KPP
5326void module_kpp_chem(
5327 ctl_t *ctl,
5328 cache_t *cache,
5329 clim_t *clim,
5330 met_t *met0,
5331 met_t *met1,
5332 atm_t *atm) {
5333
5334 /* Set timer... */
5335 SELECT_TIMER("MODULE_KPP_CHEM", "PHYSICS");
5336
5337 const int nvar = NVAR, nfix = NFIX, nreact = NREACT;
5338 double rtol[1] = { 1.0e-3 };
5339 double atol[1] = { 1.0 };
5340
5341 /* Loop over particles... */
5342#ifdef _OPENACC
5343#pragma acc data copy(rtol,atol,nvar,nfix,nreact)
5344#endif
5345 PARTICLE_LOOP(0, atm->np, 1,
5346 "acc data present(ctl,cache,clim,met0,met1,atm) ") {
5347
5348 /* Initialize... */
5349 double var[nvar], fix[nfix], rconst[nreact];
5350 for (int i = 0; i < nvar; i++)
5351 var[i] = 0.0;
5352 for (int i = 0; i < nfix; i++)
5353 fix[i] = 0.0;
5354 for (int i = 0; i < nreact; i++)
5355 rconst[i] = 0.0;
5356 kpp_chem_initialize(ctl, clim, met0, met1, atm, var, fix, rconst, ip);
5357
5358 /* Integrate... */
5359 double rpar[20];
5360 int ipar[20];
5361 for (int i = 0; i < 20; i++) {
5362 ipar[i] = 0;
5363 rpar[i] = 0.0;
5364 }
5365 ipar[0] = 0; /* 0: F=F(y), i.e. independent of t (autonomous); 0:F=F(t,y), i.e. depends on t (non-autonomous) */
5366 ipar[1] = 1; /* 0: NVAR-dimentional vector of tolerances; 1:scalar tolerances */
5367 ipar[3] = 4; /* choice of the method:Rodas3 */
5368 Rosenbrock(var, fix, rconst, 0, ctl->dt_kpp,
5369 atol, rtol, &FunTemplate, &JacTemplate, rpar, ipar);
5370
5371 /* Save results.. */
5372 kpp_chem_output2atm(atm, ctl, met0, met1, var, ip);
5373 }
5374}
5375#endif
5376
5377/*****************************************************************************/
5378
5380 const ctl_t *ctl,
5381 const cache_t *cache,
5382 const clim_t *clim,
5383 met_t *met0,
5384 met_t *met1,
5385 atm_t *atm) {
5386
5387 /* Set timer... */
5388 SELECT_TIMER("MODULE_METEO", "PHYSICS");
5389
5390 /* Check quantity flags... */
5391 if (ctl->qnt_tsts >= 0)
5392 if (ctl->qnt_tice < 0 || ctl->qnt_tnat < 0)
5393 ERRMSG("Need T_ice and T_NAT to calculate T_STS!");
5394
5395 /* Loop over particles... */
5396 PARTICLE_LOOP(0, atm->np, 0,
5397 "acc data present(ctl,cache,clim,met0,met1,atm)") {
5398
5399 double ps, ts, zs, us, vs, ess, nss, shf, lsm, sst, pbl, pt, pct, pcb,
5400 cl, plcl, plfc, pel, cape, cin, o3c, pv, t, tt, u, v, w, h2o, h2ot,
5401 o3, lwc, rwc, iwc, swc, cc, z, zt, eta_d = 0, wdot = 0;
5402
5403 /* Interpolate meteo data... */
5405 INTPOL_TIME_ALL(atm->time[ip], atm->p[ip], atm->lon[ip], atm->lat[ip]);
5406
5407 /* Diagnose eta coordinate on native model levels... */
5408 if (ctl->qnt_eta_d >= 0)
5409 intpol_met_4d_zeta(met0, met0->pl, met0->zetal,
5410 met1, met1->pl, met1->zetal,
5411 atm->time[ip], atm->p[ip], atm->lon[ip],
5412 atm->lat[ip], &eta_d, ci, cw, 1);
5413
5414 /* Interpolate vertical velocity on native model levels... */
5415 if ((ctl->advect_vert_coord == 1 && ctl->qnt_zeta_dot >= 0)
5416 || (ctl->advect_vert_coord == 3 && ctl->qnt_eta_dot >= 0))
5417 intpol_met_4d_zeta(met0, met0->pl, met0->zeta_dotl,
5418 met1, met1->pl, met1->zeta_dotl,
5419 atm->time[ip], atm->p[ip], atm->lon[ip],
5420 atm->lat[ip], &wdot, ci, cw, 1);
5421
5422 /* Set quantities... */
5423 SET_ATM(qnt_ps, ps);
5424 SET_ATM(qnt_ts, ts);
5425 SET_ATM(qnt_zs, zs);
5426 SET_ATM(qnt_us, us);
5427 SET_ATM(qnt_vs, vs);
5428 SET_ATM(qnt_ess, ess);
5429 SET_ATM(qnt_nss, nss);
5430 SET_ATM(qnt_shf, shf);
5431 SET_ATM(qnt_lsm, lsm);
5432 SET_ATM(qnt_sst, sst);
5433 SET_ATM(qnt_pbl, pbl);
5434 SET_ATM(qnt_pt, pt);
5435 SET_ATM(qnt_tt, tt);
5436 SET_ATM(qnt_zt, zt);
5437 SET_ATM(qnt_h2ot, h2ot);
5438 SET_ATM(qnt_zg, z);
5439 SET_ATM(qnt_p, atm->p[ip]);
5440 SET_ATM(qnt_t, t);
5441 SET_ATM(qnt_rho, RHO(atm->p[ip], t));
5442 SET_ATM(qnt_u, u);
5443 SET_ATM(qnt_v, v);
5444 SET_ATM(qnt_w, w);
5445 SET_ATM(qnt_h2o, h2o);
5446 SET_ATM(qnt_o3, o3);
5447 SET_ATM(qnt_lwc, lwc);
5448 SET_ATM(qnt_rwc, rwc);
5449 SET_ATM(qnt_iwc, iwc);
5450 SET_ATM(qnt_swc, swc);
5451 SET_ATM(qnt_cc, cc);
5452 SET_ATM(qnt_pct, pct);
5453 SET_ATM(qnt_pcb, pcb);
5454 SET_ATM(qnt_cl, cl);
5455 SET_ATM(qnt_plcl, plcl);
5456 SET_ATM(qnt_plfc, plfc);
5457 SET_ATM(qnt_pel, pel);
5458 SET_ATM(qnt_cape, cape);
5459 SET_ATM(qnt_cin, cin);
5460 SET_ATM(qnt_o3c, o3c);
5461 const double lat_ref =
5462 ctl->met_coord_type == 0 ? atm->lat[ip] : ctl->met_utm_ref_lat;
5463 SET_ATM(qnt_hno3,
5464 clim_zm(&clim->hno3, atm->time[ip], lat_ref, atm->p[ip]));
5465 SET_ATM(qnt_oh, clim_oh(ctl, clim, atm->time[ip],
5466 atm->lon[ip], atm->lat[ip], atm->p[ip]));
5467 SET_ATM(qnt_h2o2, clim_zm(&clim->h2o2, atm->time[ip],
5468 lat_ref, atm->p[ip]));
5469 SET_ATM(qnt_ho2, clim_zm(&clim->ho2, atm->time[ip], lat_ref, atm->p[ip]));
5470 SET_ATM(qnt_o1d, clim_zm(&clim->o1d, atm->time[ip], lat_ref, atm->p[ip]));
5471 SET_ATM(qnt_vh, sqrt(u * u + v * v));
5472 SET_ATM(qnt_vz, -1e3 * H0 / atm->p[ip] * w);
5473 SET_ATM(qnt_psat, PSAT(t));
5474 SET_ATM(qnt_psice, PSICE(t));
5475 SET_ATM(qnt_pw, PW(atm->p[ip], h2o));
5476 SET_ATM(qnt_sh, SH(h2o));
5477 SET_ATM(qnt_rh, RH(atm->p[ip], t, h2o));
5478 SET_ATM(qnt_rhice, RHICE(atm->p[ip], t, h2o));
5479 SET_ATM(qnt_theta, THETA(atm->p[ip], t));
5480 SET_ATM(qnt_zeta, atm->q[ctl->qnt_zeta][ip]);
5481 SET_ATM(qnt_zeta_d, ZETA(ps, atm->p[ip], t));
5482 SET_ATM(qnt_eta, atm->q[ctl->qnt_eta][ip]);
5483 SET_ATM(qnt_eta_d, eta_d);
5484 if (ctl->advect_vert_coord == 1) {
5485 SET_ATM(qnt_zeta_dot, wdot);
5486 } else if (ctl->advect_vert_coord == 3) {
5487 SET_ATM(qnt_eta_dot, wdot);
5488 }
5489 SET_ATM(qnt_tvirt, TVIRT(t, h2o));
5490 SET_ATM(qnt_lapse, lapse_rate(t, h2o));
5491 SET_ATM(qnt_pv, pv);
5492 SET_ATM(qnt_tdew, TDEW(atm->p[ip], h2o));
5493 SET_ATM(qnt_tice, TICE(atm->p[ip], h2o));
5494 SET_ATM(qnt_tnat,
5495 nat_temperature(atm->p[ip], h2o,
5496 clim_zm(&clim->hno3, atm->time[ip],
5497 atm->lat[ip], atm->p[ip])));
5498 SET_ATM(qnt_tsts,
5499 0.5 * (atm->q[ctl->qnt_tice][ip] + atm->q[ctl->qnt_tnat][ip]));
5500 }
5501}
5502
5503/*****************************************************************************/
5504
5506 const ctl_t *ctl,
5507 const clim_t *clim,
5508 atm_t *atm,
5509 const double t) {
5510
5511 /* Set timer... */
5512 SELECT_TIMER("MODULE_MIXING", "PHYSICS");
5513
5514 /* Allocate... */
5515 const int np = atm->np;
5516 int *restrict const ixs = (int *) malloc((size_t) np * sizeof(int));
5517 int *restrict const iys = (int *) malloc((size_t) np * sizeof(int));
5518 int *restrict const izs = (int *) malloc((size_t) np * sizeof(int));
5519 double *restrict const mixparam =
5520 (double *) malloc((size_t) np * sizeof(double));
5521 if (ixs == NULL || iys == NULL || izs == NULL || mixparam == NULL)
5522 ERRMSG("Out of memory!");
5523
5524 /* Set grid box size... */
5525 const double dz = (ctl->mixing_z1 - ctl->mixing_z0) / ctl->mixing_nz;
5526 const double dlon = (ctl->mixing_lon1 - ctl->mixing_lon0) / ctl->mixing_nx;
5527 const double dlat = (ctl->mixing_lat1 - ctl->mixing_lat0) / ctl->mixing_ny;
5528
5529 /* Set time interval... */
5530 const double t0 = t - 0.5 * ctl->dt_mod;
5531 const double t1 = t + 0.5 * ctl->dt_mod;
5532 const double dt_mix = ctl->mixing_dt > 0 ? ctl->mixing_dt : ctl->dt_mod;
5533
5534 /* Get indices... */
5535#ifdef _OPENACC
5536#pragma acc enter data create(ixs[0:np],iys[0:np],izs[0:np],mixparam[0:np])
5537#pragma acc data present(ctl,clim,atm,ixs,iys,izs,mixparam)
5538#pragma acc parallel loop independent gang vector
5539#else
5540#pragma omp parallel for default(shared)
5541#endif
5542 for (int ip = 0; ip < np; ip++) {
5543 const double zpart = Z(atm->p[ip]);
5544 if (atm->time[ip] < t0 || atm->time[ip] > t1
5545 || atm->lon[ip] < ctl->mixing_lon0
5546 || atm->lon[ip] >= ctl->mixing_lon1
5547 || atm->lat[ip] < ctl->mixing_lat0
5548 || atm->lat[ip] >= ctl->mixing_lat1
5549 || zpart < ctl->mixing_z0 || zpart >= ctl->mixing_z1) {
5550 izs[ip] = -1;
5551 continue;
5552 }
5553 ixs[ip] = (int) ((atm->lon[ip] - ctl->mixing_lon0) / dlon);
5554 iys[ip] = (int) ((atm->lat[ip] - ctl->mixing_lat0) / dlat);
5555 izs[ip] = (int) ((zpart - ctl->mixing_z0) / dz);
5556 if (ixs[ip] >= ctl->mixing_nx || iys[ip] >= ctl->mixing_ny
5557 || izs[ip] >= ctl->mixing_nz) {
5558 izs[ip] = -1;
5559 continue;
5560 }
5561 const double w = tropo_weight(ctl, clim, atm, ip);
5562 const double rate =
5563 w / ctl->mixing_tau_trop + (1.0 - w) / ctl->mixing_tau_strat;
5564 mixparam[ip] = -expm1(-dt_mix * rate);
5565 }
5566
5567 /* Calculate interparcel mixing... */
5568 const int use_ensemble = (ctl->nens > 0);
5569
5570 const int quantities[] = {
5571 ctl->qnt_m, ctl->qnt_vmr, ctl->qnt_Ch2o, ctl->qnt_Co3,
5572 ctl->qnt_Cco, ctl->qnt_Coh, ctl->qnt_Ch, ctl->qnt_Cho2,
5573 ctl->qnt_Ch2o2, ctl->qnt_Co1d, ctl->qnt_Co3p, ctl->qnt_Cccl4,
5574 ctl->qnt_Cccl3f, ctl->qnt_Cccl2f2, ctl->qnt_Cn2o,
5575 ctl->qnt_Csf6, ctl->qnt_aoa, ctl->qnt_Arn222, ctl->qnt_Apb210,
5576 ctl->qnt_Abe7, ctl->qnt_Acs137, ctl->qnt_Ai131, ctl->qnt_Axe133
5577 };
5578 const int n_qnt = sizeof(quantities) / sizeof(quantities[0]);
5579
5580 for (int i = 0; i < n_qnt; i++)
5581 if (quantities[i] >= 0)
5582 module_mixing_help(ctl, atm, ixs, iys, izs, mixparam, quantities[i],
5583 use_ensemble);
5584
5585 /* Free... */
5586#ifdef _OPENACC
5587#pragma acc exit data delete(ixs,iys,izs,mixparam)
5588#endif
5589 free(ixs);
5590 free(iys);
5591 free(izs);
5592 free(mixparam);
5593}
5594
5595/*****************************************************************************/
5596
5598 const ctl_t *ctl,
5599 atm_t *atm,
5600 const int *ixs,
5601 const int *iys,
5602 const int *izs,
5603 const double *mixparam,
5604 const int qnt_idx,
5605 const int use_ensemble) {
5606
5607 const int np = atm->np;
5608 const int ngrid = ctl->mixing_nx * ctl->mixing_ny * ctl->mixing_nz;
5609 const int nens = use_ensemble ? ctl->nens : 1;
5610 const int total_grid = ngrid * nens;
5611
5612 double *restrict const cmean =
5613 (double *) malloc((size_t) total_grid * sizeof(double));
5614 double *restrict const cweight =
5615 (double *) malloc((size_t) total_grid * sizeof(double));
5616 if (cmean == NULL || cweight == NULL)
5617 ERRMSG("Out of memory!");
5618
5619 /* Init... */
5620#ifdef _OPENACC
5621#pragma acc enter data create(cmean[0:total_grid],cweight[0:total_grid])
5622#pragma acc data present(ctl,atm,ixs,iys,izs,mixparam,cmean,cweight)
5623#pragma acc parallel loop independent gang vector
5624#else
5625#ifdef __NVCOMPILER
5626#pragma novector
5627#endif
5628#pragma omp parallel for
5629#endif
5630 for (int i = 0; i < total_grid; i++) {
5631 cmean[i] = 0.0;
5632 cweight[i] = 0.0;
5633 }
5634
5635 /* Loop over particles... */
5636#ifdef _OPENACC
5637#pragma acc parallel loop independent gang vector
5638#endif
5639 for (int ip = 0; ip < np; ip++)
5640 if (izs[ip] >= 0) {
5641 const int ens = use_ensemble ? (int) atm->q[ctl->qnt_ens][ip] : 0;
5642 if (ens < 0 || ens >= nens)
5643 continue;
5644 const int idx =
5645 ens * ngrid + ARRAY_3D(ixs[ip], iys[ip], ctl->mixing_ny, izs[ip],
5646 ctl->mixing_nz);
5647#ifdef _OPENACC
5648#pragma acc atomic update
5649#endif
5650 cmean[idx] += mixparam[ip] * atm->q[qnt_idx][ip];
5651#ifdef _OPENACC
5652#pragma acc atomic update
5653#endif
5654 cweight[idx] += mixparam[ip];
5655 }
5656
5657 /* Compute means... */
5658#ifdef _OPENACC
5659#pragma acc parallel loop independent gang vector
5660#else
5661#ifdef __NVCOMPILER
5662#pragma novector
5663#endif
5664#pragma omp parallel for
5665#endif
5666 for (int i = 0; i < total_grid; i++)
5667 if (cweight[i] > 0)
5668 cmean[i] /= cweight[i];
5669
5670 /* Interparcel mixing... */
5671#ifdef _OPENACC
5672#pragma acc parallel loop independent gang vector
5673#else
5674#pragma omp parallel for
5675#endif
5676 for (int ip = 0; ip < np; ip++) {
5677 if (izs[ip] >= 0) {
5678 const int ens = use_ensemble ? (int) atm->q[ctl->qnt_ens][ip] : 0;
5679
5680 if (ens < 0 || ens >= nens)
5681 continue;
5682 const int idx =
5683 ens * ngrid + ARRAY_3D(ixs[ip], iys[ip], ctl->mixing_ny, izs[ip],
5684 ctl->mixing_nz);
5685 atm->q[qnt_idx][ip] +=
5686 (cmean[idx] - atm->q[qnt_idx][ip]) * mixparam[ip];
5687 }
5688 }
5689
5690 /* Free... */
5691#ifdef _OPENACC
5692#pragma acc exit data delete(cmean,cweight)
5693#endif
5694 free(cmean);
5695 free(cweight);
5696}
5697
5698/*****************************************************************************/
5699
5701 const ctl_t *ctl,
5702 const cache_t *cache,
5703 const clim_t *clim,
5704 met_t *met0,
5705 met_t *met1,
5706 atm_t *atm) {
5707
5708 /* Set timer... */
5709 SELECT_TIMER("MODULE_OH_CHEM", "PHYSICS");
5710
5711 /* Check quantity flags... */
5712 if (ctl->qnt_m < 0 && ctl->qnt_vmr < 0)
5713 ERRMSG("Module needs quantity mass or volume mixing ratio!");
5714
5715 /* Parameter of SO2 correction... */
5716 const double a = 4.71572206e-08;
5717 const double b = -8.28782867e-01;
5718 const double low = pow(1. / a, 1. / b);
5719
5720 /* Loop over particles... */
5721 PARTICLE_LOOP(0, atm->np, 1,
5722 "acc data present(ctl,cache,clim,met0,met1,atm)") {
5723
5724 /* Get temperature... */
5725 double t;
5727 INTPOL_3D(t, 1);
5728
5729 /* Calculate molecular density... */
5730 const double M = MOLEC_DENS(atm->p[ip], t);
5731
5732 /* Use constant reaction rate... */
5733 double k = NAN;
5734 if (ctl->oh_chem_reaction == 1)
5735 k = ctl->oh_chem[0];
5736
5737 /* Calculate bimolecular reaction rate... */
5738 else if (ctl->oh_chem_reaction == 2)
5739 k = ctl->oh_chem[0] * exp(-ctl->oh_chem[1] / t);
5740
5741 /* Calculate termolecular reaction rate... */
5742 if (ctl->oh_chem_reaction == 3) {
5743
5744 /* Calculate rate coefficient for X + OH + M -> XOH + M
5745 (JPL Publication 19-05) ... */
5746 const double k0 =
5747 ctl->oh_chem[0] * (ctl->oh_chem[1] !=
5748 0 ? pow(298. / t, ctl->oh_chem[1]) : 1.);
5749 const double ki =
5750 ctl->oh_chem[2] * (ctl->oh_chem[3] !=
5751 0 ? pow(298. / t, ctl->oh_chem[3]) : 1.);
5752 const double c = log10(k0 * M / ki);
5753 k = k0 * M / (1. + k0 * M / ki) * pow(0.6, 1. / (1. + c * c));
5754 }
5755
5756 /* Correction factor for high SO2 concentration
5757 (if qnt_Cx is defined, the correction is switched on)... */
5758 double cor = 1;
5759 if (ctl->qnt_Cx >= 0)
5760 cor =
5761 atm->q[ctl->qnt_Cx][ip] >
5762 low ? a * pow(atm->q[ctl->qnt_Cx][ip], b) : 1;
5763
5764 /* Calculate exponential decay... */
5765 const double rate_coef =
5766 k * clim_oh(ctl, clim, atm->time[ip], atm->lon[ip],
5767 atm->lat[ip], atm->p[ip]) * M * cor;
5768 const double aux = exp(-cache->dt[ip] * rate_coef);
5769 if (ctl->qnt_m >= 0) {
5770 if (ctl->qnt_mloss_oh >= 0)
5771 atm->q[ctl->qnt_mloss_oh][ip]
5772 += atm->q[ctl->qnt_m][ip] * (1 - aux);
5773 atm->q[ctl->qnt_m][ip] *= aux;
5774 if (ctl->qnt_loss_rate >= 0)
5775 atm->q[ctl->qnt_loss_rate][ip] += rate_coef;
5776 }
5777 if (ctl->qnt_vmr >= 0)
5778 atm->q[ctl->qnt_vmr][ip] *= aux;
5779 }
5780}
5781
5782/*****************************************************************************/
5783
5785 const cache_t *cache,
5786 met_t *met0,
5787 met_t *met1,
5788 atm_t *atm,
5789 const int reflect) {
5790
5791 /* Set timer... */
5792 SELECT_TIMER("MODULE_POSITION", "PHYSICS");
5793
5794 /* Loop over particles... */
5795 PARTICLE_LOOP(0, atm->np, 1, "acc data present(cache,met0,met1,atm)") {
5796
5797 /* Init... */
5798 double ps;
5800
5801 if (met0->coord_type == 0) {
5802 /* Calculate modulo... */
5803 atm->lon[ip] = FMOD(atm->lon[ip], 360.);
5804 atm->lat[ip] = FMOD(atm->lat[ip], 360.);
5805
5806 /* Check latitude... */
5807 while (atm->lat[ip] < -90 || atm->lat[ip] > 90) {
5808 if (atm->lat[ip] > 90) {
5809 atm->lat[ip] = 180 - atm->lat[ip];
5810 atm->lon[ip] += 180;
5811 }
5812 if (atm->lat[ip] < -90) {
5813 atm->lat[ip] = -180 - atm->lat[ip];
5814 atm->lon[ip] += 180;
5815 }
5816 }
5817
5818 /* Check longitude... */
5819 while (atm->lon[ip] < -180)
5820 atm->lon[ip] += 360;
5821 while (atm->lon[ip] >= 180)
5822 atm->lon[ip] -= 360;
5823 } else {
5824 intpol_check_cartesian(met0->lon, met0->nx, met0->lat, met0->ny,
5825 atm->lon[ip], atm->lat[ip], &atm->lon[ip],
5826 &atm->lat[ip]);
5827 }
5828
5829 /* Check pressure... */
5830 const double ptop = met0->p[met0->np - 1];
5831 if (atm->p[ip] < ptop) {
5832 atm->p[ip] = reflect ? ptop * ptop / atm->p[ip] : ptop;
5833 } else if (atm->p[ip] > 300.) {
5834 INTPOL_2D(ps, 1);
5835 if (atm->p[ip] > ps)
5836 atm->p[ip] = reflect ? ps * ps / atm->p[ip] : ps;
5837 }
5838 }
5839}
5840
5841/*****************************************************************************/
5842
5844 const ctl_t *ctl,
5845 const cache_t *cache,
5846 atm_t *atm) {
5847
5848 /* Set timer... */
5849 SELECT_TIMER("MODULE_RADIO_DECAY", "PHYSICS");
5850
5851 /* Set decay constants of radioactive species [s^-1]... */
5852 const double lambda_rn222 = log(2.0) / RADIO_HALF_LIFE_RN222;
5853 const double lambda_pb210 = log(2.0) / RADIO_HALF_LIFE_PB210;
5854 const double lambda_be7 = log(2.0) / RADIO_HALF_LIFE_BE7;
5855 const double lambda_cs137 = log(2.0) / RADIO_HALF_LIFE_CS137;
5856 const double lambda_i131 = log(2.0) / RADIO_HALF_LIFE_I131;
5857 const double lambda_xe133 = log(2.0) / RADIO_HALF_LIFE_XE133;
5858
5859 /* Loop over particles... */
5860 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,atm)") {
5861
5862 /* Set timestep... */
5863 const double dt = cache->dt[ip];
5864
5865 /* Loss for Pb-210... */
5866 if (ctl->qnt_Apb210 >= 0)
5867 atm->q[ctl->qnt_Apb210][ip] *= exp(-dt * lambda_pb210);
5868
5869 /* Loss for Rn-222... */
5870 if (ctl->qnt_Arn222 >= 0) {
5871 const double old = atm->q[ctl->qnt_Arn222][ip];
5872 const double aux = exp(-dt * lambda_rn222);
5873 const double lost = old * (1.0 - aux);
5874 atm->q[ctl->qnt_Arn222][ip] = old * aux;
5875
5876 /* Parent-daughter process for Pb-210... */
5877 if (ctl->qnt_Apb210 >= 0)
5878 atm->q[ctl->qnt_Apb210][ip] += lost * lambda_pb210 / lambda_rn222;
5879 }
5880
5881 /* Loss for Be-7... */
5882 if (ctl->qnt_Abe7 >= 0)
5883 atm->q[ctl->qnt_Abe7][ip] *= exp(-dt * lambda_be7);
5884
5885 /* Loss for Cs-137... */
5886 if (ctl->qnt_Acs137 >= 0)
5887 atm->q[ctl->qnt_Acs137][ip] *= exp(-dt * lambda_cs137);
5888
5889 /* Loss for I-131... */
5890 if (ctl->qnt_Ai131 >= 0)
5891 atm->q[ctl->qnt_Ai131][ip] *= exp(-dt * lambda_i131);
5892
5893 /* Loss for Xe-133... */
5894 if (ctl->qnt_Axe133 >= 0)
5895 atm->q[ctl->qnt_Axe133][ip] *= exp(-dt * lambda_xe133);
5896 }
5897}
5898
5899/*****************************************************************************/
5900
5902 const ctl_t *ctl,
5903 const cache_t *cache,
5904 met_t *met0,
5905 met_t *met1,
5906 atm_t *atm,
5907 depo_t *depo) {
5908
5909 /* Set timer... */
5910 SELECT_TIMER("MODULE_RADIO_DEPO", "PHYSICS");
5911
5912 /* Decay constants of deposited radionuclides [s^-1]... */
5913 const double lambda_pb210 = log(2.0) / RADIO_HALF_LIFE_PB210;
5914 const double lambda_be7 = log(2.0) / RADIO_HALF_LIFE_BE7;
5915 const double lambda_cs137 = log(2.0) / RADIO_HALF_LIFE_CS137;
5916 const double lambda_i131 = log(2.0) / RADIO_HALF_LIFE_I131;
5917
5918 /* Set horizontal grid increments... */
5919 const double dlon = (ctl->grid_lon1 - ctl->grid_lon0) / ctl->grid_nx;
5920 const double dlat = (ctl->grid_lat1 - ctl->grid_lat0) / ctl->grid_ny;
5921
5922 /* Loop over particles... */
5923 PARTICLE_LOOP(0, atm->np, 1,
5924 "acc data present(ctl,cache,met0,met1,atm,depo)") {
5925
5926 /* Deposition is only defined for forward integration... */
5927 const double dt = cache->dt[ip];
5928 if (dt <= 0)
5929 continue;
5930
5931 /* Get surface pressure... */
5932 double ps;
5934 INTPOL_2D(ps, 1);
5935
5936 /* Calculate dry deposition rate [s^-1]... */
5937 double dry_pb210 = 0, dry_be7 = 0, dry_cs137 = 0, dry_i131 = 0;
5938 if (atm->p[ip] >= ps - ctl->dry_depo_dp) {
5939 const double dz = 1000. * (Z(ps - ctl->dry_depo_dp) - Z(ps));
5940 if (dz > 0) {
5941 dry_pb210 = RADIO_DRY_VDEP_PB210 / dz;
5942 dry_be7 = RADIO_DRY_VDEP_BE7 / dz;
5943 dry_cs137 = RADIO_DRY_VDEP_CS137 / dz;
5944 dry_i131 = RADIO_DRY_VDEP_I131 / dz;
5945 }
5946 }
5947
5948 /* Calculate wet deposition rate [s^-1]... */
5949 double wet_pb210 = 0, wet_be7 = 0, wet_cs137 = 0, wet_i131 = 0;
5950 double pct;
5951 INTPOL_2D(pct, 1);
5952 if (isfinite(pct) && atm->p[ip] > pct) {
5953 double cl;
5954 INTPOL_2D(cl, 0);
5955 if (cl > 0) {
5956 const double Is =
5957 pow(cl / ctl->wet_depo_pre[0], 1. / ctl->wet_depo_pre[1]);
5958 if (Is >= 0.01) {
5959 double lwc, rwc, iwc, swc, t;
5960 INTPOL_3D(lwc, 1);
5961 INTPOL_3D(rwc, 0);
5962 INTPOL_3D(iwc, 0);
5963 INTPOL_3D(swc, 0);
5964 INTPOL_3D(t, 0);
5965 const int inside = (lwc > 0 || rwc > 0 || iwc > 0 || swc > 0);
5966 double eta;
5967 if (inside) {
5968 if (t > WET_DEPO_T_LIQUID)
5969 eta = 1;
5970 else if (t <= WET_DEPO_T_ICE)
5971 eta = ctl->wet_depo_ic_ret_ratio;
5972 else
5974 ctl->wet_depo_ic_ret_ratio, t);
5975 } else
5976 eta = (t > WET_DEPO_T_LIQUID_BC ? 1 : ctl->wet_depo_bc_ret_ratio);
5977 wet_pb210 = RADIO_WET_COEFF_PB210 * Is * eta;
5978 wet_be7 = RADIO_WET_COEFF_BE7 * Is * eta;
5979 wet_cs137 = RADIO_WET_COEFF_CS137 * Is * eta;
5980 wet_i131 = RADIO_WET_COEFF_I131 * Is * eta;
5981 }
5982 }
5983 }
5984
5985 /* Get deposition grid index... */
5986 const int ingrid =
5987 (atm->lon[ip] >= ctl->grid_lon0
5988 && atm->lon[ip] < ctl->grid_lon1
5989 && atm->lat[ip] >= ctl->grid_lat0 && atm->lat[ip] < ctl->grid_lat1);
5990 const int ix = ingrid
5991 ? (int) ((atm->lon[ip] - ctl->grid_lon0) / dlon) : 0;
5992 const int iy = ingrid
5993 ? (int) ((atm->lat[ip] - ctl->grid_lat0) / dlat) : 0;
5994 const int idx = (ingrid ? ARRAY_2D(ix, iy, ctl->grid_ny) : 0);
5995 const double tref = atm->time[ip] - ctl->t_start;
5996
5997 /* Deposit Pb-210... */
5998 if (ctl->qnt_Apb210 >= 0) {
5999 const double old = atm->q[ctl->qnt_Apb210][ip];
6000 const double aux = exp(-dt * (dry_pb210 + wet_pb210));
6001 const double lost = old * (1. - aux);
6002 const double deposited = lost
6003 * (ctl->radio_decay ? exp(lambda_pb210 * tref) : 1.0);
6004 atm->q[ctl->qnt_Apb210][ip] = old * aux;
6005 if (ingrid && lost > 0) {
6006#ifdef _OPENACC
6007#pragma acc atomic update
6008#else
6009#pragma omp atomic update
6010#endif
6011 depo->Apb210[idx] += deposited;
6012 }
6013 }
6014
6015 /* Deposit Be-7... */
6016 if (ctl->qnt_Abe7 >= 0) {
6017 const double old = atm->q[ctl->qnt_Abe7][ip];
6018 const double aux = exp(-dt * (dry_be7 + wet_be7));
6019 const double lost = old * (1. - aux);
6020 const double deposited = lost
6021 * (ctl->radio_decay ? exp(lambda_be7 * tref) : 1.0);
6022 atm->q[ctl->qnt_Abe7][ip] = old * aux;
6023 if (ingrid && lost > 0) {
6024#ifdef _OPENACC
6025#pragma acc atomic update
6026#else
6027#pragma omp atomic update
6028#endif
6029 depo->Abe7[idx] += deposited;
6030 }
6031 }
6032
6033 /* Deposit Cs-137... */
6034 if (ctl->qnt_Acs137 >= 0) {
6035 const double old = atm->q[ctl->qnt_Acs137][ip];
6036 const double aux = exp(-dt * (dry_cs137 + wet_cs137));
6037 const double lost = old * (1. - aux);
6038 const double deposited = lost
6039 * (ctl->radio_decay ? exp(lambda_cs137 * tref) : 1.0);
6040 atm->q[ctl->qnt_Acs137][ip] = old * aux;
6041 if (ingrid && lost > 0) {
6042#ifdef _OPENACC
6043#pragma acc atomic update
6044#else
6045#pragma omp atomic update
6046#endif
6047 depo->Acs137[idx] += deposited;
6048 }
6049 }
6050
6051 /* Deposit aerosol-bound I-131... */
6052 if (ctl->qnt_Ai131 >= 0) {
6053 const double old = atm->q[ctl->qnt_Ai131][ip];
6054 const double aux = exp(-dt * (dry_i131 + wet_i131));
6055 const double lost = old * (1. - aux);
6056 const double deposited = lost
6057 * (ctl->radio_decay ? exp(lambda_i131 * tref) : 1.0);
6058 atm->q[ctl->qnt_Ai131][ip] = old * aux;
6059 if (ingrid && lost > 0) {
6060#ifdef _OPENACC
6061#pragma acc atomic update
6062#else
6063#pragma omp atomic update
6064#endif
6065 depo->Ai131[idx] += deposited;
6066 }
6067 }
6068 }
6069}
6070
6071/*****************************************************************************/
6072
6074 const int ntask) {
6075
6076 /* Initialize GSL random number generators... */
6077 gsl_rng_env_setup();
6078 if (omp_get_max_threads() > NTHREADS)
6079 ERRMSG("Too many threads!");
6080 for (int i = 0; i < NTHREADS; i++) {
6081 rng[i] = gsl_rng_alloc(gsl_rng_default);
6082 gsl_rng_set(rng[i], gsl_rng_default_seed
6083 + (long unsigned) (ntask * NTHREADS + i));
6084 }
6085
6086 /* Initialize cuRAND random number generators... */
6087#ifdef CURAND
6088 if (curandCreateGenerator(&rng_curand, CURAND_RNG_PSEUDO_DEFAULT) !=
6089 CURAND_STATUS_SUCCESS)
6090 ERRMSG("Cannot create random number generator!");
6091 if (curandSetPseudoRandomGeneratorSeed(rng_curand, ntask) !=
6092 CURAND_STATUS_SUCCESS)
6093 ERRMSG("Cannot set seed for random number generator!");
6094 if (curandSetStream
6095 (rng_curand,
6096 (cudaStream_t) acc_get_cuda_stream(acc_async_sync)) !=
6097 CURAND_STATUS_SUCCESS)
6098 ERRMSG("Cannot set stream for random number generator!");
6099#endif
6100}
6101
6102/*****************************************************************************/
6103
6105 const ctl_t *ctl,
6106 double *rs,
6107 const size_t n,
6108 const int method) {
6109
6110 /* Use GSL random number generators... */
6111 if (ctl->rng_type == 0) {
6112
6113 /* Uniform distribution... */
6114 if (method == 0) {
6115#pragma omp parallel for default(shared)
6116 for (size_t i = 0; i < n; ++i)
6117 rs[i] = gsl_rng_uniform(rng[omp_get_thread_num()]);
6118 }
6119
6120 /* Normal distribution... */
6121 else if (method == 1) {
6122#pragma omp parallel for default(shared)
6123 for (size_t i = 0; i < n; ++i)
6124 rs[i] = gsl_ran_gaussian_ziggurat(rng[omp_get_thread_num()], 1.0);
6125 }
6126
6127 /* Update of random numbers on device... */
6128#ifdef _OPENACC
6129 SELECT_TIMER("UPDATE_DEVICE", "MEMORY");
6130#pragma acc update device(rs[:n])
6131#endif
6132 }
6133
6134 /* Use Squares random number generator (Widynski, 2022)... */
6135 else if (ctl->rng_type == 1) {
6136
6137 /* Set key (don't change this!)... */
6138 const uint64_t key = 0xc8e4fd154ce32f6d;
6139
6140 /* Uniform distribution... */
6141#ifdef _OPENACC
6142#pragma acc data present(rs)
6143#pragma acc parallel loop independent gang vector
6144#else
6145#pragma omp parallel for default(shared)
6146#endif
6147 for (size_t i = 0; i < n + 1; ++i) {
6148 uint64_t r, t, x, y, z;
6149 y = x = (rng_ctr + i) * key;
6150 z = y + key;
6151 x = x * x + y;
6152 x = (x >> 32) | (x << 32);
6153 x = x * x + z;
6154 x = (x >> 32) | (x << 32);
6155 x = x * x + y;
6156 x = (x >> 32) | (x << 32);
6157 t = x = x * x + z;
6158 x = (x >> 32) | (x << 32);
6159 r = t ^ ((x * x + y) >> 32);
6160 rs[i] = (double) r / (double) UINT64_MAX;
6161 }
6162 rng_ctr += n + 1;
6163
6164 /* Normal distribution... */
6165 if (method == 1) {
6166#ifdef _OPENACC
6167#pragma acc parallel loop independent gang vector
6168#else
6169#pragma omp parallel for default(shared)
6170#endif
6171 for (size_t i = 0; i < n; i += 2) {
6172 const double r = sqrt(-2.0 * log(rs[i]));
6173 const double phi = 2.0 * M_PI * rs[i + 1];
6174 rs[i] = r * cosf((float) phi);
6175 rs[i + 1] = r * sinf((float) phi);
6176 }
6177 }
6178 }
6179
6180 /* Use cuRAND random number generators... */
6181 else if (ctl->rng_type == 2) {
6182#ifdef CURAND
6183#pragma acc host_data use_device(rs)
6184 {
6185
6186 /* Uniform distribution... */
6187 if (method == 0) {
6188 if (curandGenerateUniformDouble(rng_curand, rs, (n < 4 ? 4 : n)) !=
6189 CURAND_STATUS_SUCCESS)
6190 ERRMSG("Cannot create random numbers!");
6191 }
6192
6193 /* Normal distribution... */
6194 else if (method == 1) {
6195 if (curandGenerateNormalDouble
6196 (rng_curand, rs, (n < 4 ? 4 : n), 0.0,
6197 1.0) != CURAND_STATUS_SUCCESS)
6198 ERRMSG("Cannot create random numbers!");
6199 }
6200 }
6201#else
6202 ERRMSG("MPTRAC was compiled without cuRAND!");
6203#endif
6204 }
6205}
6206
6207/*****************************************************************************/
6208
6210 const ctl_t *ctl,
6211 const cache_t *cache,
6212 met_t *met0,
6213 met_t *met1,
6214 atm_t *atm) {
6215
6216 /* Set timer... */
6217 SELECT_TIMER("MODULE_SEDI", "PHYSICS")
6218 /* Loop over particles... */
6219 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
6220
6221 /* Get temperature... */
6222 double t;
6224 INTPOL_3D(t, 1);
6225
6226 /* Sedimentation velocity... */
6227 const double v_s = sedi(atm->p[ip], t, atm->q[ctl->qnt_rp][ip],
6228 atm->q[ctl->qnt_rhop][ip]);
6229
6230 /* Calculate pressure change... */
6231 atm->p[ip] += DZ2DP(v_s * cache->dt[ip] / 1000., atm->p[ip]);
6232 }
6233}
6234
6235/*****************************************************************************/
6236
6238 const ctl_t *ctl,
6239 const met_t *met0,
6240 cache_t *cache,
6241 atm_t *atm) {
6242
6243 /* Set timer... */
6244 SELECT_TIMER("MODULE_SORT", "PHYSICS");
6245
6246 /* Allocate... */
6247 const int np = atm->np;
6248 double *restrict const a = (double *) malloc((size_t) np * sizeof(double));
6249 int *restrict const p = (int *) malloc((size_t) np * sizeof(int));
6250 if (a == NULL || p == NULL)
6251 ERRMSG("Out of memory!");
6252
6253#ifdef _OPENACC
6254#pragma acc enter data create(a[0:np],p[0:np])
6255#pragma acc data present(ctl,met0,cache,atm,a,p)
6256#endif
6257
6258 /* Get box index... */
6259#ifdef _OPENACC
6260#pragma acc parallel loop independent gang vector
6261#else
6262#pragma omp parallel for default(shared)
6263#endif
6264 for (int ip = 0; ip < np; ip++) {
6265 a[ip] =
6266 (double) ((locate_reg(met0->lon, met0->nx, atm->lon[ip]) * met0->ny +
6267 locate_irr(met0->lat, met0->ny, atm->lat[ip]))
6268 * met0->np + locate_irr(met0->p, met0->np, atm->p[ip]));
6269 p[ip] = ip;
6270 }
6271
6272 /* Sorting... */
6273#ifdef THRUST
6274#ifdef _OPENACC
6275#pragma acc host_data use_device(a,p)
6276#endif
6277 thrustSortWrapper(a, np, p);
6278#else
6279 size_t *perm_sz = (size_t *) malloc((size_t) np * sizeof(size_t));
6280 if (perm_sz == NULL)
6281 ERRMSG("Out of memory!");
6282#ifdef _OPENACC
6283#pragma acc update self(a[0:np])
6284#endif
6285 gsl_sort_index(perm_sz, a, 1, (size_t) np);
6286 for (int ip = 0; ip < np; ++ip)
6287 p[ip] = (int) perm_sz[ip];
6288 free(perm_sz);
6289#ifdef _OPENACC
6290#pragma acc update device(p[0:np])
6291#endif
6292#endif
6293
6294 /* Sort data... */
6295 sort_array(atm->time, p, a, np);
6296 sort_array(atm->p, p, a, np);
6297 sort_array(atm->lon, p, a, np);
6298 sort_array(atm->lat, p, a, np);
6299 for (int iq = 0; iq < ctl->nq; iq++)
6300 sort_array(atm->q[iq], p, a, np);
6301 sort_array(cache->dt, p, a, np);
6302 sort_uvwp(cache, p, a, np);
6303
6304 /* Free... */
6305#ifdef _OPENACC
6306#pragma acc exit data delete(a,p)
6307#endif
6308 free(a);
6309 free(p);
6310}
6311
6312/*****************************************************************************/
6313
6315 const ctl_t *ctl,
6316 cache_t *cache,
6317 const met_t *met0,
6318 atm_t *atm,
6319 const double t) {
6320
6321 /* Set timer... */
6322 SELECT_TIMER("MODULE_TIMESTEPS", "PHYSICS");
6323
6324 const double latmin = gsl_stats_min(met0->lat, 1, (size_t) met0->ny),
6325 latmax = gsl_stats_max(met0->lat, 1, (size_t) met0->ny);
6326
6327 const int local =
6328 (fabs(met0->lon[met0->nx - 1] - met0->lon[0] - 360.0) >= 0.01);
6329
6330 /* Loop over particles... */
6331 PARTICLE_LOOP(0, atm->np, 0, "acc data present(ctl,cache,met0,atm)") {
6332
6333 /* Set time step for each air parcel... */
6334 if ((ctl->direction * (atm->time[ip] - ctl->t_start) >= 0
6335 && ctl->direction * (atm->time[ip] - ctl->t_stop) <= 0
6336 && ctl->direction * (atm->time[ip] - t) < 0))
6337 cache->dt[ip] = t - atm->time[ip];
6338 else
6339 cache->dt[ip] = 0.0;
6340
6341 /* Check horizontal boundaries of local meteo data... */
6342#ifdef DD
6343 /* With domain decomposition, the subdomain rather than the meteo grid
6344 defines the boundaries of a trajectory... */
6345 int dd = (ctl->dd) ? 0 : 1;
6346#else
6347 int dd = 1;
6348#endif
6349 if (dd) {
6350 if (local && (atm->lon[ip] <= met0->lon[0]
6351 || atm->lon[ip] >= met0->lon[met0->nx - 1]
6352 || atm->lat[ip] <= latmin || atm->lat[ip] >= latmax))
6353 cache->dt[ip] = 0.0;
6354 } else {
6355 if ((int) atm->q[ctl->qnt_current_subdomain][ip] == -1)
6356 cache->dt[ip] = 0;
6357 }
6358 }
6359}
6360
6361/*****************************************************************************/
6362
6364 ctl_t *ctl,
6365 const atm_t *atm) {
6366
6367 /* Set timer... */
6368 SELECT_TIMER("MODULE_TIMESTEPS_INIT", "PHYSICS");
6369
6370 /* Set start time... */
6371 if (ctl->direction == 1) {
6372 ctl->t_start = gsl_stats_min(atm->time, 1, (size_t) atm->np);
6373 if (ctl->t_stop > 1e99)
6374 ctl->t_stop = gsl_stats_max(atm->time, 1, (size_t) atm->np);
6375 } else {
6376 ctl->t_start = gsl_stats_max(atm->time, 1, (size_t) atm->np);
6377 if (ctl->t_stop > 1e99)
6378 ctl->t_stop = gsl_stats_min(atm->time, 1, (size_t) atm->np);
6379 }
6380
6381 /* Check time interval... */
6382 if (ctl->direction * (ctl->t_stop - ctl->t_start) <= 0)
6383 ERRMSG("Nothing to do! Check T_STOP and DIRECTION!");
6384
6385 /* Round start time... */
6386 if (ctl->direction == 1)
6387 ctl->t_start = floor(ctl->t_start / ctl->dt_mod) * ctl->dt_mod;
6388 else
6389 ctl->t_start = ceil(ctl->t_start / ctl->dt_mod) * ctl->dt_mod;
6390}
6391
6392/*****************************************************************************/
6393
6395 const ctl_t *ctl,
6396 const cache_t *cache,
6397 const clim_t *clim,
6398 met_t *met0,
6399 met_t *met1,
6400 atm_t *atm) {
6401
6402 if (ctl->met_coord_type != 0)
6403 ERRMSG("Only lat/lon grid supported");
6404
6405 /* Set timer... */
6406 SELECT_TIMER("MODULE_TRACER_CHEM", "PHYSICS");
6407
6408 /* Loop over particles... */
6409 PARTICLE_LOOP(0, atm->np, 1,
6410 "acc data present(ctl,cache,clim,met0,met1,atm)") {
6411
6412 /* Get temperature... */
6413 double t;
6415 INTPOL_3D(t, 1);
6416
6417 /* Get molecular density... */
6418 const double M = MOLEC_DENS(atm->p[ip], t);
6419
6420 /* Get total column ozone... */
6421 double o3c;
6422 INTPOL_2D(o3c, 1);
6423
6424 /* Get solar zenith angle... */
6425 const double sza =
6426 acos(cos_sza(atm->time[ip], atm->lon[ip], atm->lat[ip]));
6427
6428 /* Get O(1D) volume mixing ratio... */
6429 const double o1d =
6430 clim_zm(&clim->o1d, atm->time[ip], atm->lat[ip], atm->p[ip]);
6431
6432 /* Reactions for CFC-10... */
6433 if (ctl->qnt_Cccl4 >= 0) {
6434 const double K_o1d =
6436 const double K_hv = clim_photo(clim->photo.ccl4, &(clim->photo),
6437 atm->p[ip], sza, o3c);
6438 atm->q[ctl->qnt_Cccl4][ip] *= exp(-cache->dt[ip] * (K_hv + K_o1d));
6439 }
6440
6441 /* Reactions for CFC-11... */
6442 if (ctl->qnt_Cccl3f >= 0) {
6443 const double K_o1d =
6445 const double K_hv = clim_photo(clim->photo.ccl3f, &(clim->photo),
6446 atm->p[ip], sza, o3c);
6447 atm->q[ctl->qnt_Cccl3f][ip] *= exp(-cache->dt[ip] * (K_hv + K_o1d));
6448 }
6449
6450 /* Reactions for CFC-12... */
6451 if (ctl->qnt_Cccl2f2 >= 0) {
6452 const double K_o1d =
6454 const double K_hv = clim_photo(clim->photo.ccl2f2, &(clim->photo),
6455 atm->p[ip], sza, o3c);
6456 atm->q[ctl->qnt_Cccl2f2][ip] *= exp(-cache->dt[ip] * (K_hv + K_o1d));
6457 }
6458
6459 /* Reactions for N2O... */
6460 if (ctl->qnt_Cn2o >= 0) {
6461 const double K_o1d =
6463 const double K_hv = clim_photo(clim->photo.n2o, &(clim->photo),
6464 atm->p[ip], sza, o3c);
6465 atm->q[ctl->qnt_Cn2o][ip] *= exp(-cache->dt[ip] * (K_hv + K_o1d));
6466 }
6467 }
6468}
6469
6470/*****************************************************************************/
6471
6473 const ctl_t *ctl,
6474 const cache_t *cache,
6475 met_t *met0,
6476 met_t *met1,
6477 atm_t *atm) {
6478
6479 /* Set timer... */
6480 SELECT_TIMER("MODULE_WET_DEPO", "PHYSICS");
6481
6482 /* Check quantity flags... */
6483 if (ctl->qnt_m < 0 && ctl->qnt_vmr < 0)
6484 ERRMSG("Module needs quantity mass or volume mixing ratio!");
6485
6486 /* Loop over particles... */
6487 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
6488
6489 /* Check whether particle is below cloud top... */
6490 double pct;
6492 INTPOL_2D(pct, 1);
6493 if (!isfinite(pct) || atm->p[ip] <= pct)
6494 continue;
6495
6496 /* Get cloud bottom pressure... */
6497 double pcb;
6498 INTPOL_2D(pcb, 0);
6499
6500 /* Estimate precipitation rate (Pisso et al., 2019)... */
6501 double cl;
6502 INTPOL_2D(cl, 0);
6503 const double Is =
6504 pow(1. / ctl->wet_depo_pre[0] * cl, 1. / ctl->wet_depo_pre[1]);
6505 if (Is < 0.01)
6506 continue;
6507
6508 /* Check whether particle is inside or below cloud... */
6509 double lwc, rwc, iwc, swc;
6510 INTPOL_3D(lwc, 1);
6511 INTPOL_3D(rwc, 0);
6512 INTPOL_3D(iwc, 0);
6513 INTPOL_3D(swc, 0);
6514 const int inside = (lwc > 0 || rwc > 0 || iwc > 0 || swc > 0);
6515
6516 /* Get temperature... */
6517 double t;
6518 INTPOL_3D(t, 0);
6519
6520 /* Calculate in-cloud scavenging coefficient... */
6521 double lambda = 0;
6522 if (inside) {
6523
6524 /* Calculate retention factor... */
6525 double eta;
6526 if (t > WET_DEPO_T_LIQUID)
6527 eta = 1;
6528 else if (t <= WET_DEPO_T_ICE)
6529 eta = ctl->wet_depo_ic_ret_ratio;
6530 else
6531 eta =
6533 ctl->wet_depo_ic_ret_ratio, t);
6534
6535 /* Use exponential dependency for particles (Bakels et al., 2024)... */
6536 if (ctl->wet_depo_ic_a > 0)
6537 lambda = ctl->wet_depo_ic_a * pow(Is, ctl->wet_depo_ic_b) * eta;
6538
6539 /* Use Henry's law for gases... */
6540 else if (ctl->wet_depo_ic_h[0] > 0) {
6541
6542 /* Get Henry's constant (Burkholder et al., 2019; Sander, 2023)... */
6543 double h = ctl->wet_depo_ic_h[0]
6544 * exp(ctl->wet_depo_ic_h[1] * (1. / t - 1. / CHEM_REF_TEMP));
6545
6546 /* Use effective Henry's constant for SO2
6547 (Berglen, 2004; Simpson, 2012)... */
6548 if (ctl->wet_depo_so2_ph > 0) {
6549 const double H_ion = pow(10., -ctl->wet_depo_so2_ph);
6550 const double K_1 = SO2_DISS_K1_REF
6551 * exp(SO2_DISS_K1_TEMP * (1. / t - 1. / CHEM_REF_TEMP));
6552 const double K_2 = SO2_DISS_K2_REF
6553 * exp(SO2_DISS_K2_TEMP * (1. / t - 1. / CHEM_REF_TEMP));
6554 h *= (1. + K_1 / H_ion + K_1 * K_2 / SQR(H_ion));
6555 }
6556
6557 /* Estimate depth of cloud layer... */
6558 const double dz = 1e3 * (Z(pct) - Z(pcb));
6559
6560 /* Calculate scavenging coefficient... */
6561 lambda = h * RI * t * Is / 3.6e6 / dz * eta;
6562 }
6563 }
6564
6565 /* Calculate below-cloud scavenging coefficient... */
6566 else {
6567
6568 /* Calculate retention factor... */
6569 double eta;
6570 if (t > WET_DEPO_T_LIQUID_BC)
6571 eta = 1;
6572 else
6573 eta = ctl->wet_depo_bc_ret_ratio;
6574
6575 /* Use exponential dependency for particles (Bakels et al., 2024)... */
6576 if (ctl->wet_depo_bc_a > 0)
6577 lambda = ctl->wet_depo_bc_a * pow(Is, ctl->wet_depo_bc_b) * eta;
6578
6579 /* Use Henry's law for gases... */
6580 else if (ctl->wet_depo_bc_h[0] > 0) {
6581
6582 /* Get Henry's constant (Burkholder et al., 2019; Sander, 2023)... */
6583 const double h = ctl->wet_depo_bc_h[0]
6584 * exp(ctl->wet_depo_bc_h[1] * (1. / t - 1. / CHEM_REF_TEMP));
6585
6586 /* Estimate depth of cloud layer... */
6587 const double dz = 1e3 * (Z(pct) - Z(pcb));
6588
6589 /* Calculate scavenging coefficient... */
6590 lambda = h * RI * t * Is / 3.6e6 / dz * eta;
6591 }
6592 }
6593
6594 /* Calculate exponential decay of mass... */
6595 const double aux = exp(-cache->dt[ip] * lambda);
6596 if (ctl->qnt_m >= 0) {
6597 if (ctl->qnt_mloss_wet >= 0)
6598 atm->q[ctl->qnt_mloss_wet][ip]
6599 += atm->q[ctl->qnt_m][ip] * (1 - aux);
6600 atm->q[ctl->qnt_m][ip] *= aux;
6601 if (ctl->qnt_loss_rate >= 0)
6602 atm->q[ctl->qnt_loss_rate][ip] += lambda;
6603 }
6604 if (ctl->qnt_vmr >= 0)
6605 atm->q[ctl->qnt_vmr][ip] *= aux;
6606 }
6607}
6608
6609/*****************************************************************************/
6610
6612 ctl_t **ctl,
6613 cache_t **cache,
6614 clim_t **clim,
6615 met_t **met0,
6616 met_t **met1,
6617 atm_t **atm,
6618 depo_t **depo,
6619 dd_t **dd) {
6620
6621 /* Initialize GPU... */
6622#ifdef _OPENACC
6623 if (ctl != NULL || cache != NULL || clim != NULL || met0 != NULL
6624 || met1 != NULL || atm != NULL || depo != NULL || dd != NULL) {
6625 SELECT_TIMER("ACC_INIT", "INIT");
6626 if (acc_get_num_devices(acc_device_nvidia) <= 0)
6627 ERRMSG("Not running on a GPU device!");
6628 acc_device_t device_type = acc_get_device_type();
6629 acc_init(device_type);
6630 }
6631#endif
6632
6633 /* Allocate... */
6634 SELECT_TIMER("ALLOC", "MEMORY");
6635 if (ctl != NULL)
6636 ALLOC(*ctl, ctl_t, 1);
6637 if (cache != NULL)
6638 ALLOC(*cache, cache_t, 1);
6639 if (clim != NULL)
6640 ALLOC(*clim, clim_t, 1);
6641 if (met0 != NULL)
6642 ALLOC(*met0, met_t, 1);
6643 if (met1 != NULL)
6644 ALLOC(*met1, met_t, 1);
6645 if (atm != NULL)
6646 ALLOC(*atm, atm_t, 1);
6647 if (depo != NULL)
6648 ALLOC(*depo, depo_t, 1);
6649 if (dd != NULL) {
6650 ALLOC(*dd, dd_t, 1);
6651
6652 /* Initialize the MPI datatype such that mptrac_free() knows whether
6653 dd_init() has created it... */
6654#ifdef DD
6655 (*dd)->MPI_Particle = MPI_DATATYPE_NULL;
6656#endif
6657 }
6658
6659 /* Create data region on GPU... */
6660#ifdef _OPENACC
6661 SELECT_TIMER("CREATE_DATA_REGION", "MEMORY");
6662 if (ctl != NULL) {
6663 ctl_t *ctlup = *ctl;
6664#pragma acc enter data create(ctlup[:1])
6665 }
6666 if (cache != NULL) {
6667 cache_t *cacheup = *cache;
6668#pragma acc enter data create(cacheup[:1])
6669 }
6670 if (clim != NULL) {
6671 clim_t *climup = *clim;
6672#pragma acc enter data create(climup[:1])
6673 }
6674 if (met0 != NULL) {
6675 met_t *met0up = *met0;
6676#pragma acc enter data create(met0up[:1])
6677 }
6678 if (met1 != NULL) {
6679 met_t *met1up = *met1;
6680#pragma acc enter data create(met1up[:1])
6681 }
6682 if (atm != NULL) {
6683 atm_t *atmup = *atm;
6684#pragma acc enter data create(atmup[:1])
6685 }
6686 if (depo != NULL) {
6687 depo_t *depoup = *depo;
6688#pragma acc enter data create(depoup[:1])
6689 }
6690#ifdef DD
6691 if (dd != NULL) {
6692 dd_t *ddup = *dd;
6693#pragma acc enter data create(ddup[:1])
6694 }
6695#endif
6696#endif
6697}
6698
6699/*****************************************************************************/
6700
6702 ctl_t *ctl,
6703 cache_t *cache,
6704 clim_t *clim,
6705 met_t *met0,
6706 met_t *met1,
6707 atm_t *atm,
6708 depo_t *depo,
6709 dd_t *dd) {
6710
6711#ifdef DD
6712#if LOGLEV >= 2
6713 if (dd_node != NULL) {
6714 int rank;
6715 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
6716 LOG(2, "DD_COMM rank=%d sent=%llu intra=%llu inter=%llu "
6717 "intra_bytes=%llu inter_bytes=%llu particle_bytes=%d",
6718 rank, dd_sent, dd_intra, dd_inter,
6719 dd_intra * (unsigned long long) dd_particle_bytes,
6720 dd_inter * (unsigned long long) dd_particle_bytes,
6721 dd_particle_bytes);
6722 free(dd_node);
6723 dd_node = NULL;
6724 }
6725#endif
6726#endif
6727
6728 /* Delete data region on GPU... */
6729#ifdef _OPENACC
6730 SELECT_TIMER("DELETE_DATA_REGION", "MEMORY");
6731 if (ctl != NULL) {
6732#pragma acc exit data delete(ctl[:1])
6733 }
6734 if (cache != NULL) {
6735#pragma acc exit data delete(cache[:1])
6736 }
6737 if (clim != NULL) {
6738#pragma acc exit data delete(clim[:1])
6739 }
6740 if (met0 != NULL) {
6741#pragma acc exit data delete(met0[:1])
6742 }
6743 if (met1 != NULL) {
6744#pragma acc exit data delete(met1[:1])
6745 }
6746 if (atm != NULL) {
6747#pragma acc exit data delete(atm[:1])
6748 }
6749 if (depo != NULL) {
6750#pragma acc exit data delete(depo[:1])
6751 }
6752#ifdef DD
6753 if (dd != NULL) {
6754#pragma acc exit data delete(dd[:1])
6755 }
6756#endif
6757#endif
6758
6759 /* Free... */
6760 SELECT_TIMER("FREE", "MEMORY");
6761 free(atm);
6762 free(depo);
6763 free(ctl);
6764 free(cache);
6765 free(clim);
6766 free(met0);
6767 free(met1);
6768
6769 /* Free MPI datatype... */
6770#ifdef DD
6771 if (dd != NULL && dd->MPI_Particle != MPI_DATATYPE_NULL)
6772 MPI_Type_free(&dd->MPI_Particle);
6773#endif
6774 free(dd);
6775}
6776
6777/*****************************************************************************/
6778
6780 ctl_t *ctl,
6781 clim_t *clim,
6782 const double t,
6783 met_t **met0,
6784 met_t **met1,
6785 dd_t *dd) {
6786
6787 static int init;
6788
6789 met_t *mets;
6790
6791 char cachefile[LEN], cmd[2 * LEN], filename[LEN];
6792
6793 /* Set timer... */
6794 SELECT_TIMER("GET_MET", "INPUT");
6795
6796 /* Init... */
6797 if (t == ctl->t_start || !init) {
6798 init = 1;
6799
6800 /* Read meteo data... */
6801 get_met_filename(ctl, t + (ctl->direction == -1 ? -1 : 0), -1,
6802 ctl->metbase, ctl->dt_met, filename);
6803 if (!mptrac_read_met(filename, ctl, clim, *met0, dd))
6804 ERRMSG("Cannot open file!");
6805
6806 get_met_filename(ctl, t + (ctl->direction == 1 ? 1 : 0), 1,
6807 ctl->metbase, ctl->dt_met, filename);
6808 if (!mptrac_read_met(filename, ctl, clim, *met1, dd))
6809 ERRMSG("Cannot open file!");
6810
6811 /* Update GPU... */
6812 mptrac_update_device(NULL, NULL, NULL, met0, met1, NULL);
6813 SELECT_TIMER("GET_MET", "INPUT");
6814
6815 /* Caching... */
6816 if (ctl->met_cache && t != ctl->t_stop) {
6817 get_met_filename(ctl, t + 1.1 * ctl->dt_met * ctl->direction,
6818 ctl->direction, ctl->metbase, ctl->dt_met, cachefile);
6819 sprintf(cmd, "cat %s > /dev/null &", cachefile);
6820 LOG(1, "Caching: %s", cachefile);
6821 if (system(cmd) != 0)
6822 WARN("Caching command failed!");
6823 }
6824 }
6825
6826 /* Read new data for forward trajectories... */
6827 if (t > (*met1)->time) {
6828
6829 /* Pointer swap... */
6830 mets = *met1;
6831 *met1 = *met0;
6832 *met0 = mets;
6833
6834 /* Read new meteo data... */
6835 get_met_filename(ctl, t, 1, ctl->metbase, ctl->dt_met, filename);
6836 if (!mptrac_read_met(filename, ctl, clim, *met1, dd))
6837 ERRMSG("Cannot open file!");
6838
6839 /* Update GPU... */
6840 mptrac_update_device(NULL, NULL, NULL, NULL, met1, NULL);
6841 SELECT_TIMER("GET_MET", "INPUT");
6842
6843 /* Caching... */
6844 if (ctl->met_cache && t != ctl->t_stop) {
6845 get_met_filename(ctl, t + ctl->dt_met, 1, ctl->metbase, ctl->dt_met,
6846 cachefile);
6847 sprintf(cmd, "cat %s > /dev/null &", cachefile);
6848 LOG(1, "Caching: %s", cachefile);
6849 if (system(cmd) != 0)
6850 WARN("Caching command failed!");
6851 }
6852 }
6853
6854 /* Read new data for backward trajectories... */
6855 if (t < (*met0)->time) {
6856
6857 /* Pointer swap... */
6858 mets = *met1;
6859 *met1 = *met0;
6860 *met0 = mets;
6861
6862 /* Read new meteo data... */
6863 get_met_filename(ctl, t, -1, ctl->metbase, ctl->dt_met, filename);
6864 if (!mptrac_read_met(filename, ctl, clim, *met0, dd))
6865 ERRMSG("Cannot open file!");
6866
6867 /* Update GPU... */
6868 mptrac_update_device(NULL, NULL, NULL, met0, NULL, NULL);
6869 SELECT_TIMER("GET_MET", "INPUT");
6870
6871 /* Caching... */
6872 if (ctl->met_cache && t != ctl->t_stop) {
6873 get_met_filename(ctl, t - ctl->dt_met, -1, ctl->metbase, ctl->dt_met,
6874 cachefile);
6875 sprintf(cmd, "cat %s > /dev/null &", cachefile);
6876 LOG(1, "Caching: %s", cachefile);
6877 if (system(cmd) != 0)
6878 WARN("Caching command failed!");
6879 }
6880 }
6881
6882 if ((*met0)->coord_type != (*met1)->coord_type)
6883 ERRMSG("Coordinate types do not match!");
6884
6885 /* Check that grids are consistent... */
6886 if ((*met0)->nx != 0 && (*met1)->nx != 0) {
6887 if ((*met0)->nx != (*met1)->nx
6888 || (*met0)->ny != (*met1)->ny || (*met0)->np != (*met1)->np)
6889 ERRMSG("Meteo grid dimensions do not match!");
6890 for (int ix = 0; ix < (*met0)->nx; ix++)
6891 if (fabs((*met0)->lon[ix] - (*met1)->lon[ix]) > 0.001)
6892 ERRMSG("Meteo grid longitudes do not match!");
6893 for (int iy = 0; iy < (*met0)->ny; iy++)
6894 if (fabs((*met0)->lat[iy] - (*met1)->lat[iy]) > 0.001)
6895 ERRMSG("Meteo grid latitudes do not match!");
6896 for (int ip = 0; ip < (*met0)->np; ip++)
6897 if (fabs((*met0)->p[ip] - (*met1)->p[ip]) > 0.001)
6898 ERRMSG("Meteo grid pressure levels do not match!");
6899 }
6900}
6901
6902/*****************************************************************************/
6903
6905 ctl_t *ctl,
6906 cache_t *cache,
6907 clim_t *clim,
6908 atm_t *atm,
6909 depo_t *depo,
6910 const int ntask) {
6911
6912 /* Initialize timesteps... */
6913 module_timesteps_init(ctl, atm);
6914
6915 /* Initialize random number generator... */
6916 module_rng_init(ntask);
6917
6918 /* Update GPU memory... */
6919 mptrac_update_device(ctl, cache, clim, NULL, NULL, atm);
6920#ifdef _OPENACC
6921#pragma acc update device(depo[:1])
6922#else
6923 (void) depo;
6924#endif
6925}
6926
6927/*****************************************************************************/
6928
6930 const char *filename,
6931 const ctl_t *ctl,
6932 atm_t *atm) {
6933
6934 int result;
6935
6936 /* Set timer... */
6937 SELECT_TIMER("READ_ATM", "INPUT");
6938
6939 /* Init... */
6940 atm->np = 0;
6941
6942 /* Write info... */
6943 LOG(1, "Read atmospheric data: %s", filename);
6944
6945 /* Read ASCII data... */
6946 if (ctl->atm_type == 0)
6947 result = read_atm_asc(filename, ctl, atm);
6948
6949 /* Read binary data... */
6950 else if (ctl->atm_type == 1)
6951 result = read_atm_bin(filename, ctl, atm);
6952
6953 /* Read netCDF data... */
6954 else if (ctl->atm_type == 2)
6955 result = read_atm_nc(filename, ctl, atm);
6956
6957 /* Read CLaMS data... */
6958 else if (ctl->atm_type == 3 || ctl->atm_type == 4)
6959 result = read_atm_clams(filename, ctl, atm);
6960
6961 /* Error... */
6962 else
6963 ERRMSG("Atmospheric data type not supported!");
6964
6965 /* Check result... */
6966 if (result != 1)
6967 return 0;
6968
6969 /* Check number of air parcels... */
6970 if (atm->np < 1)
6971 ERRMSG("Can not read any data!");
6972
6973 /* Write info... */
6974 double mini, maxi;
6975 LOG(2, "Number of particles: %d", atm->np);
6976 gsl_stats_minmax(&mini, &maxi, atm->time, 1, (size_t) atm->np);
6977 LOG(2, "Time range: %.2f ... %.2f s", mini, maxi);
6978 gsl_stats_minmax(&mini, &maxi, atm->p, 1, (size_t) atm->np);
6979 LOG(2, "Altitude range: %g ... %g km", Z(maxi), Z(mini));
6980 LOG(2, "Pressure range: %g ... %g hPa", maxi, mini);
6981 gsl_stats_minmax(&mini, &maxi, atm->lon, 1, (size_t) atm->np);
6982 LOG(2, "%s range: %g ... %g %s",
6983 ctl->met_coord_type == 0 ? "Longitude" : "X coordinate", mini, maxi,
6984 ctl->met_coord_type == 0 ? "deg" : "m");
6985 gsl_stats_minmax(&mini, &maxi, atm->lat, 1, (size_t) atm->np);
6986 LOG(2, "%s range: %g ... %g %s",
6987 ctl->met_coord_type == 0 ? "Latitude" : "Y coordinate", mini, maxi,
6988 ctl->met_coord_type == 0 ? "deg" : "m");
6989 for (int iq = 0; iq < ctl->nq; iq++) {
6990 char msg[5 * LEN];
6991 sprintf(msg, "Quantity %s range: %s ... %s %s",
6992 ctl->qnt_name[iq], ctl->qnt_format[iq],
6993 ctl->qnt_format[iq], ctl->qnt_unit[iq]);
6994 gsl_stats_minmax(&mini, &maxi, atm->q[iq], 1, (size_t) atm->np);
6995 LOG(2, msg, mini, maxi);
6996 }
6997
6998 /* Return success... */
6999 return 1;
7000}
7001
7002/*****************************************************************************/
7003
7005 const ctl_t *ctl,
7006 clim_t *clim) {
7007
7008 /* Set timer... */
7009 SELECT_TIMER("READ_CLIM", "INPUT");
7010
7011 /* Init tropopause climatology... */
7012 clim_tropo_init(clim);
7013
7014 /* Read photolysis rates... */
7015 if (ctl->clim_photo[0] != '-')
7016 read_clim_photo(ctl->clim_photo, &clim->photo);
7017
7018 /* Read HNO3 climatology... */
7019 if (ctl->clim_hno3_filename[0] != '-')
7020 read_clim_zm(ctl->clim_hno3_filename, "HNO3", &clim->hno3);
7021
7022 /* Read OH climatology... */
7023 if (ctl->clim_oh_filename[0] != '-') {
7024 read_clim_zm(ctl->clim_oh_filename, "OH", &clim->oh);
7025 if (ctl->oh_chem_beta > 0)
7026 clim_oh_diurnal_correction(ctl, clim);
7027 }
7028
7029 /* Read H2O2 climatology... */
7030 if (ctl->clim_h2o2_filename[0] != '-')
7031 read_clim_zm(ctl->clim_h2o2_filename, "H2O2", &clim->h2o2);
7032
7033 /* Read HO2 climatology... */
7034 if (ctl->clim_ho2_filename[0] != '-')
7035 read_clim_zm(ctl->clim_ho2_filename, "HO2", &clim->ho2);
7036
7037 /* Read O(1D) climatology... */
7038 if (ctl->clim_o1d_filename[0] != '-')
7039 read_clim_zm(ctl->clim_o1d_filename, "O1D", &clim->o1d);
7040
7041 /* Read CFC-10 time series... */
7042 if (ctl->clim_ccl4_timeseries[0] != '-')
7044
7045 /* Read CFC-11 time series... */
7046 if (ctl->clim_ccl3f_timeseries[0] != '-')
7048
7049 /* Read CFC-12 time series... */
7050 if (ctl->clim_ccl2f2_timeseries[0] != '-')
7052
7053 /* Read N2O time series... */
7054 if (ctl->clim_n2o_timeseries[0] != '-')
7055 read_clim_ts(ctl->clim_n2o_timeseries, &clim->n2o);
7056
7057 /* Read SF6 time series... */
7058 if (ctl->clim_sf6_timeseries[0] != '-')
7059 read_clim_ts(ctl->clim_sf6_timeseries, &clim->sf6);
7060}
7061
7062/*****************************************************************************/
7063
7065 const char *filename,
7066 int argc,
7067 char *argv[],
7068 ctl_t *ctl) {
7069
7070 /* Set timer... */
7071 SELECT_TIMER("READ_CTL", "INPUT");
7072
7073 /* Write info... */
7074 LOG(1, "\nMassive-Parallel Trajectory Calculations (MPTRAC)\n"
7075 "(executable: %s | version: %s | compiled: %s, %s)\n",
7076 argv[0], VERSION, __DATE__, __TIME__);
7077
7078 /* Initialize quantity indices... */
7079 ctl->qnt_idx = -1;
7080 ctl->qnt_ens = -1;
7081 ctl->qnt_stat = -1;
7082 ctl->qnt_m = -1;
7083 ctl->qnt_vmr = -1;
7084 ctl->qnt_rp = -1;
7085 ctl->qnt_rhop = -1;
7086 ctl->qnt_ps = -1;
7087 ctl->qnt_ts = -1;
7088 ctl->qnt_zs = -1;
7089 ctl->qnt_us = -1;
7090 ctl->qnt_vs = -1;
7091 ctl->qnt_ess = -1;
7092 ctl->qnt_nss = -1;
7093 ctl->qnt_shf = -1;
7094 ctl->qnt_lsm = -1;
7095 ctl->qnt_sst = -1;
7096 ctl->qnt_pbl = -1;
7097 ctl->qnt_pt = -1;
7098 ctl->qnt_tt = -1;
7099 ctl->qnt_zt = -1;
7100 ctl->qnt_h2ot = -1;
7101 ctl->qnt_zg = -1;
7102 ctl->qnt_p = -1;
7103 ctl->qnt_t = -1;
7104 ctl->qnt_rho = -1;
7105 ctl->qnt_u = -1;
7106 ctl->qnt_v = -1;
7107 ctl->qnt_w = -1;
7108 ctl->qnt_h2o = -1;
7109 ctl->qnt_o3 = -1;
7110 ctl->qnt_lwc = -1;
7111 ctl->qnt_rwc = -1;
7112 ctl->qnt_iwc = -1;
7113 ctl->qnt_swc = -1;
7114 ctl->qnt_cc = -1;
7115 ctl->qnt_pct = -1;
7116 ctl->qnt_pcb = -1;
7117 ctl->qnt_cl = -1;
7118 ctl->qnt_plcl = -1;
7119 ctl->qnt_plfc = -1;
7120 ctl->qnt_pel = -1;
7121 ctl->qnt_cape = -1;
7122 ctl->qnt_cin = -1;
7123 ctl->qnt_o3c = -1;
7124 ctl->qnt_hno3 = -1;
7125 ctl->qnt_oh = -1;
7126 ctl->qnt_h2o2 = -1;
7127 ctl->qnt_ho2 = -1;
7128 ctl->qnt_o1d = -1;
7129 ctl->qnt_mloss_oh = -1;
7130 ctl->qnt_mloss_h2o2 = -1;
7131 ctl->qnt_mloss_kpp = -1;
7132 ctl->qnt_mloss_wet = -1;
7133 ctl->qnt_mloss_dry = -1;
7134 ctl->qnt_mloss_decay = -1;
7135 ctl->qnt_loss_rate = -1;
7136 ctl->qnt_psat = -1;
7137 ctl->qnt_psice = -1;
7138 ctl->qnt_pw = -1;
7139 ctl->qnt_sh = -1;
7140 ctl->qnt_rh = -1;
7141 ctl->qnt_rhice = -1;
7142 ctl->qnt_theta = -1;
7143 ctl->qnt_iso_var = -1;
7144 ctl->qnt_zeta = -1;
7145 ctl->qnt_zeta_d = -1;
7146 ctl->qnt_zeta_dot = -1;
7147 ctl->qnt_eta = -1;
7148 ctl->qnt_eta_d = -1;
7149 ctl->qnt_eta_dot = -1;
7150 ctl->qnt_tvirt = -1;
7151 ctl->qnt_lapse = -1;
7152 ctl->qnt_vh = -1;
7153 ctl->qnt_vz = -1;
7154 ctl->qnt_pv = -1;
7155 ctl->qnt_tdew = -1;
7156 ctl->qnt_tice = -1;
7157 ctl->qnt_tsts = -1;
7158 ctl->qnt_tnat = -1;
7159 ctl->qnt_Cx = -1;
7160 ctl->qnt_Ch2o = -1;
7161 ctl->qnt_Co3 = -1;
7162 ctl->qnt_Cco = -1;
7163 ctl->qnt_Coh = -1;
7164 ctl->qnt_Ch = -1;
7165 ctl->qnt_Cho2 = -1;
7166 ctl->qnt_Ch2o2 = -1;
7167 ctl->qnt_Co1d = -1;
7168 ctl->qnt_Co3p = -1;
7169 ctl->qnt_Cccl4 = -1;
7170 ctl->qnt_Cccl3f = -1;
7171 ctl->qnt_Cccl2f2 = -1;
7172 ctl->qnt_Cn2o = -1;
7173 ctl->qnt_Csf6 = -1;
7174 ctl->qnt_aoa = -1;
7175 ctl->qnt_Arn222 = -1;
7176 ctl->qnt_Apb210 = -1;
7177 ctl->qnt_Abe7 = -1;
7178 ctl->qnt_Acs137 = -1;
7179 ctl->qnt_Ai131 = -1;
7180 ctl->qnt_Axe133 = -1;
7181 ctl->qnt_current_subdomain = -1;
7182 ctl->qnt_target_subdomain = -1;
7183
7184 /* Read quantities... */
7185 ctl->nq = (int) scan_ctl(filename, argc, argv, "NQ", -1, "0", NULL);
7186 if (ctl->nq > NQ)
7187 ERRMSG("Too many quantities!");
7188 for (int iq = 0; iq < ctl->nq; iq++) {
7189
7190 /* Read quantity name and format... */
7191 scan_ctl(filename, argc, argv, "QNT_NAME", iq, "", ctl->qnt_name[iq]);
7192 scan_ctl(filename, argc, argv, "QNT_LONGNAME", iq, ctl->qnt_name[iq],
7193 ctl->qnt_longname[iq]);
7194 scan_ctl(filename, argc, argv, "QNT_FORMAT", iq, "%g",
7195 ctl->qnt_format[iq]);
7196 if (strcasecmp(ctl->qnt_name[iq], "aoa") == 0)
7197 sprintf(ctl->qnt_format[iq], "%%.2f");
7198
7199 /* Try to identify quantity... */
7200 SET_QNT(qnt_idx, "idx", "particle index", "-")
7201 SET_QNT(qnt_ens, "ens", "ensemble index", "-")
7202 SET_QNT(qnt_stat, "stat", "station flag", "-")
7203 SET_QNT(qnt_m, "m", "mass", "kg")
7204 SET_QNT(qnt_vmr, "vmr", "volume mixing ratio", "ppv")
7205 SET_QNT(qnt_rp, "rp", "particle radius", "microns")
7206 SET_QNT(qnt_rhop, "rhop", "particle density", "kg/m^3")
7207 SET_QNT(qnt_ps, "ps", "surface pressure", "hPa")
7208 SET_QNT(qnt_ts, "ts", "surface temperature", "K")
7209 SET_QNT(qnt_zs, "zs", "surface height", "km")
7210 SET_QNT(qnt_us, "us", "surface zonal wind", "m/s")
7211 SET_QNT(qnt_vs, "vs", "surface meridional wind", "m/s")
7212 SET_QNT(qnt_ess, "ess", "eastward turbulent surface stress", "N/m^2")
7213 SET_QNT(qnt_nss, "nss", "northward turbulent surface stress", "N/m^2")
7214 SET_QNT(qnt_shf, "shf", "surface sensible heat flux", "W/m^2")
7215 SET_QNT(qnt_lsm, "lsm", "land-sea mask", "1")
7216 SET_QNT(qnt_sst, "sst", "sea surface temperature", "K")
7217 SET_QNT(qnt_pbl, "pbl", "planetary boundary layer", "hPa")
7218 SET_QNT(qnt_pt, "pt", "tropopause pressure", "hPa")
7219 SET_QNT(qnt_tt, "tt", "tropopause temperature", "K")
7220 SET_QNT(qnt_zt, "zt", "tropopause geopotential height", "km")
7221 SET_QNT(qnt_h2ot, "h2ot", "tropopause water vapor", "ppv")
7222 SET_QNT(qnt_zg, "zg", "geopotential height", "km")
7223 SET_QNT(qnt_p, "p", "pressure", "hPa")
7224 SET_QNT(qnt_t, "t", "temperature", "K")
7225 SET_QNT(qnt_rho, "rho", "air density", "kg/m^3")
7226 SET_QNT(qnt_u, "u", "zonal wind", "m/s")
7227 SET_QNT(qnt_v, "v", "meridional wind", "m/s")
7228 SET_QNT(qnt_w, "w", "vertical velocity", "hPa/s")
7229 SET_QNT(qnt_h2o, "h2o", "water vapor", "ppv")
7230 SET_QNT(qnt_o3, "o3", "ozone", "ppv")
7231 SET_QNT(qnt_lwc, "lwc", "cloud liquid water content", "kg/kg")
7232 SET_QNT(qnt_rwc, "rwc", "cloud rain water content", "kg/kg")
7233 SET_QNT(qnt_iwc, "iwc", "cloud ice water content", "kg/kg")
7234 SET_QNT(qnt_swc, "swc", "cloud snow water content", "kg/kg")
7235 SET_QNT(qnt_cc, "cc", "cloud cover", "1")
7236 SET_QNT(qnt_pct, "pct", "cloud top pressure", "hPa")
7237 SET_QNT(qnt_pcb, "pcb", "cloud bottom pressure", "hPa")
7238 SET_QNT(qnt_cl, "cl", "total column cloud water", "kg/m^2")
7239 SET_QNT(qnt_plcl, "plcl", "lifted condensation level", "hPa")
7240 SET_QNT(qnt_plfc, "plfc", "level of free convection", "hPa")
7241 SET_QNT(qnt_pel, "pel", "equilibrium level", "hPa")
7242 SET_QNT(qnt_cape, "cape", "convective available potential energy",
7243 "J/kg")
7244 SET_QNT(qnt_cin, "cin", "convective inhibition", "J/kg")
7245 SET_QNT(qnt_o3c, "o3c", "total column ozone", "DU")
7246 SET_QNT(qnt_hno3, "hno3", "nitric acid", "ppv")
7247 SET_QNT(qnt_oh, "oh", "hydroxyl radical", "ppv")
7248 SET_QNT(qnt_h2o2, "h2o2", "hydrogen peroxide", "ppv")
7249 SET_QNT(qnt_ho2, "ho2", "hydroperoxyl radical", "ppv")
7250 SET_QNT(qnt_o1d, "o1d", "atomic oxygen", "ppv")
7251 SET_QNT(qnt_mloss_oh, "mloss_oh", "mass loss due to OH chemistry", "kg")
7252 SET_QNT(qnt_mloss_h2o2, "mloss_h2o2",
7253 "mass loss due to H2O2 chemistry", "kg")
7254 SET_QNT(qnt_mloss_kpp, "mloss_kpp", "mass loss due to kpp chemistry",
7255 "kg")
7256 SET_QNT(qnt_mloss_wet, "mloss_wet", "mass loss due to wet deposition",
7257 "kg")
7258 SET_QNT(qnt_mloss_dry, "mloss_dry", "mass loss due to dry deposition",
7259 "kg")
7260 SET_QNT(qnt_mloss_decay, "mloss_decay",
7261 "mass loss due to exponential decay", "kg")
7262 SET_QNT(qnt_loss_rate, "loss_rate", "total loss rate", "s^-1")
7263 SET_QNT(qnt_psat, "psat", "saturation pressure over water", "hPa")
7264 SET_QNT(qnt_psice, "psice", "saturation pressure over ice", "hPa")
7265 SET_QNT(qnt_pw, "pw", "partial water vapor pressure", "hPa")
7266 SET_QNT(qnt_sh, "sh", "specific humidity", "kg/kg")
7267 SET_QNT(qnt_rh, "rh", "relative humidity", "%%")
7268 SET_QNT(qnt_rhice, "rhice", "relative humidity over ice", "%%")
7269 SET_QNT(qnt_theta, "theta", "potential temperature", "K")
7270 SET_QNT(qnt_iso_var, "iso_var", "isosurface reference value", "varies")
7271 SET_QNT(qnt_zeta, "zeta", "zeta coordinate", "K")
7272 SET_QNT(qnt_zeta_d, "zeta_d", "diagnosed zeta coordinate", "K")
7273 SET_QNT(qnt_zeta_dot, "zeta_dot", "velocity of zeta coordinate",
7274 "K/day")
7275 SET_QNT(qnt_eta, "eta", "eta coordinate", "1")
7276 SET_QNT(qnt_eta_d, "eta_d", "diagnosed eta coordinate", "1")
7277 SET_QNT(qnt_eta_dot, "eta_dot", "velocity of eta coordinate", "1/s")
7278 SET_QNT(qnt_tvirt, "tvirt", "virtual temperature", "K")
7279 SET_QNT(qnt_lapse, "lapse", "temperature lapse rate", "K/km")
7280 SET_QNT(qnt_vh, "vh", "horizontal velocity", "m/s")
7281 SET_QNT(qnt_vz, "vz", "vertical velocity", "m/s")
7282 SET_QNT(qnt_pv, "pv", "potential vorticity", "PVU")
7283 SET_QNT(qnt_tdew, "tdew", "dew point temperature", "K")
7284 SET_QNT(qnt_tice, "tice", "frost point temperature", "K")
7285 SET_QNT(qnt_tsts, "tsts", "STS existence temperature", "K")
7286 SET_QNT(qnt_tnat, "tnat", "NAT existence temperature", "K")
7287 SET_QNT(qnt_Cx, "Cx", "Trace species x volume mixing ratio", "ppv")
7288 SET_QNT(qnt_Ch2o, "Ch2o", "H2O volume mixing ratio", "ppv")
7289 SET_QNT(qnt_Co3, "Co3", "O3 volume mixing ratio", "ppv")
7290 SET_QNT(qnt_Cco, "Cco", "CO volume mixing ratio", "ppv")
7291 SET_QNT(qnt_Coh, "Coh", "HO volume mixing ratio", "ppv")
7292 SET_QNT(qnt_Ch, "Ch", "H radical volume mixing ratio", "ppv")
7293 SET_QNT(qnt_Cho2, "Cho2", "HO2 volume mixing ratio", "ppv")
7294 SET_QNT(qnt_Ch2o2, "Ch2o2", "H2O2 volume mixing ratio", "ppv")
7295 SET_QNT(qnt_Co1d, "Co1d", "O(1D) volume mixing ratio", "ppv")
7296 SET_QNT(qnt_Co3p, "Co3p", "O(3P) radical volume mixing ratio", "ppv")
7297 SET_QNT(qnt_Cccl4, "Cccl4", "CCl4 (CFC-10) volume mixing ratio", "ppv")
7298 SET_QNT(qnt_Cccl3f, "Cccl3f", "CCl3F (CFC-11) volume mixing ratio",
7299 "ppv")
7300 SET_QNT(qnt_Cccl2f2, "Cccl2f2", "CCl2F2 (CFC-12) volume mixing ratio",
7301 "ppv")
7302 SET_QNT(qnt_Cn2o, "Cn2o", "N2O volume mixing ratio", "ppv")
7303 SET_QNT(qnt_Csf6, "Csf6", "SF6 volume mixing ratio", "ppv")
7304 SET_QNT(qnt_aoa, "aoa", "age of air", "s")
7305 SET_QNT(qnt_Arn222, "Arn222", "Rn-222 activity", "Bq")
7306 SET_QNT(qnt_Apb210, "Apb210", "Pb-210 activity", "Bq")
7307 SET_QNT(qnt_Abe7, "Abe7", "Be-7 activity", "Bq")
7308 SET_QNT(qnt_Acs137, "Acs137", "Cs-137 activity", "Bq")
7309 SET_QNT(qnt_Ai131, "Ai131", "I-131 activity", "Bq")
7310 SET_QNT(qnt_Axe133, "Axe133", "Xe-133 activity", "Bq")
7311 SET_QNT(qnt_current_subdomain, "current_subdomain",
7312 "current subdomain rank", "-")
7313 SET_QNT(qnt_target_subdomain, "target_subdomain",
7314 "target subdomain rank", "-")
7315 scan_ctl(filename, argc, argv, "QNT_UNIT", iq, "", ctl->qnt_unit[iq]);
7316 }
7317
7318 ctl->met_coord_type =
7319 (int) scan_ctl(filename, argc, argv, "MET_COORD_TYPE", -1, "0", NULL);
7320 if (ctl->met_coord_type < 0 || ctl->met_coord_type > 1)
7321 ERRMSG("MET_COORD_TYPE must be 0 or 1!");
7322 ctl->met_utm_ref_lat = 0.0;
7323 ctl->met_utm_ref_lon = 0.0;
7324 if (ctl->met_coord_type == 1) {
7325 ctl->met_utm_ref_lat =
7326 scan_ctl(filename, argc, argv, "MET_UTM_REF_LAT", -1, "", NULL);
7327 ctl->met_utm_ref_lon =
7328 scan_ctl(filename, argc, argv, "MET_UTM_REF_LON", -1, "", NULL);
7329 }
7330
7331 /* Vertical coordinate and velocity... */
7332 ctl->advect_vert_coord =
7333 (int) scan_ctl(filename, argc, argv, "ADVECT_VERT_COORD", -1, "0", NULL);
7334 if (ctl->advect_vert_coord < 0 || ctl->advect_vert_coord > 3)
7335 ERRMSG("ADVECT_VERT_COORD must be 0, 1, 2, or 3!");
7336
7337 if (ctl->advect_vert_coord == 1 && ctl->qnt_zeta < 0)
7338 ERRMSG("Add quantity zeta for diabatic advection!");
7339 if (ctl->advect_vert_coord == 3 && ctl->qnt_eta < 0)
7340 ERRMSG("Add quantity eta for etadot avection!");
7341
7342 ctl->met_vert_coord =
7343 (int) scan_ctl(filename, argc, argv, "MET_VERT_COORD", -1, "0", NULL);
7344 if (ctl->met_vert_coord < 0 || ctl->met_vert_coord > 4)
7345 ERRMSG("MET_VERT_COORD must be 0, 1, 2, 3, or 4!");
7346 if (ctl->qnt_eta_d >= 0
7347 && ctl->met_vert_coord != 2 && ctl->met_vert_coord != 3)
7348 ERRMSG("Quantity eta_d requires full-level A and B coefficients!");
7349
7350 if (ctl->advect_vert_coord == 2 && ctl->met_vert_coord == 0)
7351 ERRMSG
7352 ("Using ADVECT_VERT_COORD = 2 requires meteo data on model levels!");
7353 if (ctl->advect_vert_coord == 3 && ctl->met_vert_coord != 3)
7354 ERRMSG
7355 ("Using ADVECT_VERT_COORD = 3 requires A and B model level coefficients!");
7356
7357 ctl->met_gp2z =
7358 (int) scan_ctl(filename, argc, argv, "MET_GP2Z", -1, "0", NULL);
7359 if (ctl->met_gp2z != 0 && ctl->met_gp2z != 1)
7360 ERRMSG("Set MET_GP2Z to 0 or 1!");
7361
7362 /* Time steps of simulation... */
7363 ctl->direction =
7364 (int) scan_ctl(filename, argc, argv, "DIRECTION", -1, "1", NULL);
7365 if (ctl->direction != -1 && ctl->direction != 1)
7366 ERRMSG("Set DIRECTION to -1 or 1!");
7367 ctl->t_stop = scan_ctl(filename, argc, argv, "T_STOP", -1, "1e100", NULL);
7368 ctl->dt_mod = scan_ctl(filename, argc, argv, "DT_MOD", -1, "180", NULL);
7369
7370 /* Meteo data... */
7371 scan_ctl(filename, argc, argv, "METBASE", -1, "-", ctl->metbase);
7372 ctl->dt_met = scan_ctl(filename, argc, argv, "DT_MET", -1, "3600", NULL);
7373 if (ctl->dt_mod > ctl->dt_met)
7374 ERRMSG("DT_MOD must not exceed DT_MET!");
7375 ctl->met_convention =
7376 (int) scan_ctl(filename, argc, argv, "MET_CONVENTION", -1, "0", NULL);
7377 ctl->met_type =
7378 (int) scan_ctl(filename, argc, argv, "MET_TYPE", -1, "0", NULL);
7379 if (ctl->advect_vert_coord == 1 && ctl->met_type != 0)
7380 ERRMSG
7381 ("Please use meteo files in netcdf format for diabatic calculations.");
7382 if (ctl->advect_vert_coord == 3 && ctl->met_type != 0)
7383 ERRMSG
7384 ("Please use meteo files in netcdf format for etadot calculations.");
7385 ctl->met_clams =
7386 (int) scan_ctl(filename, argc, argv, "MET_CLAMS", -1, "0", NULL);
7387 ctl->met_nc_scale =
7388 (int) scan_ctl(filename, argc, argv, "MET_NC_SCALE", -1, "1", NULL);
7389 ctl->met_nc_read_mode =
7390 (int) scan_ctl(filename, argc, argv, "MET_NC_READ_MODE", -1, "0", NULL);
7391 if (ctl->met_nc_read_mode < 0 || ctl->met_nc_read_mode > 1)
7392 ERRMSG("Set MET_NC_READ_MODE to 0 or 1!");
7393 ctl->met_nc_level =
7394 (int) scan_ctl(filename, argc, argv, "MET_NC_LEVEL", -1, "0", NULL);
7395 ctl->met_nc_quant =
7396 (int) scan_ctl(filename, argc, argv, "MET_NC_QUANT", -1, "0", NULL);
7397 ctl->met_zstd_level =
7398 (int) scan_ctl(filename, argc, argv, "MET_ZSTD_LEVEL", -1, "-3", NULL);
7399 ctl->met_zstd_nworkers =
7400 (int) scan_ctl(filename, argc, argv, "MET_ZSTD_NWORKERS", -1, "4", NULL);
7401 ctl->met_lz4_accel =
7402 (int) scan_ctl(filename, argc, argv, "MET_LZ4_ACCEL", -1, "8", NULL);
7403 ctl->met_pck_zstd =
7404 (int) scan_ctl(filename, argc, argv, "MET_PCK_ZSTD", -1, "0", NULL);
7405 if (ctl->met_pck_zstd != 0 && ctl->met_pck_zstd != 1)
7406 ERRMSG("Set MET_PCK_ZSTD to 0 or 1!");
7407#ifndef ZSTD
7408 if (ctl->met_type == 2 && ctl->met_pck_zstd)
7409 ERRMSG("MET_PCK_ZSTD requires MPTRAC to be compiled with ZSTD support!");
7410#endif
7411 const int def_lossy_scale =
7412 (int) scan_ctl(filename, argc, argv, "MET_LOSSY_SCALE", -1, "0", NULL);
7413 for (int i = 0; i < METVAR; i++) {
7414 char defprec_zfp[LEN] = "7", deftol_zfp[LEN] = "0.0";
7415 char defprec_sz3[LEN] = "6", deftol_sz3[LEN] = "0.0";
7416 if (i == 0) { /* geopotential height */
7417 sprintf(defprec_zfp, "12");
7418 sprintf(defprec_sz3, "11");
7419 } else if (i == 1) { /* temperature */
7420 sprintf(defprec_zfp, "11");
7421 sprintf(defprec_sz3, "7");
7422 } else if (i == 2 || i == 3) { /* horizontal wind */
7423 sprintf(defprec_zfp, "7");
7424 sprintf(defprec_sz3, "7");
7425 } else if (i == 4) { /* vertical wind */
7426 sprintf(defprec_zfp, "6");
7427 sprintf(defprec_sz3, "13");
7428 } else if (i == 5) { /* potential vorticity */
7429 sprintf(defprec_zfp, "7");
7430 sprintf(defprec_sz3, "20");
7431 } else if (i == 6) { /* water vapor */
7432 sprintf(defprec_zfp, "10");
7433 sprintf(defprec_sz3, "18");
7434 } else if (i == 7) { /* ozone */
7435 sprintf(defprec_zfp, "9");
7436 sprintf(defprec_sz3, "10");
7437 } else if (i >= 8 && i <= 11) { /* cloud water fields */
7438 sprintf(defprec_zfp, "6");
7439 sprintf(defprec_sz3, "13");
7440 } else if (i == 12) { /* cloud cover */
7441 sprintf(defprec_zfp, "9");
7442 sprintf(defprec_sz3, "6");
7443 }
7444 ctl->met_zfp_prec[i] =
7445 (int) scan_ctl(filename, argc, argv, "MET_ZFP_PREC", i, defprec_zfp,
7446 NULL);
7447 ctl->met_zfp_tol[i] =
7448 scan_ctl(filename, argc, argv, "MET_ZFP_TOL", i, deftol_zfp, NULL);
7449 ctl->met_sz3_prec[i] =
7450 (int) scan_ctl(filename, argc, argv, "MET_SZ3_PREC", i, defprec_sz3,
7451 NULL);
7452 ctl->met_sz3_tol[i] =
7453 scan_ctl(filename, argc, argv, "MET_SZ3_TOL", i, deftol_sz3, NULL);
7454 char defscale[LEN];
7455 snprintf(defscale, LEN, "%d", def_lossy_scale);
7456 ctl->met_lossy_scale[i] =
7457 (int) scan_ctl(filename, argc, argv, "MET_LOSSY_SCALE", i, defscale,
7458 NULL);
7459 if (ctl->met_lossy_scale[i] < 0 || ctl->met_lossy_scale[i] > 1)
7460 ERRMSG("Set MET_LOSSY_SCALE to 0 or 1!");
7461 }
7462
7463 /* Scan compression diagnostics file... */
7464 scan_ctl(filename, argc, argv, "MET_COMP_LOGFILE", -1, "-",
7465 ctl->met_comp_logfile);
7466 ctl->met_cms_batch =
7467 (int) scan_ctl(filename, argc, argv, "MET_CMS_BATCH", -1, "-1", NULL);
7468 ctl->met_cms_zstd =
7469 (int) scan_ctl(filename, argc, argv, "MET_CMS_ZSTD", -1, "1", NULL);
7470 ctl->met_cms_nd0x =
7471 (int) scan_ctl(filename, argc, argv, "MET_CMS_ND0X", -1, "48", NULL);
7472 ctl->met_cms_nd0y =
7473 (int) scan_ctl(filename, argc, argv, "MET_CMS_ND0Y", -1, "24", NULL);
7474 ctl->met_cms_maxlev =
7475 (int) scan_ctl(filename, argc, argv, "MET_CMS_MAXLEV", -1, "6", NULL);
7476 for (int i = 0; i < METVAR; i++) {
7477 char defeps[LEN] = "1.0";
7478 if (i == 1 || i == 2 || i == 3)
7479 sprintf(defeps, "0.05");
7480 ctl->met_cms_eps[i] =
7481 scan_ctl(filename, argc, argv, "MET_CMS_EPS", i, defeps, NULL);
7482 }
7483 ctl->met_dx = (int) scan_ctl(filename, argc, argv, "MET_DX", -1, "1", NULL);
7484 ctl->met_dy = (int) scan_ctl(filename, argc, argv, "MET_DY", -1, "1", NULL);
7485 ctl->met_dp = (int) scan_ctl(filename, argc, argv, "MET_DP", -1, "1", NULL);
7486 if (ctl->met_dx < 1 || ctl->met_dy < 1 || ctl->met_dp < 1)
7487 ERRMSG("MET_DX, MET_DY, and MET_DP need to be greater than zero!");
7488 ctl->met_sx = (int) scan_ctl(filename, argc, argv, "MET_SX", -1, "1", NULL);
7489 ctl->met_sy = (int) scan_ctl(filename, argc, argv, "MET_SY", -1, "1", NULL);
7490 ctl->met_sp = (int) scan_ctl(filename, argc, argv, "MET_SP", -1, "1", NULL);
7491 if (ctl->met_sx < 1 || ctl->met_sy < 1 || ctl->met_sp < 1)
7492 ERRMSG("MET_SX, MET_SY, and MET_SP need to be greater than zero!");
7493 ctl->met_detrend =
7494 scan_ctl(filename, argc, argv, "MET_DETREND", -1, "-999", NULL);
7495 ctl->met_np = (int) scan_ctl(filename, argc, argv, "MET_NP", -1, "0", NULL);
7496 if (ctl->met_np > EP)
7497 ERRMSG("Too many pressure levels!");
7498 ctl->met_press_level_def =
7499 (int) scan_ctl(filename, argc, argv, "MET_PRESS_LEVEL_DEF", -1, "-1",
7500 NULL);
7501 if (ctl->met_press_level_def >= 0) {
7502 level_definitions(ctl);
7503 } else {
7504 if (ctl->met_np > 0) {
7505 for (int ip = 0; ip < ctl->met_np; ip++)
7506 ctl->met_p[ip] =
7507 scan_ctl(filename, argc, argv, "MET_P", ip, "", NULL);
7508 }
7509 }
7510 ctl->met_nlev =
7511 (int) scan_ctl(filename, argc, argv, "MET_NLEV", -1, "0", NULL);
7512 if (ctl->met_nlev > EP)
7513 ERRMSG("Too many model levels!");
7514 for (int ip = 0; ip < ctl->met_nlev; ip++)
7515 ctl->met_lev_hyam[ip] =
7516 scan_ctl(filename, argc, argv, "MET_LEV_HYAM", ip, "", NULL);
7517 for (int ip = 0; ip < ctl->met_nlev; ip++)
7518 ctl->met_lev_hybm[ip] =
7519 scan_ctl(filename, argc, argv, "MET_LEV_HYBM", ip, "", NULL);
7520 ctl->met_geopot_sx =
7521 (int) scan_ctl(filename, argc, argv, "MET_GEOPOT_SX", -1, "-1", NULL);
7522 ctl->met_geopot_sy =
7523 (int) scan_ctl(filename, argc, argv, "MET_GEOPOT_SY", -1, "-1", NULL);
7524 ctl->met_relhum =
7525 (int) scan_ctl(filename, argc, argv, "MET_RELHUM", -1, "0", NULL);
7526 ctl->met_cape =
7527 (int) scan_ctl(filename, argc, argv, "MET_CAPE", -1, "1", NULL);
7528 if (ctl->met_cape < 0 || ctl->met_cape > 1)
7529 ERRMSG("Set MET_CAPE to 0 or 1!");
7530 ctl->met_pbl =
7531 (int) scan_ctl(filename, argc, argv, "MET_PBL", -1, "3", NULL);
7532 if (ctl->met_pbl < 0 || ctl->met_pbl > 3)
7533 ERRMSG("Set MET_PBL to 0 ... 3!");
7534 ctl->met_pbl_min =
7535 scan_ctl(filename, argc, argv, "MET_PBL_MIN", -1, "0.1", NULL);
7536 ctl->met_pbl_max =
7537 scan_ctl(filename, argc, argv, "MET_PBL_MAX", -1, "5.0", NULL);
7538 ctl->met_tropo =
7539 (int) scan_ctl(filename, argc, argv, "MET_TROPO", -1, "3", NULL);
7540 if (ctl->met_tropo < 0 || ctl->met_tropo > 5)
7541 ERRMSG("Set MET_TROPO to 0 ... 5!");
7542 ctl->met_tropo_pv =
7543 scan_ctl(filename, argc, argv, "MET_TROPO_PV", -1, "3.5", NULL);
7544 ctl->met_tropo_theta =
7545 scan_ctl(filename, argc, argv, "MET_TROPO_THETA", -1, "380", NULL);
7546 ctl->met_tropo_spline =
7547 (int) scan_ctl(filename, argc, argv, "MET_TROPO_SPLINE", -1, "1", NULL);
7548 ctl->met_dt_out =
7549 scan_ctl(filename, argc, argv, "MET_DT_OUT", -1, "0.1", NULL);
7550 ctl->met_cache =
7551 (int) scan_ctl(filename, argc, argv, "MET_CACHE", -1, "0", NULL);
7552 ctl->met_mpi_share =
7553 (int) scan_ctl(filename, argc, argv, "MET_MPI_SHARE", -1, "0", NULL);
7554
7555 /* Sorting... */
7556 ctl->sort_dt = scan_ctl(filename, argc, argv, "SORT_DT", -1, "-999", NULL);
7557
7558 /* Isosurface parameters... */
7559 ctl->isosurf =
7560 (int) scan_ctl(filename, argc, argv, "ISOSURF", -1, "0", NULL);
7561 if (ctl->isosurf >= 1 && ctl->isosurf <= 3 && ctl->qnt_iso_var < 0)
7562 ERRMSG("Add quantity iso_var for ISOSURF=1, 2, or 3!");
7563 scan_ctl(filename, argc, argv, "BALLOON", -1, "-", ctl->balloon);
7564
7565 /* Random number generator... */
7566 ctl->rng_type =
7567 (int) scan_ctl(filename, argc, argv, "RNG_TYPE", -1, "1", NULL);
7568 if (ctl->rng_type < 0 || ctl->rng_type > 2)
7569 ERRMSG("Set RNG_TYPE to 0, 1, or 2!");
7570
7571 /* Advection parameters... */
7572 ctl->advect = (int) scan_ctl(filename, argc, argv, "ADVECT", -1, "2", NULL);
7573 if (!(ctl->advect == 1 || ctl->advect == 2 || ctl->advect == 4))
7574 ERRMSG("Set ADVECT to 1, 2, or 4!");
7575
7576 /* Diffusion parameters... */
7577 ctl->diffusion
7578 = (int) scan_ctl(filename, argc, argv, "DIFFUSION", -1, "0", NULL);
7579 if (ctl->diffusion < 0 || ctl->diffusion > 1)
7580 ERRMSG("Set DIFFUSION to 0 or 1!");
7581 ctl->turb_pbl_scheme =
7582 (int) scan_ctl(filename, argc, argv, "TURB_PBL_SCHEME", -1, "0", NULL);
7583 if (ctl->turb_pbl_scheme < 0 || ctl->turb_pbl_scheme > 1)
7584 ERRMSG("Set TURB_PBL_SCHEME to 0 or 1!");
7585 ctl->turb_dx_pbl =
7586 scan_ctl(filename, argc, argv, "TURB_DX_PBL", -1, "50", NULL);
7587 ctl->turb_dx_trop =
7588 scan_ctl(filename, argc, argv, "TURB_DX_TROP", -1, "50", NULL);
7589 ctl->turb_dx_strat =
7590 scan_ctl(filename, argc, argv, "TURB_DX_STRAT", -1, "0", NULL);
7591 ctl->turb_dz_pbl =
7592 scan_ctl(filename, argc, argv, "TURB_DZ_PBL", -1, "0", NULL);
7593 ctl->turb_dz_trop =
7594 scan_ctl(filename, argc, argv, "TURB_DZ_TROP", -1, "0", NULL);
7595 ctl->turb_dz_strat =
7596 scan_ctl(filename, argc, argv, "TURB_DZ_STRAT", -1, "0.1", NULL);
7597 ctl->turb_mesox =
7598 scan_ctl(filename, argc, argv, "TURB_MESOX", -1, "0.16", NULL);
7599 ctl->turb_mesoz =
7600 scan_ctl(filename, argc, argv, "TURB_MESOZ", -1, "0.16", NULL);
7601 ctl->turb_pbl_trans =
7602 scan_ctl(filename, argc, argv, "TURB_PBL_TRANS", -1, "0", NULL);
7603 if (ctl->turb_pbl_trans < 0 || ctl->turb_pbl_trans > 1)
7604 ERRMSG("TURB_PBL_TRANS must be in the range [0, 1]!");
7605
7606 /* Convection... */
7607 ctl->conv_mix_pbl
7608 = (int) scan_ctl(filename, argc, argv, "CONV_MIX_PBL", -1, "0", NULL);
7609 ctl->conv_pbl_trans
7610 = scan_ctl(filename, argc, argv, "CONV_PBL_TRANS", -1, "0", NULL);
7611 if (ctl->conv_pbl_trans < 0 || ctl->conv_pbl_trans > 1)
7612 ERRMSG("CONV_PBL_TRANS must be in the range [0, 1]!");
7613 ctl->conv_cape
7614 = scan_ctl(filename, argc, argv, "CONV_CAPE", -1, "-999", NULL);
7615 ctl->conv_cin
7616 = scan_ctl(filename, argc, argv, "CONV_CIN", -1, "-999", NULL);
7617 ctl->conv_dt = scan_ctl(filename, argc, argv, "CONV_DT", -1, "-999", NULL);
7618
7619 /* Boundary conditions... */
7620 ctl->bound_mass =
7621 scan_ctl(filename, argc, argv, "BOUND_MASS", -1, "-999", NULL);
7622 ctl->bound_mass_trend =
7623 scan_ctl(filename, argc, argv, "BOUND_MASS_TREND", -1, "0", NULL);
7624 ctl->bound_vmr =
7625 scan_ctl(filename, argc, argv, "BOUND_VMR", -1, "-999", NULL);
7626 ctl->bound_vmr_trend =
7627 scan_ctl(filename, argc, argv, "BOUND_VMR_TREND", -1, "0", NULL);
7628 ctl->bound_lat0 =
7629 scan_ctl(filename, argc, argv, "BOUND_LAT0", -1, "-999", NULL);
7630 ctl->bound_lat1 =
7631 scan_ctl(filename, argc, argv, "BOUND_LAT1", -1, "-999", NULL);
7632 ctl->bound_p0 =
7633 scan_ctl(filename, argc, argv, "BOUND_P0", -1, "-999", NULL);
7634 ctl->bound_p1 =
7635 scan_ctl(filename, argc, argv, "BOUND_P1", -1, "-999", NULL);
7636 ctl->bound_dps =
7637 scan_ctl(filename, argc, argv, "BOUND_DPS", -1, "-999", NULL);
7638 ctl->bound_dzs =
7639 scan_ctl(filename, argc, argv, "BOUND_DZS", -1, "-999", NULL);
7640 ctl->bound_zetas =
7641 scan_ctl(filename, argc, argv, "BOUND_ZETAS", -1, "-999", NULL);
7642 ctl->bound_pbl =
7643 (int) scan_ctl(filename, argc, argv, "BOUND_PBL", -1, "0", NULL);
7644
7645 /* Species parameters... */
7646 scan_ctl(filename, argc, argv, "SPECIES", -1, "-", ctl->species);
7647 if (strcasecmp(ctl->species, "CF2Cl2") == 0) {
7648 ctl->molmass = 120.907;
7649 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 3e-5;
7650 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 3500.0;
7651 } else if (strcasecmp(ctl->species, "CFCl3") == 0) {
7652 ctl->molmass = 137.359;
7653 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 1.1e-4;
7654 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 3300.0;
7655 } else if (strcasecmp(ctl->species, "CH4") == 0) {
7656 ctl->molmass = 16.043;
7657 ctl->oh_chem_reaction = 2;
7658 ctl->oh_chem[0] = 2.45e-12;
7659 ctl->oh_chem[1] = 1775;
7660 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 1.4e-5;
7661 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 1600.0;
7662 } else if (strcasecmp(ctl->species, "CO") == 0) {
7663 ctl->molmass = 28.01;
7664 ctl->oh_chem_reaction = 3;
7665 ctl->oh_chem[0] = 6.9e-33;
7666 ctl->oh_chem[1] = 2.1;
7667 ctl->oh_chem[2] = 1.1e-12;
7668 ctl->oh_chem[3] = -1.3;
7669 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 9.7e-6;
7670 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 1300.0;
7671 } else if (strcasecmp(ctl->species, "CO2") == 0) {
7672 ctl->molmass = 44.009;
7673 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 3.3e-4;
7674 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 2400.0;
7675 } else if (strcasecmp(ctl->species, "H2O") == 0) {
7676 ctl->molmass = 18.01528;
7677 } else if (strcasecmp(ctl->species, "N2O") == 0) {
7678 ctl->molmass = 44.013;
7679 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 2.4e-4;
7680 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 2600.;
7681 } else if (strcasecmp(ctl->species, "NH3") == 0) {
7682 ctl->molmass = 17.031;
7683 ctl->oh_chem_reaction = 2;
7684 ctl->oh_chem[0] = 1.7e-12;
7685 ctl->oh_chem[1] = 710;
7686 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 5.9e-1;
7687 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 4200.0;
7688 } else if (strcasecmp(ctl->species, "HNO3") == 0) {
7689 ctl->molmass = 63.012;
7690 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 2.1e3;
7691 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 8700.0;
7692 } else if (strcasecmp(ctl->species, "NO") == 0) {
7693 ctl->molmass = 30.006;
7694 ctl->oh_chem_reaction = 3;
7695 ctl->oh_chem[0] = 7.1e-31;
7696 ctl->oh_chem[1] = 2.6;
7697 ctl->oh_chem[2] = 3.6e-11;
7698 ctl->oh_chem[3] = 0.1;
7699 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 1.9e-5;
7700 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 1600.0;
7701 } else if (strcasecmp(ctl->species, "NO2") == 0) {
7702 ctl->molmass = 46.005;
7703 ctl->oh_chem_reaction = 3;
7704 ctl->oh_chem[0] = 1.8e-30;
7705 ctl->oh_chem[1] = 3.0;
7706 ctl->oh_chem[2] = 2.8e-11;
7707 ctl->oh_chem[3] = 0.0;
7708 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 1.2e-4;
7709 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 2400.0;
7710 } else if (strcasecmp(ctl->species, "O3") == 0) {
7711 ctl->molmass = 47.997;
7712 ctl->oh_chem_reaction = 2;
7713 ctl->oh_chem[0] = 1.7e-12;
7714 ctl->oh_chem[1] = 940;
7715 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 1e-4;
7716 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 2800.0;
7717 } else if (strcasecmp(ctl->species, "SF6") == 0) {
7718 ctl->molmass = 146.048;
7719 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 2.4e-6;
7720 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 3100.0;
7721 } else if (strcasecmp(ctl->species, "SO2") == 0) {
7722 ctl->molmass = 64.066;
7723 ctl->oh_chem_reaction = 3;
7724 ctl->oh_chem[0] = 2.9e-31;
7725 ctl->oh_chem[1] = 4.1;
7726 ctl->oh_chem[2] = 1.7e-12;
7727 ctl->oh_chem[3] = -0.2;
7728 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = SO2_HENRY_REF;
7729 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = SO2_HENRY_TEMP;
7730 }
7731
7732 /* Molar mass... */
7733 char defstr[LEN];
7734 sprintf(defstr, "%g", ctl->molmass);
7735 ctl->molmass = scan_ctl(filename, argc, argv, "MOLMASS", -1, defstr, NULL);
7736
7737 /* OH chemistry... */
7738 sprintf(defstr, "%d", ctl->oh_chem_reaction);
7739 ctl->oh_chem_reaction =
7740 (int) scan_ctl(filename, argc, argv, "OH_CHEM_REACTION", -1, defstr,
7741 NULL);
7742 for (int ip = 0; ip < 4; ip++) {
7743 sprintf(defstr, "%g", ctl->oh_chem[ip]);
7744 ctl->oh_chem[ip] =
7745 scan_ctl(filename, argc, argv, "OH_CHEM", ip, defstr, NULL);
7746 }
7747 ctl->oh_chem_beta =
7748 scan_ctl(filename, argc, argv, "OH_CHEM_BETA", -1, "0", NULL);
7749
7750 /* H2O2 chemistry... */
7751 ctl->h2o2_chem_reaction =
7752 (int) scan_ctl(filename, argc, argv, "H2O2_CHEM_REACTION", -1, "0", NULL);
7753
7754 /* KPP chemistry... */
7755 ctl->kpp_chem =
7756 (int) scan_ctl(filename, argc, argv, "KPP_CHEM", -1, "0", NULL);
7757 ctl->dt_kpp = scan_ctl(filename, argc, argv, "DT_KPP", -1, "1800", NULL);
7758
7759 /* First order tracer chemistry... */
7760 ctl->tracer_chem =
7761 (int) scan_ctl(filename, argc, argv, "TRACER_CHEM", -1, "0", NULL);
7762
7763 /* Radioactive decay... */
7764 ctl->radio_decay =
7765 (int) scan_ctl(filename, argc, argv, "RADIO_DECAY", -1, "0", NULL);
7766 ctl->radio_depo =
7767 (int) scan_ctl(filename, argc, argv, "RADIO_DEPO", -1, "0", NULL);
7768 if (ctl->radio_depo && ctl->met_coord_type != 0)
7769 ERRMSG("Radioactive deposition requires a lat/lon meteorological grid!");
7770
7771 /* Wet deposition... */
7772 for (int ip = 0; ip < 2; ip++) {
7773 sprintf(defstr, "%g", ctl->wet_depo_ic_h[ip]);
7774 ctl->wet_depo_ic_h[ip] =
7775 scan_ctl(filename, argc, argv, "WET_DEPO_IC_H", ip, defstr, NULL);
7776 }
7777 for (int ip = 0; ip < 1; ip++) {
7778 sprintf(defstr, "%g", ctl->wet_depo_bc_h[ip]);
7779 ctl->wet_depo_bc_h[ip] =
7780 scan_ctl(filename, argc, argv, "WET_DEPO_BC_H", ip, defstr, NULL);
7781 }
7782 ctl->wet_depo_so2_ph =
7783 scan_ctl(filename, argc, argv, "WET_DEPO_SO2_PH", -1, "0", NULL);
7784 ctl->wet_depo_ic_a =
7785 scan_ctl(filename, argc, argv, "WET_DEPO_IC_A", -1, "0", NULL);
7786 ctl->wet_depo_ic_b =
7787 scan_ctl(filename, argc, argv, "WET_DEPO_IC_B", -1, "0", NULL);
7788 ctl->wet_depo_bc_a =
7789 scan_ctl(filename, argc, argv, "WET_DEPO_BC_A", -1, "0", NULL);
7790 ctl->wet_depo_bc_b =
7791 scan_ctl(filename, argc, argv, "WET_DEPO_BC_B", -1, "0", NULL);
7792 ctl->wet_depo_pre[0] =
7793 scan_ctl(filename, argc, argv, "WET_DEPO_PRE", 0, "0.5", NULL);
7794 ctl->wet_depo_pre[1] =
7795 scan_ctl(filename, argc, argv, "WET_DEPO_PRE", 1, "0.36", NULL);
7797 scan_ctl(filename, argc, argv, "WET_DEPO_IC_RET_RATIO", -1, "1", NULL);
7799 scan_ctl(filename, argc, argv, "WET_DEPO_BC_RET_RATIO", -1, "1", NULL);
7800
7801 /* Dry deposition... */
7802 ctl->dry_depo_vdep =
7803 scan_ctl(filename, argc, argv, "DRY_DEPO_VDEP", -1, "0", NULL);
7804 ctl->dry_depo_dp =
7805 scan_ctl(filename, argc, argv, "DRY_DEPO_DP", -1, "30", NULL);
7806
7807 /* Climatological data... */
7808 scan_ctl(filename, argc, argv, "CLIM_PHOTO", -1,
7809 "../../data/clams_photolysis_rates.nc", ctl->clim_photo);
7810 scan_ctl(filename, argc, argv, "CLIM_HNO3_FILENAME", -1,
7811 "../../data/gozcards_HNO3.nc", ctl->clim_hno3_filename);
7812 scan_ctl(filename, argc, argv, "CLIM_OH_FILENAME", -1,
7813 "../../data/clams_radical_species_vmr.nc", ctl->clim_oh_filename);
7814 scan_ctl(filename, argc, argv, "CLIM_H2O2_FILENAME", -1,
7815 "../../data/cams_H2O2.nc", ctl->clim_h2o2_filename);
7816 scan_ctl(filename, argc, argv, "CLIM_HO2_FILENAME", -1,
7817 "../../data/clams_radical_species_vmr.nc", ctl->clim_ho2_filename);
7818 scan_ctl(filename, argc, argv, "CLIM_O1D_FILENAME", -1,
7819 "../../data/clams_radical_species_vmr.nc", ctl->clim_o1d_filename);
7820 scan_ctl(filename, argc, argv, "CLIM_CCL4_TIMESERIES", -1,
7821 "../../data/noaa_gml_ccl4.tab", ctl->clim_ccl4_timeseries);
7822 scan_ctl(filename, argc, argv, "CLIM_CCL3F_TIMESERIES", -1,
7823 "../../data/noaa_gml_cfc11.tab", ctl->clim_ccl3f_timeseries);
7824 scan_ctl(filename, argc, argv, "CLIM_CCL2F2_TIMESERIES", -1,
7825 "../../data/noaa_gml_cfc12.tab", ctl->clim_ccl2f2_timeseries);
7826 scan_ctl(filename, argc, argv, "CLIM_N2O_TIMESERIES", -1,
7827 "../../data/noaa_gml_n2o.tab", ctl->clim_n2o_timeseries);
7828 scan_ctl(filename, argc, argv, "CLIM_SF6_TIMESERIES", -1,
7829 "../../data/noaa_gml_sf6.tab", ctl->clim_sf6_timeseries);
7830
7831 /* Mixing... */
7832 ctl->mixing_dt =
7833 scan_ctl(filename, argc, argv, "MIXING_DT", -1, "3600.", NULL);
7834 ctl->mixing_tau_trop =
7835 scan_ctl(filename, argc, argv, "MIXING_TAU_TROP", -1, "-999", NULL);
7836 ctl->mixing_tau_strat =
7837 scan_ctl(filename, argc, argv, "MIXING_TAU_STRAT", -1, "-999", NULL);
7838 ctl->mixing_z0 =
7839 scan_ctl(filename, argc, argv, "MIXING_Z0", -1, "-5", NULL);
7840 ctl->mixing_z1 =
7841 scan_ctl(filename, argc, argv, "MIXING_Z1", -1, "85", NULL);
7842 ctl->mixing_nz =
7843 (int) scan_ctl(filename, argc, argv, "MIXING_NZ", -1, "90", NULL);
7844 ctl->mixing_lon0 =
7845 scan_ctl(filename, argc, argv, "MIXING_LON0", -1, "-180", NULL);
7846 ctl->mixing_lon1 =
7847 scan_ctl(filename, argc, argv, "MIXING_LON1", -1, "180", NULL);
7848 ctl->mixing_nx =
7849 (int) scan_ctl(filename, argc, argv, "MIXING_NX", -1, "360", NULL);
7850 ctl->mixing_lat0 =
7851 scan_ctl(filename, argc, argv, "MIXING_LAT0", -1, "-90", NULL);
7852 ctl->mixing_lat1 =
7853 scan_ctl(filename, argc, argv, "MIXING_LAT1", -1, "90", NULL);
7854 ctl->mixing_ny =
7855 (int) scan_ctl(filename, argc, argv, "MIXING_NY", -1, "180", NULL);
7856 if (ctl->mixing_nx < 1 || ctl->mixing_ny < 1 || ctl->mixing_nz < 1
7857 || ctl->mixing_lon0 >= ctl->mixing_lon1
7858 || ctl->mixing_lat0 >= ctl->mixing_lat1
7859 || ctl->mixing_z0 >= ctl->mixing_z1
7860 || ctl->mixing_lat0 < -90 || ctl->mixing_lat1 > 90)
7861 ERRMSG("Invalid mixing grid!");
7862 if ((ctl->mixing_tau_trop > 0) != (ctl->mixing_tau_strat > 0)
7863 || ctl->mixing_tau_trop == 0 || ctl->mixing_tau_strat == 0)
7864 ERRMSG("Mixing times must either both be positive or both be disabled!");
7865
7866 /* Chemistry grid... */
7867 ctl->chemgrid_z0 =
7868 scan_ctl(filename, argc, argv, "CHEMGRID_Z0", -1, "-5", NULL);
7869 ctl->chemgrid_z1 =
7870 scan_ctl(filename, argc, argv, "CHEMGRID_Z1", -1, "85", NULL);
7871 ctl->chemgrid_nz =
7872 (int) scan_ctl(filename, argc, argv, "CHEMGRID_NZ", -1, "90", NULL);
7873 ctl->chemgrid_lon0 =
7874 scan_ctl(filename, argc, argv, "CHEMGRID_LON0", -1, "-180", NULL);
7875 ctl->chemgrid_lon1 =
7876 scan_ctl(filename, argc, argv, "CHEMGRID_LON1", -1, "180", NULL);
7877 ctl->chemgrid_nx =
7878 (int) scan_ctl(filename, argc, argv, "CHEMGRID_NX", -1, "360", NULL);
7879 ctl->chemgrid_lat0 =
7880 scan_ctl(filename, argc, argv, "CHEMGRID_LAT0", -1, "-90", NULL);
7881 ctl->chemgrid_lat1 =
7882 scan_ctl(filename, argc, argv, "CHEMGRID_LAT1", -1, "90", NULL);
7883 ctl->chemgrid_ny =
7884 (int) scan_ctl(filename, argc, argv, "CHEMGRID_NY", -1, "180", NULL);
7885 if (ctl->chemgrid_nx < 1 || ctl->chemgrid_ny < 1 || ctl->chemgrid_nz < 1
7886 || ctl->chemgrid_lon0 >= ctl->chemgrid_lon1
7887 || ctl->chemgrid_lat0 >= ctl->chemgrid_lat1
7888 || ctl->chemgrid_z0 >= ctl->chemgrid_z1
7889 || ctl->chemgrid_lat0 < -90 || ctl->chemgrid_lat1 > 90)
7890 ERRMSG("Invalid chemistry grid!");
7891
7892 /* Exponential decay... */
7893 ctl->tdec_trop = scan_ctl(filename, argc, argv, "TDEC_TROP", -1, "0", NULL);
7894 ctl->tdec_strat =
7895 scan_ctl(filename, argc, argv, "TDEC_STRAT", -1, "0", NULL);
7896
7897 /* PSC analysis... */
7898 ctl->psc_h2o = scan_ctl(filename, argc, argv, "PSC_H2O", -1, "4e-6", NULL);
7899 ctl->psc_hno3 =
7900 scan_ctl(filename, argc, argv, "PSC_HNO3", -1, "9e-9", NULL);
7901
7902 /* Output of atmospheric data... */
7903 scan_ctl(filename, argc, argv, "ATM_BASENAME", -1, "-", ctl->atm_basename);
7904 scan_ctl(filename, argc, argv, "ATM_GPFILE", -1, "-", ctl->atm_gpfile);
7905 ctl->atm_dt_out =
7906 scan_ctl(filename, argc, argv, "ATM_DT_OUT", -1, "86400", NULL);
7907 ctl->atm_filter =
7908 (int) scan_ctl(filename, argc, argv, "ATM_FILTER", -1, "0", NULL);
7909 ctl->atm_stride =
7910 (int) scan_ctl(filename, argc, argv, "ATM_STRIDE", -1, "1", NULL);
7911 ctl->atm_type =
7912 (int) scan_ctl(filename, argc, argv, "ATM_TYPE", -1, "0", NULL);
7913 ctl->atm_type_out =
7914 (int) scan_ctl(filename, argc, argv, "ATM_TYPE_OUT", -1, "-1", NULL);
7915 if (ctl->atm_type_out == -1)
7916 ctl->atm_type_out = ctl->atm_type;
7917 ctl->atm_nc_level =
7918 (int) scan_ctl(filename, argc, argv, "ATM_NC_LEVEL", -1, "0", NULL);
7919 for (int iq = 0; iq < ctl->nq; iq++)
7920 ctl->atm_nc_quant[iq] =
7921 (int) scan_ctl(filename, argc, argv, "ATM_NC_QUANT", iq, "0", NULL);
7922 ctl->obs_type =
7923 (int) scan_ctl(filename, argc, argv, "OBS_TYPE", -1, "0", NULL);
7924
7925 /* Output of mass budget data... */
7926 scan_ctl(filename, argc, argv, "BUDGET_BASENAME", -1, "-",
7927 ctl->budget_basename);
7928 ctl->budget_dt_out =
7929 scan_ctl(filename, argc, argv, "BUDGET_DT_OUT", -1, "86400", NULL);
7930 if (ctl->budget_basename[0] != '-' && ctl->budget_dt_out <= 0)
7931 ERRMSG("Invalid mass budget output settings!");
7932
7933 /* Output of radioactive deposition data... */
7934 scan_ctl(filename, argc, argv, "DEPO_BASENAME", -1, "-",
7935 ctl->depo_basename);
7936 ctl->depo_dt_out =
7937 scan_ctl(filename, argc, argv, "DEPO_DT_OUT", -1, "86400", NULL);
7938 ctl->depo_type =
7939 (int) scan_ctl(filename, argc, argv, "DEPO_TYPE", -1, "0", NULL);
7940
7941 /* Output of CSI data... */
7942 scan_ctl(filename, argc, argv, "CSI_BASENAME", -1, "-", ctl->csi_basename);
7943 scan_ctl(filename, argc, argv, "CSI_KERNEL", -1, "-", ctl->csi_kernel);
7944 ctl->csi_dt_out =
7945 scan_ctl(filename, argc, argv, "CSI_DT_OUT", -1, "86400", NULL);
7946 scan_ctl(filename, argc, argv, "CSI_OBSFILE", -1, "-", ctl->csi_obsfile);
7947 ctl->csi_obsmin =
7948 scan_ctl(filename, argc, argv, "CSI_OBSMIN", -1, "0", NULL);
7949 ctl->csi_modmin =
7950 scan_ctl(filename, argc, argv, "CSI_MODMIN", -1, "0", NULL);
7951 ctl->csi_z0 = scan_ctl(filename, argc, argv, "CSI_Z0", -1, "-5", NULL);
7952 ctl->csi_z1 = scan_ctl(filename, argc, argv, "CSI_Z1", -1, "85", NULL);
7953 ctl->csi_nz = (int) scan_ctl(filename, argc, argv, "CSI_NZ", -1, "1", NULL);
7954 ctl->csi_lon0 =
7955 scan_ctl(filename, argc, argv, "CSI_LON0", -1, "-180", NULL);
7956 ctl->csi_lon1 = scan_ctl(filename, argc, argv, "CSI_LON1", -1, "180", NULL);
7957 ctl->csi_nx =
7958 (int) scan_ctl(filename, argc, argv, "CSI_NX", -1, "360", NULL);
7959 ctl->csi_lat0 = scan_ctl(filename, argc, argv, "CSI_LAT0", -1, "-90", NULL);
7960 ctl->csi_lat1 = scan_ctl(filename, argc, argv, "CSI_LAT1", -1, "90", NULL);
7961 ctl->csi_ny =
7962 (int) scan_ctl(filename, argc, argv, "CSI_NY", -1, "180", NULL);
7963 if (ctl->csi_nx < 1 || ctl->csi_ny < 1 || ctl->csi_nz < 1
7964 || ctl->csi_lon0 >= ctl->csi_lon1
7965 || ctl->csi_lat0 >= ctl->csi_lat1 || ctl->csi_z0 >= ctl->csi_z1
7966 || ctl->csi_lat0 < -90 || ctl->csi_lat1 > 90)
7967 ERRMSG("Invalid CSI grid!");
7968
7969 /* Output of ensemble data... */
7970 ctl->nens = (int) scan_ctl(filename, argc, argv, "NENS", -1, "0", NULL);
7971 if (ctl->nens < 0)
7972 ERRMSG("NENS must not be negative!");
7973 if (ctl->nens > 0 && ctl->qnt_ens < 0)
7974 ERRMSG("Add quantity ens for ensemble calculations!");
7975 scan_ctl(filename, argc, argv, "ENS_BASENAME", -1, "-", ctl->ens_basename);
7976 ctl->ens_dt_out =
7977 scan_ctl(filename, argc, argv, "ENS_DT_OUT", -1, "86400", NULL);
7978
7979 /* Output of grid data... */
7980 scan_ctl(filename, argc, argv, "GRID_BASENAME", -1, "-",
7981 ctl->grid_basename);
7982 scan_ctl(filename, argc, argv, "GRID_KERNEL", -1, "-", ctl->grid_kernel);
7983 scan_ctl(filename, argc, argv, "GRID_GPFILE", -1, "-", ctl->grid_gpfile);
7984 ctl->grid_dt_out =
7985 scan_ctl(filename, argc, argv, "GRID_DT_OUT", -1, "86400", NULL);
7986 ctl->grid_sparse =
7987 (int) scan_ctl(filename, argc, argv, "GRID_SPARSE", -1, "0", NULL);
7988 ctl->grid_nc_level =
7989 (int) scan_ctl(filename, argc, argv, "GRID_NC_LEVEL", -1, "0", NULL);
7990 for (int iq = 0; iq < ctl->nq; iq++)
7991 ctl->grid_nc_quant[iq] =
7992 (int) scan_ctl(filename, argc, argv, "GRID_NC_QUANT", iq, "0", NULL);
7993 ctl->grid_stddev =
7994 (int) scan_ctl(filename, argc, argv, "GRID_STDDEV", -1, "0", NULL);
7995 ctl->grid_z0 = scan_ctl(filename, argc, argv, "GRID_Z0", -1, "-5", NULL);
7996 ctl->grid_z1 = scan_ctl(filename, argc, argv, "GRID_Z1", -1, "85", NULL);
7997 ctl->grid_nz =
7998 (int) scan_ctl(filename, argc, argv, "GRID_NZ", -1, "1", NULL);
7999 ctl->grid_lon0 =
8000 scan_ctl(filename, argc, argv, "GRID_LON0", -1, "-180", NULL);
8001 ctl->grid_lon1 =
8002 scan_ctl(filename, argc, argv, "GRID_LON1", -1, "180", NULL);
8003 ctl->grid_nx =
8004 (int) scan_ctl(filename, argc, argv, "GRID_NX", -1, "360", NULL);
8005 ctl->grid_lat0 =
8006 scan_ctl(filename, argc, argv, "GRID_LAT0", -1, "-90", NULL);
8007 ctl->grid_lat1 =
8008 scan_ctl(filename, argc, argv, "GRID_LAT1", -1, "90", NULL);
8009 ctl->grid_ny =
8010 (int) scan_ctl(filename, argc, argv, "GRID_NY", -1, "180", NULL);
8011 ctl->grid_type =
8012 (int) scan_ctl(filename, argc, argv, "GRID_TYPE", -1, "0", NULL);
8013 if (ctl->grid_nx < 1 || ctl->grid_nx > EX
8014 || ctl->grid_ny < 1 || ctl->grid_ny > EY || ctl->grid_nz < 1)
8015 ERRMSG("Invalid output grid dimensions!");
8016 if (ctl->grid_lon0 >= ctl->grid_lon1
8017 || ctl->grid_lat0 >= ctl->grid_lat1 || ctl->grid_z0 >= ctl->grid_z1
8018 || ctl->grid_lat0 < -90 || ctl->grid_lat1 > 90)
8019 ERRMSG("Invalid output grid boundaries!");
8020 if (ctl->depo_basename[0] != '-'
8021 && (ctl->depo_dt_out <= 0 || ctl->depo_type < 0 || ctl->depo_type > 1))
8022 ERRMSG("Invalid radioactive deposition output settings!");
8023
8024 /* Output of profile data... */
8025 scan_ctl(filename, argc, argv, "PROF_BASENAME", -1, "-",
8026 ctl->prof_basename);
8027 scan_ctl(filename, argc, argv, "PROF_OBSFILE", -1, "-", ctl->prof_obsfile);
8028 ctl->prof_z0 = scan_ctl(filename, argc, argv, "PROF_Z0", -1, "0", NULL);
8029 ctl->prof_z1 = scan_ctl(filename, argc, argv, "PROF_Z1", -1, "60", NULL);
8030 ctl->prof_nz =
8031 (int) scan_ctl(filename, argc, argv, "PROF_NZ", -1, "60", NULL);
8032 ctl->prof_lon0 =
8033 scan_ctl(filename, argc, argv, "PROF_LON0", -1, "-180", NULL);
8034 ctl->prof_lon1 =
8035 scan_ctl(filename, argc, argv, "PROF_LON1", -1, "180", NULL);
8036 ctl->prof_nx =
8037 (int) scan_ctl(filename, argc, argv, "PROF_NX", -1, "360", NULL);
8038 ctl->prof_lat0 =
8039 scan_ctl(filename, argc, argv, "PROF_LAT0", -1, "-90", NULL);
8040 ctl->prof_lat1 =
8041 scan_ctl(filename, argc, argv, "PROF_LAT1", -1, "90", NULL);
8042 ctl->prof_ny =
8043 (int) scan_ctl(filename, argc, argv, "PROF_NY", -1, "180", NULL);
8044 if (ctl->prof_nx < 1 || ctl->prof_ny < 1 || ctl->prof_nz < 1
8045 || ctl->prof_lon0 >= ctl->prof_lon1
8046 || ctl->prof_lat0 >= ctl->prof_lat1 || ctl->prof_z0 >= ctl->prof_z1
8047 || ctl->prof_lat0 < -90 || ctl->prof_lat1 > 90)
8048 ERRMSG("Invalid profile grid!");
8049
8050 /* Output of sample data... */
8051 scan_ctl(filename, argc, argv, "SAMPLE_BASENAME", -1, "-",
8052 ctl->sample_basename);
8053 scan_ctl(filename, argc, argv, "SAMPLE_KERNEL", -1, "-",
8054 ctl->sample_kernel);
8055 scan_ctl(filename, argc, argv, "SAMPLE_OBSFILE", -1, "-",
8056 ctl->sample_obsfile);
8057 ctl->sample_dx =
8058 scan_ctl(filename, argc, argv, "SAMPLE_DX", -1, "50", NULL);
8059 ctl->sample_dz =
8060 scan_ctl(filename, argc, argv, "SAMPLE_DZ", -1, "-999", NULL);
8061
8062 /* Output of station data... */
8063 scan_ctl(filename, argc, argv, "STAT_BASENAME", -1, "-",
8064 ctl->stat_basename);
8065 ctl->stat_lon = scan_ctl(filename, argc, argv, "STAT_LON", -1, "0", NULL);
8066 ctl->stat_lat = scan_ctl(filename, argc, argv, "STAT_LAT", -1, "0", NULL);
8067 ctl->stat_r = scan_ctl(filename, argc, argv, "STAT_R", -1, "50", NULL);
8068 ctl->stat_t0 =
8069 scan_ctl(filename, argc, argv, "STAT_T0", -1, "-1e100", NULL);
8070 ctl->stat_t1 = scan_ctl(filename, argc, argv, "STAT_T1", -1, "1e100", NULL);
8071
8072 /* Output of VTK data... */
8073 scan_ctl(filename, argc, argv, "VTK_BASENAME", -1, "-", ctl->vtk_basename);
8074 ctl->vtk_dt_out =
8075 scan_ctl(filename, argc, argv, "VTK_DT_OUT", -1, "86400", NULL);
8076 ctl->vtk_stride =
8077 (int) scan_ctl(filename, argc, argv, "VTK_STRIDE", -1, "1", NULL);
8078 ctl->vtk_scale =
8079 scan_ctl(filename, argc, argv, "VTK_SCALE", -1, "1.0", NULL);
8080 ctl->vtk_offset =
8081 scan_ctl(filename, argc, argv, "VTK_OFFSET", -1, "0.0", NULL);
8082 ctl->vtk_sphere =
8083 (int) scan_ctl(filename, argc, argv, "VTK_SPHERE", -1, "0", NULL);
8084
8085 /* Domain decomposition... */
8086
8087 /* Only the trac tool sets this flag after reading the control parameters... */
8088 ctl->dd_supported = 0;
8089#ifdef DD
8090 /* Domain decomposition is only implemented by the trac tool and only works
8091 if the executable was started with one task per subdomain. Therefore, use
8092 it by default only if MPI has been initialized by the calling tool and if
8093 more than one task is running; otherwise, stay in serial mode. */
8094 int mpi_initialized = 0;
8095 int mpi_size = 1;
8096 MPI_Initialized(&mpi_initialized);
8097 if (mpi_initialized)
8098 MPI_Comm_size(MPI_COMM_WORLD, &mpi_size);
8099 ctl->dd =
8100 (int) scan_ctl(filename, argc, argv, "DD", -1,
8101 (mpi_initialized && mpi_size > 1) ? "1" : "0", NULL);
8102#else
8103 ctl->dd = (int) scan_ctl(filename, argc, argv, "DD", -1, "0", NULL);
8104#endif
8105
8107 (int) scan_ctl(filename, argc, argv, "DD_SUBDOMAINS_MERIDIONAL", -1,
8108 (ctl->dd == 1) ? "2" : "1", NULL);
8109 ctl->dd_subdomains_zonal =
8110 (int) scan_ctl(filename, argc, argv, "DD_SUBDOMAINS_ZONAL", -1,
8111 (ctl->dd == 1) ? "2" : "1", NULL);
8112 ctl->dd_halos_size =
8113 (int) scan_ctl(filename, argc, argv, "DD_HALOS_SIZE", -1, "1", NULL);
8114 ctl->dd_sort_dt =
8115 (double) scan_ctl(filename, argc, argv, "DD_SORT_DT", -1, "1800", NULL);
8116 ctl->dd_exchange_mode =
8117 (int) scan_ctl(filename, argc, argv, "DD_EXCHANGE_MODE", -1, "0", NULL);
8118 if (ctl->dd_exchange_mode < 0 || ctl->dd_exchange_mode > 1)
8119 ERRMSG("Set DD_EXCHANGE_MODE to 0 (sort/push) or 1 (no sort)!");
8120 if (ctl->met_nc_read_mode == 1 &&
8121 (!ctl->dd || ctl->met_type != 0 || ctl->met_convention != 0))
8122 ERRMSG("MET_NC_READ_MODE=1 requires MPI DD, NetCDF, and MET_CONVENTION=0!");
8123#ifndef DD
8124 if (ctl->met_nc_read_mode == 1)
8125 ERRMSG("MET_NC_READ_MODE=1 requires a DD build!");
8126#endif
8127
8128#ifdef DD
8129 /* Check the domain decomposition configuration... */
8130 if (ctl->dd) {
8131 if (!mpi_initialized)
8132 ERRMSG
8133 ("Domain decomposition requires to start the executable with one task per subdomain!");
8134 if (ctl->dd_halos_size < 0)
8135 ERRMSG("DD_HALOS_SIZE must be non-negative!");
8136 if (ctl->dd_subdomains_zonal < 1 || ctl->dd_subdomains_meridional < 1)
8137 ERRMSG("The number of subdomains has to be positive!");
8138 if (ctl->dd_subdomains_zonal * ctl->dd_subdomains_meridional == 1)
8139 ERRMSG("Please provide zonal and meridional subdomain numbers!");
8140 if (mpi_size != ctl->dd_subdomains_zonal * ctl->dd_subdomains_meridional)
8141 ERRMSG("Number of tasks and subdomains is not identical!");
8142 if (ctl->qnt_current_subdomain < 0 || ctl->qnt_target_subdomain < 0)
8143 ERRMSG
8144 ("Domain decomposition requires the quantities current_subdomain and target_subdomain!");
8145 if (ctl->met_coord_type != 0)
8146 ERRMSG("Domain decomposition requires a lat/lon meteorological grid!");
8147 if (ctl->radio_depo)
8148 ERRMSG
8149 ("Radioactive deposition is not supported with domain decomposition!");
8150 }
8151#endif
8152}
8153
8154/*****************************************************************************/
8155
8157 const char *filename,
8158 const ctl_t *ctl,
8159 const clim_t *clim,
8160 met_t *met,
8161 dd_t *dd) {
8162
8163 /* Write info... */
8164 LOG(1, "Read meteo data: %s", filename);
8165
8166#ifdef DD
8167 /* With domain decomposition, each task reads the meteo data of its own
8168 subdomain only, which is not what the tools operating on the full meteo
8169 grid expect... */
8170 if (ctl->dd && !ctl->dd_supported)
8171 ERRMSG("The domain decomposition is only implemented by the trac tool!");
8172#endif
8173
8174 /* Set rank... */
8175 int rank = 0;
8176
8177 /* Check whether the meteo data is shared between the tasks... */
8178 int share = ctl->met_mpi_share;
8179#ifdef MPI
8180 /* Sharing the meteo data between the tasks requires to start the executable
8181 with a parallel launcher. If MPI has not been initialized, the meteo data
8182 is read by the single task itself... */
8183 int mpi_initialized = 0;
8184 MPI_Initialized(&mpi_initialized);
8185 if (share && !mpi_initialized) {
8186 WARN("Cannot share meteo data because MPI has not been initialized!");
8187 share = 0;
8188 }
8189 if (share)
8190 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
8191#endif
8192
8193 /* Check rank... */
8194 if (!share || rank == 0) {
8195
8196 /* Read netCDF data... */
8197 if (ctl->met_type == 0) {
8198 if (read_met_nc(filename, ctl, met, dd) != 1)
8199 return 0;
8200 }
8201
8202 /* Read binary data... */
8203 else if ((ctl->met_type >= 1 && ctl->met_type <= 5)
8204 || ctl->met_type == 7 || ctl->met_type == 8) {
8205 if (read_met_bin(filename, ctl, met) != 1)
8206 return 0;
8207 }
8208#ifdef ECCODES
8209 /* Read grib data... */
8210 else if (ctl->met_type == 6) {
8211 if (read_met_grib(filename, ctl, met) != 1)
8212 return 0;
8213 }
8214#endif
8215
8216 /* Not implemented... */
8217 else
8218 ERRMSG("MET_TYPE not implemented!");
8219
8220 /* Preprocessing for netCDF and grib files... */
8221 if (ctl->met_type == 0 || ctl->met_type == 6) {
8222
8223 /* Extrapolate data for lower boundary... */
8225
8226 /* Fix polar winds... */
8228
8229 /* Create periodic boundary conditions... */
8230#ifdef DD
8231 /* Zonal DD halos provide periodic boundaries only when there is more
8232 than one zonal subdomain. With a single zonal subdomain, append the
8233 periodic endpoint locally as in a run without DD. */
8234 if (!ctl->dd || ctl->dd_subdomains_zonal == 1)
8235 read_met_periodic(met);
8236#else
8237 read_met_periodic(met);
8238#endif
8239
8240 /* Downsampling... */
8241 read_met_sample(ctl, met);
8242
8243 /* Calculate geopotential heights... */
8244 read_met_geopot(ctl, met);
8245
8246 /* Calculate potential vorticity... */
8247 read_met_pv(met);
8248
8249 /* Calculate boundary layer data... */
8250 read_met_pbl(ctl, met);
8251
8252 /* Calculate tropopause data... */
8253 read_met_tropo(ctl, clim, met);
8254
8255 /* Calculate cloud properties... */
8256 read_met_cloud(met);
8257
8258 /* Calculate convective available potential energy... */
8259 read_met_cape(ctl, clim, met);
8260
8261 /* Calculate total column ozone... */
8262 read_met_ozone(met);
8263
8264 /* Detrending... */
8265 read_met_detrend(ctl, met);
8266
8267 /* Check meteo data and smooth zeta profiles ... */
8268 read_met_monotonize(ctl, met);
8269 }
8270 }
8271
8272 /* Broadcast data via MPI... */
8273#ifdef MPI
8274 if (share) {
8275
8276 /* Set timer... */
8277 SELECT_TIMER("READ_MET_MPI_BCAST", "COMM");
8278 LOG(2, "Broadcast data on rank %d...", rank);
8279
8280 /* Broadcast... */
8281 broadcast_large_data(met, sizeof(met_t));
8282 }
8283#endif
8284
8285 /* Return success... */
8286 return 1;
8287}
8288
8289/*****************************************************************************/
8290
8292 ctl_t *ctl,
8293 cache_t *cache,
8294 clim_t *clim,
8295 met_t **met0,
8296 met_t **met1,
8297 atm_t *atm,
8298 depo_t *depo,
8299 double t,
8300 dd_t *dd) {
8301
8302 /* Initialize modules... */
8303 if (t == ctl->t_start) {
8304
8305 /* Initialize isosurface data... */
8306 if (ctl->isosurf >= 1 && ctl->isosurf <= 4)
8307 module_isosurf_init(ctl, cache, *met0, *met1, atm);
8308
8309 /* Initialize advection... */
8310 module_advect_init(ctl, cache, *met0, *met1, atm);
8311
8312 /* Initialize chemistry... */
8313 module_chem_init(ctl, cache, clim, *met0, *met1, atm);
8314 }
8315
8316 /* Set time steps of air parcels... */
8317 module_timesteps(ctl, cache, *met0, atm, t);
8318
8319 /* Sort particles... */
8320 if (!(ctl->dd && ctl->dd_exchange_mode == 1)
8321 && ctl->sort_dt > 0 && fmod(t, ctl->sort_dt) == 0)
8322 module_sort(ctl, *met0, cache, atm);
8323
8324 /* Check positions (initial)... */
8325 module_position(cache, *met0, *met1, atm, 0);
8326
8327 /* Advection... */
8328 if (ctl->advect > 0)
8329 module_advect(ctl, cache, *met0, *met1, atm);
8330
8331 /* Turbulent diffusion... */
8332 if (ctl->diffusion
8333 && (ctl->turb_dx_pbl > 0 || ctl->turb_dz_pbl > 0
8334 || ctl->turb_dx_trop > 0 || ctl->turb_dz_trop > 0
8335 || ctl->turb_dx_strat > 0 || ctl->turb_dz_strat > 0))
8336 module_diff_turb(ctl, cache, clim, *met0, *met1, atm);
8337
8338 /* Optional PBL-specific diffusion scheme... */
8339 if (ctl->diffusion && ctl->turb_pbl_scheme == 1)
8340 module_diff_pbl(ctl, cache, *met0, *met1, atm);
8341
8342 /* Mesoscale diffusion... */
8343 if (ctl->diffusion && (ctl->turb_mesox > 0 || ctl->turb_mesoz > 0))
8344 module_diff_meso(ctl, cache, *met0, *met1, atm);
8345
8346 /* Convection... */
8347 if ((ctl->conv_mix_pbl || ctl->conv_cape >= 0)
8348 && (ctl->conv_dt <= 0 || fmod(t, ctl->conv_dt) == 0))
8349 module_convection(ctl, cache, *met0, *met1, atm);
8350
8351 /* Sedimentation... */
8352 if (ctl->qnt_rp >= 0 && ctl->qnt_rhop >= 0)
8353 module_sedi(ctl, cache, *met0, *met1, atm);
8354
8355 /* Isosurface... */
8356 if (ctl->isosurf >= 1 && ctl->isosurf <= 4)
8357 module_isosurf(ctl, cache, *met0, *met1, atm);
8358
8359 /* Check positions (final)... */
8360 module_position(cache, *met0, *met1, atm, 1);
8361
8362 /* Interpolate meteo data... */
8363 if (ctl->met_dt_out > 0
8364 && (ctl->met_dt_out < ctl->dt_mod || fmod(t, ctl->met_dt_out) == 0))
8365 module_meteo(ctl, cache, clim, *met0, *met1, atm);
8366
8367 /* Check boundary conditions (initial)... */
8368 if ((ctl->bound_lat0 < ctl->bound_lat1)
8369 && (ctl->bound_p0 > ctl->bound_p1))
8370 module_bound_cond(ctl, cache, clim, *met0, *met1, atm);
8371
8372 /* Initialize quantity of total loss rate... */
8373 if (ctl->qnt_loss_rate >= 0) {
8374 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,atm)") {
8375 atm->q[ctl->qnt_loss_rate][ip] = 0;
8376 }
8377 }
8378
8379 /* Decay of particle mass... */
8380 if (ctl->tdec_trop > 0 && ctl->tdec_strat > 0)
8381 module_decay(ctl, cache, clim, atm);
8382
8383 /* Interparcel mixing... */
8384 if (ctl->mixing_tau_trop > 0 && ctl->mixing_tau_strat > 0
8385 && (ctl->mixing_dt <= 0 || fmod(t, ctl->mixing_dt) == 0))
8386 module_mixing(ctl, clim, atm, t);
8387
8388 /* Calculate the tracer vmr in the chemistry grid... */
8389 if (ctl->oh_chem_reaction != 0 || ctl->h2o2_chem_reaction != 0
8390 || (ctl->kpp_chem && fmod(t, ctl->dt_kpp) == 0))
8391 module_chem_grid(ctl, *met0, *met1, atm, t);
8392
8393 /* OH chemistry... */
8394 if (ctl->oh_chem_reaction != 0)
8395 module_oh_chem(ctl, cache, clim, *met0, *met1, atm);
8396
8397 /* H2O2 chemistry (for SO2 aqueous phase oxidation)... */
8398 if (ctl->h2o2_chem_reaction != 0)
8399 module_h2o2_chem(ctl, cache, clim, *met0, *met1, atm);
8400
8401 /* First-order tracer chemistry... */
8402 if (ctl->tracer_chem)
8403 module_tracer_chem(ctl, cache, clim, *met0, *met1, atm);
8404
8405 /* Radioactive decay... */
8406 if (ctl->radio_decay)
8407 module_radio_decay(ctl, cache, atm);
8408
8409 /* Domain decomposition... */
8410#ifdef DD
8411 if (ctl->dd)
8412 module_dd(t, ctl, cache, dd, atm, met0);
8413#else
8414 (void) dd;
8415#endif
8416
8417 /* Radioactive deposition... */
8418 if (ctl->radio_depo)
8419 module_radio_depo(ctl, cache, *met0, *met1, atm, depo);
8420
8421 /* KPP chemistry... */
8422 if (ctl->kpp_chem && fmod(t, ctl->dt_kpp) == 0) {
8423#ifdef KPP
8424 module_kpp_chem(ctl, cache, clim, *met0, *met1, atm);
8425#else
8426 ERRMSG("Code was compiled without KPP!");
8427#endif
8428 }
8429
8430 /* Wet deposition... */
8431 if ((ctl->wet_depo_ic_a > 0 || ctl->wet_depo_ic_h[0] > 0)
8432 && (ctl->wet_depo_bc_a > 0 || ctl->wet_depo_bc_h[0] > 0))
8433 module_wet_depo(ctl, cache, *met0, *met1, atm);
8434
8435 /* Dry deposition... */
8436 if (ctl->dry_depo_vdep > 0)
8437 module_dry_depo(ctl, cache, *met0, *met1, atm);
8438
8439 /* Check boundary conditions (final)... */
8440 if ((ctl->bound_lat0 < ctl->bound_lat1)
8441 && (ctl->bound_p0 > ctl->bound_p1))
8442 module_bound_cond(ctl, cache, clim, *met0, *met1, atm);
8443}
8444
8445/*****************************************************************************/
8446
8448 const ctl_t *ctl,
8449 const cache_t *cache,
8450 const clim_t *clim,
8451 met_t **met0,
8452 met_t **met1,
8453 const atm_t *atm) {
8454
8455 /* Update GPU... */
8456 if (ctl != NULL) {
8457#ifdef _OPENACC
8458 SELECT_TIMER("UPDATE_DEVICE", "MEMORY");
8459#pragma acc update device(ctl[:1])
8460#endif
8461 }
8462
8463 if (cache != NULL) {
8464#ifdef _OPENACC
8465 SELECT_TIMER("UPDATE_DEVICE", "MEMORY");
8466#pragma acc update device(cache[:1])
8467#endif
8468 }
8469
8470 if (clim != NULL) {
8471#ifdef _OPENACC
8472 SELECT_TIMER("UPDATE_DEVICE", "MEMORY");
8473#pragma acc update device(clim[:1])
8474#endif
8475 }
8476
8477 if (met0 != NULL) {
8478#ifdef _OPENACC
8479 SELECT_TIMER("UPDATE_DEVICE", "MEMORY");
8480 met_t *met0up = *met0;
8481#pragma acc update device(met0up[:1])
8482#endif
8483 }
8484
8485 if (met1 != NULL) {
8486#ifdef _OPENACC
8487 SELECT_TIMER("UPDATE_DEVICE", "MEMORY");
8488 met_t *met1up = *met1;
8489#pragma acc update device(met1up[:1])
8490#endif
8491 }
8492
8493 if (atm != NULL) {
8494#ifdef _OPENACC
8495 SELECT_TIMER("UPDATE_DEVICE", "MEMORY");
8496#pragma acc update device(atm[:1])
8497#endif
8498 }
8499}
8500
8501/*****************************************************************************/
8502
8504 const ctl_t *ctl,
8505 const cache_t *cache,
8506 const clim_t *clim,
8507 met_t **met0,
8508 met_t **met1,
8509 const atm_t *atm) {
8510
8511 /* Update GPU... */
8512 if (ctl != NULL) {
8513#ifdef _OPENACC
8514 SELECT_TIMER("UPDATE_HOST", "MEMORY");
8515#pragma acc update host(ctl[:1])
8516#endif
8517 }
8518
8519 if (cache != NULL) {
8520#ifdef _OPENACC
8521 SELECT_TIMER("UPDATE_HOST", "MEMORY");
8522#pragma acc update host(cache[:1])
8523#endif
8524 }
8525
8526 if (clim != NULL) {
8527#ifdef _OPENACC
8528 SELECT_TIMER("UPDATE_HOST", "MEMORY");
8529#pragma acc update host(clim[:1])
8530#endif
8531 }
8532
8533 if (met0 != NULL) {
8534#ifdef _OPENACC
8535 SELECT_TIMER("UPDATE_HOST", "MEMORY");
8536 met_t *met0up = *met0;
8537#pragma acc update host(met0up[:1])
8538#endif
8539 }
8540
8541 if (met1 != NULL) {
8542#ifdef _OPENACC
8543 SELECT_TIMER("UPDATE_HOST", "MEMORY");
8544 met_t *met1up = *met1;
8545#pragma acc update host(met1up[:1])
8546#endif
8547 }
8548
8549 if (atm != NULL) {
8550#ifdef _OPENACC
8551 SELECT_TIMER("UPDATE_HOST", "MEMORY");
8552#pragma acc update host(atm[:1])
8553#endif
8554 }
8555}
8556
8557/*****************************************************************************/
8558
8560 const char *filename,
8561 const ctl_t *ctl,
8562 const atm_t *atm,
8563 const double t) {
8564
8565 /* Set timer... */
8566 SELECT_TIMER("WRITE_ATM", "OUTPUT");
8567
8568 /* Write info... */
8569 LOG(1, "Write atmospheric data: %s", filename);
8570
8571 /* Write ASCII data... */
8572 if (ctl->atm_type_out == 0)
8573 write_atm_asc(filename, ctl, atm, t);
8574
8575 /* Write binary data... */
8576 else if (ctl->atm_type_out == 1)
8577 write_atm_bin(filename, ctl, atm);
8578
8579 /* Write netCDF data... */
8580 else if (ctl->atm_type_out == 2)
8581 write_atm_nc(filename, ctl, atm);
8582
8583 /* Write CLaMS trajectory data... */
8584 else if (ctl->atm_type_out == 3)
8585 write_atm_clams_traj(filename, ctl, atm, t);
8586
8587 /* Write CLaMS pos data... */
8588 else if (ctl->atm_type_out == 4)
8589 write_atm_clams(filename, ctl, atm);
8590
8591 /* Error... */
8592 else
8593 ERRMSG("Atmospheric data type not supported!");
8594
8595 /* Write info... */
8596 double mini, maxi;
8597 LOG(2, "Number of particles: %d", atm->np);
8598 gsl_stats_minmax(&mini, &maxi, atm->time, 1, (size_t) atm->np);
8599 LOG(2, "Time range: %.2f ... %.2f s", mini, maxi);
8600 gsl_stats_minmax(&mini, &maxi, atm->p, 1, (size_t) atm->np);
8601 LOG(2, "Altitude range: %g ... %g km", Z(maxi), Z(mini));
8602 LOG(2, "Pressure range: %g ... %g hPa", maxi, mini);
8603 gsl_stats_minmax(&mini, &maxi, atm->lon, 1, (size_t) atm->np);
8604 LOG(2, "%s range: %g ... %g %s",
8605 ctl->met_coord_type == 0 ? "Longitude" : "X coordinate", mini, maxi,
8606 ctl->met_coord_type == 0 ? "deg" : "m");
8607 gsl_stats_minmax(&mini, &maxi, atm->lat, 1, (size_t) atm->np);
8608 LOG(2, "%s range: %g ... %g %s",
8609 ctl->met_coord_type == 0 ? "Latitude" : "Y coordinate", mini, maxi,
8610 ctl->met_coord_type == 0 ? "deg" : "m");
8611 for (int iq = 0; iq < ctl->nq; iq++) {
8612 char msg[5 * LEN];
8613 sprintf(msg, "Quantity %s range: %s ... %s %s",
8614 ctl->qnt_name[iq], ctl->qnt_format[iq],
8615 ctl->qnt_format[iq], ctl->qnt_unit[iq]);
8616 gsl_stats_minmax(&mini, &maxi, atm->q[iq], 1, (size_t) atm->np);
8617 LOG(2, msg, mini, maxi);
8618 }
8619}
8620
8621/*****************************************************************************/
8622
8624 const char *filename,
8625 const ctl_t *ctl,
8626 met_t *met) {
8627
8628 /* Set timer... */
8629 SELECT_TIMER("WRITE_MET", "OUTPUT");
8630
8631 /* Write info... */
8632 LOG(1, "Write meteo data: %s", filename);
8633
8634 /* Check compression flags... */
8635#ifndef ZFP
8636 if (ctl->met_type == 3)
8637 ERRMSG("MPTRAC was compiled without ZFP compression!");
8638#endif
8639#ifndef ZSTD
8640 if (ctl->met_type == 4)
8641 ERRMSG("MPTRAC was compiled without ZSTD compression!");
8642#endif
8643#ifndef LZ4
8644 if (ctl->met_type == 8)
8645 ERRMSG("MPTRAC was compiled without LZ4 compression!");
8646#endif
8647#ifndef CMS
8648 if (ctl->met_type == 5)
8649 ERRMSG("MPTRAC was compiled without cmultiscale compression!");
8650#endif
8651#ifndef SZ3
8652 if (ctl->met_type == 7)
8653 ERRMSG("MPTRAC was compiled without SZ3 compression!");
8654#endif
8655
8656 /* Write netCDF data... */
8657 if (ctl->met_type == 0)
8658 write_met_nc(filename, ctl, met);
8659
8660 /* Write binary data... */
8661 else if ((ctl->met_type >= 1 && ctl->met_type <= 5)
8662 || ctl->met_type == 7 || ctl->met_type == 8)
8663 write_met_bin(filename, ctl, met);
8664
8665 /* Not implemented... */
8666 else
8667 ERRMSG("MET_TYPE not implemented!");
8668}
8669
8670/*****************************************************************************/
8671
8673 const char *dirname,
8674 const ctl_t *ctl,
8675 met_t *met0,
8676 met_t *met1,
8677 atm_t *atm,
8678 depo_t *depo,
8679 const double t) {
8680
8681 char ext[10], filename[2 * LEN];
8682
8683 double r;
8684
8685 int year, mon, day, hour, min, sec;
8686
8687 /* Get time... */
8688 jsec2time(t, &year, &mon, &day, &hour, &min, &sec, &r);
8689
8690 /* Decide which particle outputs are due, including the final step... */
8691 const int final = (t == ctl->t_stop);
8692 const int output_atm = ctl->atm_basename[0] != '-'
8693 && (fmod(t, ctl->atm_dt_out) == 0 || final);
8694 const int output_grid = ctl->grid_basename[0] != '-'
8695 && (fmod(t, ctl->grid_dt_out) == 0 || final);
8696 const int output_budget = ctl->budget_basename[0] != '-'
8697 && (fmod(t, ctl->budget_dt_out) == 0 || final);
8698 const int output_depo = ctl->depo_basename[0] != '-'
8699 && (fmod(t, ctl->depo_dt_out) == 0 || final);
8700 const int output_ens = ctl->ens_basename[0] != '-'
8701 && (fmod(t, ctl->ens_dt_out) == 0 || final);
8702 const int output_vtk = ctl->vtk_basename[0] != '-'
8703 && (fmod(t, ctl->vtk_dt_out) == 0 || final);
8704
8705 /* Update host... */
8706 if (output_atm || output_grid || output_budget || output_ens || output_vtk
8707 || ctl->csi_basename[0] != '-' || ctl->prof_basename[0] != '-'
8708 || ctl->sample_basename[0] != '-' || ctl->stat_basename[0] != '-')
8709 mptrac_update_host(NULL, NULL, NULL, NULL, NULL, atm);
8710
8711 /* Write atmospheric data... */
8712 if (output_atm) {
8713 if (ctl->atm_type_out == 0)
8714 sprintf(ext, "tab");
8715 else if (ctl->atm_type_out == 1)
8716 sprintf(ext, "bin");
8717 else if (ctl->atm_type_out >= 2)
8718 sprintf(ext, "nc");
8719 sprintf(filename, "%s/%s_%04d_%02d_%02d_%02d_%02d_%02d.%s",
8720 dirname, ctl->atm_basename, year, mon, day, hour, min, sec, ext);
8721 mptrac_write_atm(filename, ctl, atm, t);
8722 }
8723
8724 /* Write gridded data... */
8725 if (output_grid) {
8726 sprintf(filename, "%s/%s_%04d_%02d_%02d_%02d_%02d_%02d.%s",
8727 dirname, ctl->grid_basename, year, mon, day, hour, min, sec,
8728 ctl->grid_type == 0 ? "tab" : "nc");
8729 write_grid(filename, ctl, met0, met1, atm, t);
8730 }
8731
8732 /* Write mass budget data... */
8733 if (output_budget) {
8734 sprintf(filename, "%s/%s.tab", dirname, ctl->budget_basename);
8735 write_budget(filename, ctl, atm, t);
8736 }
8737
8738 /* Write radioactive deposition data... */
8739 if (output_depo) {
8740#ifdef _OPENACC
8741#pragma acc update host(depo[:1])
8742#endif
8743 sprintf(filename, "%s/%s_%04d_%02d_%02d_%02d_%02d_%02d.%s",
8744 dirname, ctl->depo_basename, year, mon, day, hour, min, sec,
8745 ctl->depo_type == 0 ? "tab" : "nc");
8746 write_depo(filename, ctl, depo, t);
8747 }
8748
8749 /* Write CSI data... */
8750 if (ctl->csi_basename[0] != '-') {
8751 sprintf(filename, "%s/%s.tab", dirname, ctl->csi_basename);
8752 write_csi(filename, ctl, atm, t);
8753 }
8754
8755 /* Write ensemble data... */
8756 if (output_ens) {
8757 sprintf(filename, "%s/%s_%04d_%02d_%02d_%02d_%02d_%02d.tab",
8758 dirname, ctl->ens_basename, year, mon, day, hour, min, sec);
8759 write_ens(filename, ctl, atm, t);
8760 }
8761
8762 /* Write profile data... */
8763 if (ctl->prof_basename[0] != '-') {
8764 sprintf(filename, "%s/%s.tab", dirname, ctl->prof_basename);
8765 write_prof(filename, ctl, met0, met1, atm, t);
8766 }
8767
8768 /* Write sample data... */
8769 if (ctl->sample_basename[0] != '-') {
8770 sprintf(filename, "%s/%s.tab", dirname, ctl->sample_basename);
8771 write_sample(filename, ctl, met0, met1, atm, t);
8772 }
8773
8774 /* Write station data... */
8775 if (ctl->stat_basename[0] != '-') {
8776 sprintf(filename, "%s/%s.tab", dirname, ctl->stat_basename);
8777 write_station(filename, ctl, atm, t);
8778 }
8779
8780 /* Write VTK data... */
8781 if (output_vtk) {
8782 static int nvtk;
8783 if (t == ctl->t_start)
8784 nvtk = 0;
8785 sprintf(filename, "%s/%s_%05d.vtk", dirname, ctl->vtk_basename, ++nvtk);
8786 write_vtk(filename, ctl, atm, t);
8787 }
8788}
8789
8790/*****************************************************************************/
8791
8793 const double p,
8794 const double h2o,
8795 const double hno3) {
8796
8797 /* Check water vapor volume mixing ratio... */
8798 const double h2o_help = MAX(h2o, 0.1e-6);
8799
8800 /* Calculate T_NAT... */
8801 const double p_hno3 = hno3 * p / 1.333224;
8802 const double p_h2o = h2o_help * p / 1.333224;
8803 const double a = 0.009179 - 0.00088 * log10(p_h2o);
8804 const double b = (38.9855 - log10(p_hno3) - 2.7836 * log10(p_h2o)) / a;
8805 const double c = -11397.0 / a;
8806 double tnat = (-b + sqrt(b * b - 4. * c)) / 2.;
8807 double x2 = (-b - sqrt(b * b - 4. * c)) / 2.;
8808 if (x2 > 0)
8809 tnat = x2;
8810
8811 return tnat;
8812}
8813
8814/*****************************************************************************/
8815
8817 const ctl_t *ctl,
8818 const atm_t *atm,
8819 const int ip,
8820 const double pbl,
8821 const double ps) {
8822
8823 /* Get pressure range... */
8824 const double p1 = pbl - ctl->turb_pbl_trans * (ps - pbl);
8825 const double p0 = pbl;
8826
8827 /* Get weighting factor... */
8828 if (atm->p[ip] > p0)
8829 return 1;
8830 else if (atm->p[ip] < p1)
8831 return 0;
8832 else
8833 return LIN(p0, 1.0, p1, 0.0, atm->p[ip]);
8834}
8835
8836/*****************************************************************************/
8837
8839 const char *filename,
8840 const ctl_t *ctl,
8841 atm_t *atm) {
8842
8843 /* Open file... */
8844 FILE *in;
8845 if (!(in = fopen(filename, "r"))) {
8846 WARN("Cannot open file!");
8847 return 0;
8848 }
8849
8850 /* Read line... */
8851 char line[LEN];
8852 while (fgets(line, LEN, in)) {
8853
8854 /* Read data... */
8855 char *tok;
8856 TOK(line, tok, "%lg", atm->time[atm->np]);
8857 TOK(NULL, tok, "%lg", atm->p[atm->np]);
8858 TOK(NULL, tok, "%lg", atm->lon[atm->np]);
8859 TOK(NULL, tok, "%lg", atm->lat[atm->np]);
8860 for (int iq = 0; iq < ctl->nq; iq++)
8861 TOK(NULL, tok, "%lg", atm->q[iq][atm->np]);
8862
8863 /* Convert altitude to pressure... */
8864 atm->p[atm->np] = P(atm->p[atm->np]);
8865
8866 /* Increment data point counter... */
8867 if ((++atm->np) > NP)
8868 ERRMSG("Too many data points!");
8869 }
8870
8871 /* Close file... */
8872 fclose(in);
8873
8874 /* Return success... */
8875 return 1;
8876}
8877
8878/*****************************************************************************/
8879
8881 const char *filename,
8882 const ctl_t *ctl,
8883 atm_t *atm) {
8884
8885 /* Open file... */
8886 FILE *in;
8887 if (!(in = fopen(filename, "r")))
8888 return 0;
8889
8890 /* Check version of binary data... */
8891 int version;
8892 FREAD(&version, int,
8893 1,
8894 in);
8895 if (version != 100)
8896 ERRMSG("Wrong version of binary data!");
8897
8898 /* Read data... */
8899 FREAD(&atm->np, int,
8900 1,
8901 in);
8902 FREAD(atm->time, double,
8903 (size_t) atm->np,
8904 in);
8905 FREAD(atm->p, double,
8906 (size_t) atm->np,
8907 in);
8908 FREAD(atm->lon, double,
8909 (size_t) atm->np,
8910 in);
8911 FREAD(atm->lat, double,
8912 (size_t) atm->np,
8913 in);
8914 for (int iq = 0; iq < ctl->nq; iq++)
8915 FREAD(atm->q[iq], double,
8916 (size_t) atm->np,
8917 in);
8918
8919 /* Read final flag... */
8920 int final;
8921 FREAD(&final, int,
8922 1,
8923 in);
8924 if (final != 999)
8925 ERRMSG("Error while reading binary data!");
8926
8927 /* Close file... */
8928 fclose(in);
8929
8930 /* Return success... */
8931 return 1;
8932}
8933
8934/*****************************************************************************/
8935
8937 const char *filename,
8938 const ctl_t *ctl,
8939 atm_t *atm) {
8940
8941 if (ctl->met_coord_type != 0)
8942 ERRMSG("CLaMS atmospheric files support only lat/lon grids");
8943
8944 int ncid, varid;
8945
8946 /* Open file... */
8947 if (nc_open(filename, NC_NOWRITE, &ncid) != NC_NOERR)
8948 return 0;
8949
8950 /* Get dimensions... */
8951 NC_INQ_DIM("NPARTS", &atm->np, 1, NP, 1);
8952
8953 /* Get time... */
8954 if (nc_inq_varid(ncid, "TIME_INIT", &varid) == NC_NOERR) {
8955 NC(nc_get_var_double(ncid, varid, atm->time));
8956 } else {
8957 WARN("TIME_INIT not found use time instead!");
8958 double time_init;
8959 NC_GET_DOUBLE("time", &time_init, 1);
8960 for (int ip = 0; ip < atm->np; ip++) {
8961 atm->time[ip] = time_init;
8962 }
8963 }
8964
8965 /* Read zeta coordinate, pressure is optional... */
8966 if (ctl->advect_vert_coord == 1) {
8967 NC_GET_DOUBLE("ZETA", atm->q[ctl->qnt_zeta], 1);
8968 NC_GET_DOUBLE("PRESS", atm->p, 0);
8969 }
8970
8971 /* Read pressure, zeta coordinate is optional... */
8972 else {
8973 if (nc_inq_varid(ncid, "PRESS_INIT", &varid) == NC_NOERR) {
8974 NC(nc_get_var_double(ncid, varid, atm->p));
8975 } else {
8976 WARN("PRESS_INIT not found use PRESS instead!");
8977 nc_inq_varid(ncid, "PRESS", &varid);
8978 NC(nc_get_var_double(ncid, varid, atm->p));
8979 }
8980 }
8981
8982 /* Read further quantities if requested... */
8983 for (int iq = 0; iq < ctl->nq; iq++)
8984 NC_GET_DOUBLE(ctl->qnt_name[iq], atm->q[iq], 0);
8985
8986 /* Read longitude and latitude... */
8987 NC_GET_DOUBLE("LON", atm->lon, 1);
8988 NC_GET_DOUBLE("LAT", atm->lat, 1);
8989
8990 /* Close file... */
8991 NC(nc_close(ncid));
8992
8993 /* Return success... */
8994 return 1;
8995}
8996
8997/*****************************************************************************/
8998
9000 const char *filename,
9001 const ctl_t *ctl,
9002 atm_t *atm) {
9003
9004 int ncid, varid;
9005
9006 /* Open file... */
9007 if (nc_open(filename, NC_NOWRITE, &ncid) != NC_NOERR)
9008 return 0;
9009
9010 /* Get dimensions... */
9011 NC_INQ_DIM("obs", &atm->np, 1, NP, 1);
9012
9013 /* Read geolocations... */
9014 NC_GET_DOUBLE("time", atm->time, 1);
9015 NC_GET_DOUBLE("press", atm->p, 1);
9016 NC_GET_DOUBLE("lon", atm->lon, 1);
9017 NC_GET_DOUBLE("lat", atm->lat, 1);
9018
9019 /* Read variables... */
9020 for (int iq = 0; iq < ctl->nq; iq++)
9021 NC_GET_DOUBLE(ctl->qnt_name[iq], atm->q[iq], 0);
9022
9023 /* Close file... */
9024 NC(nc_close(ncid));
9025
9026 /* Return success... */
9027 return 1;
9028}
9029
9030/*****************************************************************************/
9031
9033 const char *filename,
9034 clim_photo_t *photo) {
9035
9036 int ncid, varid;
9037
9038 /* Write info... */
9039 LOG(1, "Read photolysis rates: %s", filename);
9040
9041 /* Open netCDF file... */
9042 if (nc_open(filename, NC_NOWRITE, &ncid) != NC_NOERR) {
9043 WARN("Photolysis rate data are missing!");
9044 return;
9045 }
9046
9047 /* Read pressure data... */
9048 NC_INQ_DIM("press", &photo->np, 2, CP, 1);
9049 NC_GET_DOUBLE("press", photo->p, 1);
9050 if (photo->p[0] < photo->p[1])
9051 ERRMSG("Pressure data are not descending!");
9052
9053 /* Read total column ozone data... */
9054 NC_INQ_DIM("total_o3col", &photo->no3c, 2, CO3, 1);
9055 NC_GET_DOUBLE("total_o3col", photo->o3c, 1);
9056 if (photo->o3c[0] > photo->o3c[1])
9057 ERRMSG("Total column ozone data are not ascending!");
9058
9059 /* Read solar zenith angle data... */
9060 NC_INQ_DIM("sza", &photo->nsza, 2, CSZA, 1);
9061 NC_GET_DOUBLE("sza", photo->sza, 1);
9062 if (photo->sza[0] > photo->sza[1])
9063 ERRMSG("Solar zenith angle data are not ascending!");
9064
9065 /* Read data... */
9066 read_clim_photo_help(ncid, "J_N2O", photo, photo->n2o);
9067 read_clim_photo_help(ncid, "J_CCl4", photo, photo->ccl4);
9068 read_clim_photo_help(ncid, "J_CFC-11", photo, photo->ccl3f);
9069 read_clim_photo_help(ncid, "J_CFC-12", photo, photo->ccl2f2);
9070 read_clim_photo_help(ncid, "J_O2", photo, photo->o2);
9071 read_clim_photo_help(ncid, "J_O3b", photo, photo->o3_1);
9072 read_clim_photo_help(ncid, "J_O3a", photo, photo->o3_2);
9073 read_clim_photo_help(ncid, "J_H2O2", photo, photo->h2o2);
9074 read_clim_photo_help(ncid, "J_H2O", photo, photo->h2o);
9075
9076 /* Close netCDF file... */
9077 NC(nc_close(ncid));
9078
9079 /* Write info... */
9080 LOG(2, "Number of pressure levels: %d", photo->np);
9081 LOG(2, "Altitude levels: %g, %g ... %g km",
9082 Z(photo->p[0]), Z(photo->p[1]), Z(photo->p[photo->np - 1]));
9083 LOG(2, "Pressure levels: %g, %g ... %g hPa",
9084 photo->p[0], photo->p[1], photo->p[photo->np - 1]);
9085 LOG(2, "Number of solar zenith angles: %d", photo->nsza);
9086 LOG(2, "Solar zenith angles: %g, %g ... %g deg",
9087 RAD2DEG(photo->sza[0]), RAD2DEG(photo->sza[1]),
9088 RAD2DEG(photo->sza[photo->nsza - 1]));
9089 LOG(2, "Number of total column ozone values: %d", photo->no3c);
9090 LOG(2, "Total column ozone: %g, %g ... %g DU",
9091 photo->o3c[0], photo->o3c[1], photo->o3c[photo->no3c - 1]);
9092 LOG(2, "N2O photolysis rate: %g, %g ... %g s**-1",
9093 photo->n2o[0][0][0], photo->n2o[1][0][0],
9094 photo->n2o[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9095 LOG(2, "CCl4 photolysis rate: %g, %g ... %g s**-1",
9096 photo->ccl4[0][0][0], photo->ccl4[1][0][0],
9097 photo->ccl4[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9098 LOG(2, "CFC-11 photolysis rate: %g, %g ... %g s**-1",
9099 photo->ccl3f[0][0][0], photo->ccl3f[1][0][0],
9100 photo->ccl3f[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9101 LOG(2, "CFC-12 photolysis rate: %g, %g ... %g s**-1",
9102 photo->ccl2f2[0][0][0], photo->ccl2f2[1][0][0],
9103 photo->ccl2f2[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9104 LOG(2, "O2 photolysis rate: %g, %g ... %g s**-1",
9105 photo->o2[0][0][0], photo->o2[1][0][0],
9106 photo->o2[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9107 LOG(2, "O3 -> O(1D) photolysis rate: %g, %g ... %g s**-1",
9108 photo->o3_1[0][0][0], photo->o3_1[1][0][0],
9109 photo->o3_1[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9110 LOG(2, "O3 -> O(3P) photolysis rate: %g, %g ... %g s**-1",
9111 photo->o3_2[0][0][0], photo->o3_2[1][0][0],
9112 photo->o3_2[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9113 LOG(2, "H2O2 photolysis rate: %g, %g ... %g s**-1",
9114 photo->h2o2[0][0][0], photo->h2o2[1][0][0],
9115 photo->h2o2[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9116 LOG(2, "H2O photolysis rate: %g, %g ... %g s**-1",
9117 photo->h2o[0][0][0], photo->h2o[1][0][0],
9118 photo->h2o[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9119}
9120
9121/*****************************************************************************/
9122
9124 const int ncid,
9125 const char *varname,
9126 const clim_photo_t *photo,
9127 double var[CP][CSZA][CO3]) {
9128
9129 /* Allocate... */
9130 double *help;
9131 ALLOC(help, double,
9132 photo->np * photo->nsza * photo->no3c);
9133
9134 /* Read variable... */
9135 int varid;
9136 NC_GET_DOUBLE(varname, help, 1);
9137
9138 /* Copy data... */
9139 for (int ip = 0; ip < photo->np; ip++)
9140 for (int is = 0; is < photo->nsza; is++)
9141 for (int io = 0; io < photo->no3c; io++)
9142 var[ip][is][io] =
9143 help[ARRAY_3D(ip, is, photo->nsza, io, photo->no3c)];
9144
9145 /* Free... */
9146 free(help);
9147}
9148
9149/*****************************************************************************/
9150
9152 const char *filename,
9153 clim_ts_t *ts) {
9154
9155 /* Write info... */
9156 LOG(1, "Read climatological time series: %s", filename);
9157
9158 /* Open file... */
9159 FILE *in;
9160 if (!(in = fopen(filename, "r"))) {
9161 WARN("Cannot open file!");
9162 return 0;
9163 }
9164
9165 /* Read data... */
9166 char line[LEN];
9167 int nh = 0;
9168 while (fgets(line, LEN, in))
9169 if (sscanf(line, "%lg %lg", &ts->time[nh], &ts->vmr[nh]) == 2) {
9170
9171 /* Convert years to seconds... */
9172 ts->time[nh] = (ts->time[nh] - 2000.0) * 365.25 * 86400.;
9173
9174 /* Check data... */
9175 if (nh > 0 && ts->time[nh] <= ts->time[nh - 1])
9176 ERRMSG("Time series must be ascending!");
9177
9178 /* Count time steps... */
9179 if ((++nh) >= CTS)
9180 ERRMSG("Too many data points!");
9181 }
9182
9183 /* Close file... */
9184 fclose(in);
9185
9186 /* Check number of data points... */
9187 ts->ntime = nh;
9188 if (nh < 2)
9189 ERRMSG("Not enough data points!");
9190
9191 /* Write info... */
9192 LOG(2, "Number of time steps: %d", ts->ntime);
9193 LOG(2, "Time steps: %.2f, %.2f ... %.2f s", ts->time[0], ts->time[1],
9194 ts->time[nh - 1]);
9195 LOG(2, "Volume mixing ratio range: %g ... %g ppv",
9196 gsl_stats_min(ts->vmr, 1, (size_t) nh), gsl_stats_max(ts->vmr, 1,
9197 (size_t) nh));
9198
9199 /* Exit success... */
9200 return 1;
9201}
9202
9203/*****************************************************************************/
9204
9206 const char *filename,
9207 const char *varname,
9208 clim_zm_t *zm) {
9209
9210 int ncid, varid, it, iy, iz, iz2, nt;
9211
9212 double *help, varmin = 1e99, varmax = -1e99;
9213
9214 /* Write info... */
9215 LOG(1, "Read %s data: %s", varname, filename);
9216
9217 /* Open netCDF file... */
9218 if (nc_open(filename, NC_NOWRITE, &ncid) != NC_NOERR) {
9219 WARN("%s climatology data are missing!", varname);
9220 return;
9221 }
9222
9223 /* Read pressure data... */
9224 NC_INQ_DIM("press", &zm->np, 2, CP, 1);
9225 NC_GET_DOUBLE("press", zm->p, 1);
9226 if (zm->p[0] < zm->p[1])
9227 ERRMSG("Pressure data are not descending!");
9228
9229 /* Read latitudes... */
9230 NC_INQ_DIM("lat", &zm->nlat, 2, CY, 1);
9231 NC_GET_DOUBLE("lat", zm->lat, 1);
9232 if (zm->lat[0] > zm->lat[1])
9233 ERRMSG("Latitude data are not ascending!");
9234
9235 /* Set time data (for monthly means)... */
9236 zm->ntime = 12;
9237 zm->time[0] = 1209600.00;
9238 zm->time[1] = 3888000.00;
9239 zm->time[2] = 6393600.00;
9240 zm->time[3] = 9072000.00;
9241 zm->time[4] = 11664000.00;
9242 zm->time[5] = 14342400.00;
9243 zm->time[6] = 16934400.00;
9244 zm->time[7] = 19612800.00;
9245 zm->time[8] = 22291200.00;
9246 zm->time[9] = 24883200.00;
9247 zm->time[10] = 27561600.00;
9248 zm->time[11] = 30153600.00;
9249
9250 /* Check number of timesteps... */
9251 NC_INQ_DIM("time", &nt, 12, 12, 1);
9252
9253 /* Read data... */
9254 ALLOC(help, double,
9255 zm->nlat * zm->np * zm->ntime);
9256 NC_GET_DOUBLE(varname, help, 1);
9257 for (it = 0; it < zm->ntime; it++)
9258 for (iz = 0; iz < zm->np; iz++)
9259 for (iy = 0; iy < zm->nlat; iy++)
9260 zm->vmr[it][iz][iy] = help[ARRAY_3D(it, iz, zm->np, iy, zm->nlat)];
9261 free(help);
9262
9263 /* Fix data gaps... */
9264 for (it = 0; it < zm->ntime; it++)
9265 for (iy = 0; iy < zm->nlat; iy++)
9266 for (iz = 0; iz < zm->np; iz++) {
9267 if (zm->vmr[it][iz][iy] < 0) {
9268 for (iz2 = 0; iz2 < zm->np; iz2++)
9269 if (zm->vmr[it][iz2][iy] >= 0) {
9270 zm->vmr[it][iz][iy] = zm->vmr[it][iz2][iy];
9271 break;
9272 }
9273 for (iz2 = zm->np - 1; iz2 >= 0; iz2--)
9274 if (zm->vmr[it][iz2][iy] >= 0) {
9275 zm->vmr[it][iz][iy] = zm->vmr[it][iz2][iy];
9276 break;
9277 }
9278 }
9279 varmin = MIN(varmin, zm->vmr[it][iz][iy]);
9280 varmax = MAX(varmax, zm->vmr[it][iz][iy]);
9281 }
9282
9283 /* Close netCDF file... */
9284 NC(nc_close(ncid));
9285
9286 /* Write info... */
9287 LOG(2, "Number of time steps: %d", zm->ntime);
9288 LOG(2, "Time steps: %.2f, %.2f ... %.2f s",
9289 zm->time[0], zm->time[1], zm->time[zm->ntime - 1]);
9290 LOG(2, "Number of pressure levels: %d", zm->np);
9291 LOG(2, "Altitude levels: %g, %g ... %g km",
9292 Z(zm->p[0]), Z(zm->p[1]), Z(zm->p[zm->np - 1]));
9293 LOG(2, "Pressure levels: %g, %g ... %g hPa", zm->p[0],
9294 zm->p[1], zm->p[zm->np - 1]);
9295 LOG(2, "Number of latitudes: %d", zm->nlat);
9296 LOG(2, "Latitudes: %g, %g ... %g deg",
9297 zm->lat[0], zm->lat[1], zm->lat[zm->nlat - 1]);
9298 LOG(2, "%s volume mixing ratio range: %g ... %g ppv", varname, varmin,
9299 varmax);
9300}
9301
9302/*****************************************************************************/
9303
9305 const char *filename,
9306 double kz[EP],
9307 double kw[EP],
9308 int *nk) {
9309
9310 /* Write info... */
9311 LOG(1, "Read kernel function: %s", filename);
9312
9313 /* Open file... */
9314 FILE *in;
9315 if (!(in = fopen(filename, "r")))
9316 ERRMSG("Cannot open file!");
9317
9318 /* Read data... */
9319 char line[LEN];
9320 int n = 0;
9321 while (fgets(line, LEN, in))
9322 if (sscanf(line, "%lg %lg", &kz[n], &kw[n]) == 2) {
9323 if (n > 0 && kz[n] < kz[n - 1])
9324 ERRMSG("Height levels must be ascending!");
9325 if ((++n) >= EP)
9326 ERRMSG("Too many height levels!");
9327 }
9328
9329 /* Close file... */
9330 fclose(in);
9331
9332 /* Check number of data points... */
9333 *nk = n;
9334 if (n < 2)
9335 ERRMSG("Not enough height levels!");
9336
9337 /* Normalize kernel function... */
9338 const double kmax = gsl_stats_max(kw, 1, (size_t) n);
9339 for (int iz = 0; iz < n; iz++)
9340 kw[iz] /= kmax;
9341}
9342
9343/*****************************************************************************/
9344
9346 const char *filename,
9347 const ctl_t *ctl,
9348 met_t *met) {
9349
9350 FILE *in;
9351
9352 double r;
9353
9354 int year, mon, day, hour, min, sec;
9355
9356 /* Set timer... */
9357 SELECT_TIMER("READ_MET_BIN", "INPUT");
9358
9359 /* Open file... */
9360 if (!(in = fopen(filename, "r"))) {
9361 WARN("Cannot open file!");
9362 return 0;
9363 }
9364
9365 /* Check type of binary data... */
9366 int met_type;
9367 FREAD(&met_type, int,
9368 1,
9369 in);
9370 if (met_type != ctl->met_type)
9371 ERRMSG("Wrong MET_TYPE of binary data!");
9372
9373 /* Check version of binary data... */
9374 int version;
9375 FREAD(&version, int,
9376 1,
9377 in);
9378 if (version != 104)
9379 ERRMSG("Wrong version of binary data!");
9380
9381 /* Read time... */
9382 FREAD(&met->time, double,
9383 1,
9384 in);
9385 jsec2time(met->time, &year, &mon, &day, &hour, &min, &sec, &r);
9386 LOG(2, "Time: %.2f (%d-%02d-%02d, %02d:%02d UTC)",
9387 met->time, year, mon, day, hour, min);
9388 if (year < 1900 || year > 2100 || mon < 1 || mon > 12
9389 || day < 1 || day > 31 || hour < 0 || hour > 23)
9390 ERRMSG("Error while reading time!");
9391
9392 /* Read dimensions... */
9393 met->coord_type = ctl->met_coord_type;
9394
9395 FREAD(&met->nx, int,
9396 1,
9397 in);
9398 LOG(2, "Number of %s: %d",
9399 (met->coord_type == 0) ? "longitudes" : "x coordinates", met->nx);
9400 if (met->nx < 2 || met->nx > EX)
9401 ERRMSG(met->coord_type == 0
9402 ? "Number of longitudes out of range!"
9403 : "Number of x coordinates out of range!");
9404
9405 FREAD(&met->ny, int,
9406 1,
9407 in);
9408 LOG(2, "Number of %s: %d",
9409 (met->coord_type == 0) ? "latitudes" : "y coordinates", met->ny);
9410 if (met->ny < 2 || met->ny > EY)
9411 ERRMSG(met->coord_type == 0
9412 ? "Number of latitudes out of range!"
9413 : "Number of y coordinates out of range!");
9414
9415 FREAD(&met->np, int,
9416 1,
9417 in);
9418 LOG(2, "Number of levels: %d", met->np);
9419 if (met->np < 2 || met->np > EP)
9420 ERRMSG("Number of levels out of range!");
9421
9422 /* Read grid... */
9423 FREAD(met->lon, double,
9424 (size_t) met->nx,
9425 in);
9426 LOG(2, "%s: %g, %g ... %g %s",
9427 met->coord_type == 0 ? "Longitudes" : "X coordinates",
9428 met->lon[0], met->lon[1], met->lon[met->nx - 1],
9429 met->coord_type == 0 ? "deg" : "m");
9430
9431 FREAD(met->lat, double,
9432 (size_t) met->ny,
9433 in);
9434 LOG(2, "%s: %g, %g ... %g %s",
9435 met->coord_type == 0 ? "Latitudes" : "Y coordinates",
9436 met->lat[0], met->lat[1], met->lat[met->ny - 1],
9437 met->coord_type == 0 ? "deg" : "m");
9438
9439 FREAD(met->p, double,
9440 (size_t) met->np,
9441 in);
9442 LOG(2, "Altitude levels: %g, %g ... %g km",
9443 Z(met->p[0]), Z(met->p[1]), Z(met->p[met->np - 1]));
9444 LOG(2, "Pressure levels: %g, %g ... %g hPa",
9445 met->p[0], met->p[1], met->p[met->np - 1]);
9446
9447 /* Read surface data... */
9448 read_met_bin_2d(in, met, met->ps, "PS");
9449 read_met_bin_2d(in, met, met->ts, "TS");
9450 read_met_bin_2d(in, met, met->zs, "ZS");
9451 read_met_bin_2d(in, met, met->us, "US");
9452 read_met_bin_2d(in, met, met->vs, "VS");
9453 read_met_bin_2d(in, met, met->ess, "ESS");
9454 read_met_bin_2d(in, met, met->nss, "NSS");
9455 read_met_bin_2d(in, met, met->shf, "SHF");
9456 read_met_bin_2d(in, met, met->lsm, "LSM");
9457 read_met_bin_2d(in, met, met->sst, "SST");
9458 read_met_bin_2d(in, met, met->pbl, "PBL");
9459 read_met_bin_2d(in, met, met->pt, "PT");
9460 read_met_bin_2d(in, met, met->tt, "TT");
9461 read_met_bin_2d(in, met, met->zt, "ZT");
9462 read_met_bin_2d(in, met, met->h2ot, "H2OT");
9463 read_met_bin_2d(in, met, met->pct, "PCT");
9464 read_met_bin_2d(in, met, met->pcb, "PCB");
9465 read_met_bin_2d(in, met, met->cl, "CL");
9466 read_met_bin_2d(in, met, met->plcl, "PLCL");
9467 read_met_bin_2d(in, met, met->plfc, "PLFC");
9468 read_met_bin_2d(in, met, met->pel, "PEL");
9469 read_met_bin_2d(in, met, met->cape, "CAPE");
9470 read_met_bin_2d(in, met, met->cin, "CIN");
9471 read_met_bin_2d(in, met, met->o3c, "O3C");
9472
9473 /* Read level data... */
9474 read_met_bin_3d(in, ctl, met, met->z, "Z", -1e34f, 1e34f);
9475 read_met_bin_3d(in, ctl, met, met->t, "T", 0, 1e34f);
9476 read_met_bin_3d(in, ctl, met, met->u, "U", -1e34f, 1e34f);
9477 read_met_bin_3d(in, ctl, met, met->v, "V", -1e34f, 1e34f);
9478 read_met_bin_3d(in, ctl, met, met->w, "W", -1e34f, 1e34f);
9479 read_met_bin_3d(in, ctl, met, met->pv, "PV", -1e34f, 1e34f);
9480 read_met_bin_3d(in, ctl, met, met->h2o, "H2O", 0, 1e34f);
9481 read_met_bin_3d(in, ctl, met, met->o3, "O3", 0, 1e34f);
9482 read_met_bin_3d(in, ctl, met, met->lwc, "LWC", 0, 1e34f);
9483 read_met_bin_3d(in, ctl, met, met->rwc, "RWC", 0, 1e34f);
9484 read_met_bin_3d(in, ctl, met, met->iwc, "IWC", 0, 1e34f);
9485 read_met_bin_3d(in, ctl, met, met->swc, "SWC", 0, 1e34f);
9486 read_met_bin_3d(in, ctl, met, met->cc, "CC", 0, 1);
9487
9488 /* Read final flag... */
9489 int final;
9490 FREAD(&final, int,
9491 1,
9492 in);
9493 if (final != 999)
9494 ERRMSG("Error while reading binary data!");
9495
9496 /* Close file... */
9497 fclose(in);
9498
9499 /* Return success... */
9500 return 1;
9501}
9502
9503/*****************************************************************************/
9504
9506 FILE *in,
9507 const met_t *met,
9508 float var[EX][EY],
9509 const char *varname) {
9510
9511 float *help;
9512
9513 /* Allocate... */
9514 ALLOC(help, float,
9515 EX * EY);
9516
9517 /* Read uncompressed... */
9518 LOG(2, "Read 2-D variable: %s (uncompressed)", varname);
9519 FREAD(help, float,
9520 (size_t) (met->nx * met->ny),
9521 in);
9522
9523 /* Copy data... */
9524 for (int ix = 0; ix < met->nx; ix++)
9525 for (int iy = 0; iy < met->ny; iy++)
9526 var[ix][iy] = help[ARRAY_2D(ix, iy, met->ny)];
9527
9528 /* Free... */
9529 free(help);
9530}
9531
9532/*****************************************************************************/
9533
9535 FILE *in,
9536 const ctl_t *ctl,
9537 const met_t *met,
9538 float var[EX][EY][EP],
9539 const char *varname,
9540 const float bound_min,
9541 const float bound_max) {
9542
9543 float *help;
9544
9545 /* Allocate... */
9546 ALLOC(help, float,
9547 EX * EY * EP);
9548
9549 /* Read uncompressed data... */
9550 if (ctl->met_type == 1) {
9551 LOG(2, "Read 3-D variable: %s (uncompressed)", varname);
9552 FREAD(help, float,
9553 (size_t) (met->nx * met->ny * met->np),
9554 in);
9555 }
9556
9557 /* Read packed data... */
9558 else if (ctl->met_type == 2)
9559 compress_pck(ctl, met, varname, help, 1, NULL, in);
9560
9561 /* Read ZFP data... */
9562 else if (ctl->met_type == 3) {
9563#ifdef ZFP
9564 int precision;
9565 FREAD(&precision, int,
9566 1,
9567 in);
9568
9569 double tolerance;
9570 FREAD(&tolerance, double,
9571 1,
9572 in);
9573
9574 compress_zfp(ctl, met, varname, help, 1, NULL, in);
9575#else
9576 ERRMSG("MPTRAC was compiled without ZFP compression!");
9577#endif
9578 }
9579
9580 /* Read zstd data... */
9581 else if (ctl->met_type == 4) {
9582#ifdef ZSTD
9583 compress_zstd(ctl, met, varname, help, 1, NULL, in);
9584#else
9585 ERRMSG("MPTRAC was compiled without ZSTD compression!");
9586#endif
9587 }
9588
9589 /* Read LZ4 data... */
9590 else if (ctl->met_type == 8) {
9591#ifdef LZ4
9592 compress_lz4(ctl, met, varname, help, 1, NULL, in);
9593#else
9594 ERRMSG("MPTRAC was compiled without LZ4 compression!");
9595#endif
9596 }
9597
9598 /* Read cmultiscale data... */
9599 else if (ctl->met_type == 5) {
9600#ifdef CMS
9601 compress_cms(ctl, met, varname, help, 1, NULL, in);
9602#else
9603 ERRMSG("MPTRAC was compiled without cmultiscale compression!");
9604#endif
9605 }
9606
9607 /* Read SZ3 data... */
9608 else if (ctl->met_type == 7) {
9609#ifdef SZ3
9610 int precision;
9611 FREAD(&precision, int,
9612 1,
9613 in);
9614
9615 double tolerance;
9616 FREAD(&tolerance, double,
9617 1,
9618 in);
9619
9620 compress_sz3(ctl, met, varname, help, 1, NULL, in);
9621#else
9622 ERRMSG("MPTRAC was compiled without sz3 compression!");
9623#endif
9624 }
9625
9626 /* Copy data... */
9627#pragma omp parallel for default(shared) collapse(2)
9628 for (int ix = 0; ix < met->nx; ix++)
9629 for (int iy = 0; iy < met->ny; iy++)
9630 for (int ip = 0; ip < met->np; ip++) {
9631 var[ix][iy][ip] = help[ARRAY_3D(ix, iy, met->ny, ip, met->np)];
9632 if (var[ix][iy][ip] < bound_min)
9633 var[ix][iy][ip] = bound_min;
9634 else if (var[ix][iy][ip] > bound_max)
9635 var[ix][iy][ip] = bound_max;
9636 }
9637
9638 /* Free... */
9639 free(help);
9640}
9641
9642/*****************************************************************************/
9643
9645 const ctl_t *ctl,
9646 const clim_t *clim,
9647 met_t *met) {
9648
9649 /* Check parameters... */
9650 if (ctl->met_cape != 1)
9651 return;
9652
9653 if (ctl->met_coord_type != 0)
9654 ERRMSG("Only lat/lon grid supported");
9655
9656 /* Set timer... */
9657 SELECT_TIMER("READ_MET_CAPE", "METPROC");
9658 LOG(2, "Calculate CAPE...");
9659
9660 /* Vertical spacing (about 100 m)... */
9661 const double pfac = 1.01439, dz0 = RI / MA / G0 * log(pfac);
9662
9663 /* Loop over columns... */
9664#pragma omp parallel for default(shared) collapse(2)
9665 for (int ix = 0; ix < met->nx; ix++)
9666 for (int iy = 0; iy < met->ny; iy++) {
9667
9668 /* Get potential temperature and water vapor at lowest 50 hPa... */
9669 int n = 0;
9670 double h2o = 0, t, theta = 0;
9671 double pbot = MIN(met->ps[ix][iy], met->p[0]);
9672 double ptop = pbot - 50.;
9673 for (int ip = 0; ip < met->np; ip++) {
9674 if (met->p[ip] <= pbot) {
9675 theta += THETA(met->p[ip], met->t[ix][iy][ip]);
9676 h2o += met->h2o[ix][iy][ip];
9677 n++;
9678 }
9679 if (met->p[ip] < ptop && n > 0)
9680 break;
9681 }
9682 theta /= n;
9683 h2o /= n;
9684
9685 /* Cannot compute anything if water vapor is missing... */
9686 met->plcl[ix][iy] = NAN;
9687 met->plfc[ix][iy] = NAN;
9688 met->pel[ix][iy] = NAN;
9689 met->cape[ix][iy] = NAN;
9690 met->cin[ix][iy] = NAN;
9691 if (h2o <= 0)
9692 continue;
9693
9694 /* Find lifted condensation level (LCL)... */
9695 ptop = P(20.);
9696 pbot = met->ps[ix][iy];
9697 do {
9698 met->plcl[ix][iy] = (float) (0.5 * (pbot + ptop));
9699 t = theta / pow(1000. / met->plcl[ix][iy], KAPPA);
9700 if (RH(met->plcl[ix][iy], t, h2o) > 100.)
9701 ptop = met->plcl[ix][iy];
9702 else
9703 pbot = met->plcl[ix][iy];
9704 } while (pbot - ptop > 0.1);
9705
9706 /* Calculate CIN up to LCL... */
9708 double dcape, dz, h2o_env, t_env;
9709 double p = met->ps[ix][iy];
9710 met->cape[ix][iy] = met->cin[ix][iy] = 0;
9711 do {
9712 dz = dz0 * TVIRT(t, h2o);
9713 p /= pfac;
9714 t = theta / pow(1000. / p, KAPPA);
9715 intpol_met_space_3d(met, met->t, p, met->lon[ix], met->lat[iy],
9716 &t_env, ci, cw, 1);
9717 intpol_met_space_3d(met, met->h2o, p, met->lon[ix], met->lat[iy],
9718 &h2o_env, ci, cw, 0);
9719 dcape = 1e3 * G0 * (TVIRT(t, h2o) - TVIRT(t_env, h2o_env)) /
9720 TVIRT(t_env, h2o_env) * dz;
9721 if (dcape < 0)
9722 met->cin[ix][iy] += fabsf((float) dcape);
9723 } while (p > met->plcl[ix][iy]);
9724
9725 /* Calculate level of free convection (LFC), equilibrium level (EL),
9726 and convective available potential energy (CAPE)... */
9727 dcape = 0;
9728 p = met->plcl[ix][iy];
9729 t = theta / pow(1000. / p, KAPPA);
9730 ptop = 0.75 * clim_tropo(clim, met->time,
9731 ctl->met_coord_type ==
9732 0 ? met->lat[iy] : ctl->met_utm_ref_lat);
9733 do {
9734 dz = dz0 * TVIRT(t, h2o);
9735 p /= pfac;
9736 t -= lapse_rate(t, h2o) * dz;
9737 double psat = PSAT(t);
9738 h2o = psat / (p - (1. - EPS) * psat);
9739 intpol_met_space_3d(met, met->t, p, met->lon[ix], met->lat[iy],
9740 &t_env, ci, cw, 1);
9741 intpol_met_space_3d(met, met->h2o, p, met->lon[ix], met->lat[iy],
9742 &h2o_env, ci, cw, 0);
9743 double dcape_old = dcape;
9744 dcape = 1e3 * G0 * (TVIRT(t, h2o) - TVIRT(t_env, h2o_env)) /
9745 TVIRT(t_env, h2o_env) * dz;
9746 if (dcape > 0) {
9747 met->cape[ix][iy] += (float) dcape;
9748 if (!isfinite(met->plfc[ix][iy]))
9749 met->plfc[ix][iy] = (float) p;
9750 } else if (dcape_old > 0)
9751 met->pel[ix][iy] = (float) p;
9752 if (dcape < 0 && !isfinite(met->plfc[ix][iy]))
9753 met->cin[ix][iy] += fabsf((float) dcape);
9754 } while (p > ptop);
9755
9756 /* Check results... */
9757 if (!isfinite(met->plfc[ix][iy]))
9758 met->cin[ix][iy] = NAN;
9759 }
9760}
9761
9762/*****************************************************************************/
9763
9765 met_t *met) {
9766
9767 /* Set timer... */
9768 SELECT_TIMER("READ_MET_CLOUD", "METPROC");
9769 LOG(2, "Calculate cloud data...");
9770
9771 /* Thresholds for cloud detection... */
9772 const double ccmin = 0.01, cwmin = 1e-6;
9773
9774 /* Loop over columns... */
9775#pragma omp parallel for default(shared) collapse(2)
9776 for (int ix = 0; ix < met->nx; ix++)
9777 for (int iy = 0; iy < met->ny; iy++) {
9778
9779 /* Init... */
9780 met->pct[ix][iy] = NAN;
9781 met->pcb[ix][iy] = NAN;
9782 met->cl[ix][iy] = 0;
9783
9784 /* Loop over pressure levels... */
9785 for (int ip = 0; ip < met->np - 1; ip++) {
9786
9787 /* Check pressure... */
9788 if (met->p[ip] > met->ps[ix][iy] || met->p[ip] < P(20.))
9789 continue;
9790
9791 /* Check ice water and liquid water content... */
9792 if (met->cc[ix][iy][ip] > ccmin
9793 && (met->lwc[ix][iy][ip] > cwmin
9794 || met->rwc[ix][iy][ip] > cwmin
9795 || met->iwc[ix][iy][ip] > cwmin
9796 || met->swc[ix][iy][ip] > cwmin)) {
9797
9798 /* Get cloud top pressure ... */
9799 met->pct[ix][iy]
9800 = (float) (0.5 * (met->p[ip] + (float) met->p[ip + 1]));
9801
9802 /* Get cloud bottom pressure ... */
9803 if (!isfinite(met->pcb[ix][iy]))
9804 met->pcb[ix][iy]
9805 = (float) (0.5 * (met->p[ip] + met->p[MAX(ip - 1, 0)]));
9806 }
9807
9808 /* Get cloud water... */
9809 met->cl[ix][iy] += (float)
9810 (0.5 * (met->lwc[ix][iy][ip] + met->lwc[ix][iy][ip + 1]
9811 + met->rwc[ix][iy][ip] + met->rwc[ix][iy][ip + 1]
9812 + met->iwc[ix][iy][ip] + met->iwc[ix][iy][ip + 1]
9813 + met->swc[ix][iy][ip] + met->swc[ix][iy][ip + 1])
9814 * 100. * (met->p[ip] - met->p[ip + 1]) / G0);
9815 }
9816 }
9817}
9818
9819/*****************************************************************************/
9820
9822 const ctl_t *ctl,
9823 met_t *met) {
9824
9825 met_t *help;
9826
9827 /* Check parameters... */
9828 if (ctl->met_detrend <= 0)
9829 return;
9830
9831 if (ctl->met_coord_type != 0)
9832 ERRMSG("Only lat/lon grid supported");
9833
9834 /* Set timer... */
9835 SELECT_TIMER("READ_MET_DETREND", "METPROC");
9836 LOG(2, "Detrend meteo data...");
9837
9838 /* Allocate... */
9839 ALLOC(help, met_t, 1);
9840
9841 /* Calculate standard deviation... */
9842 const double sigma = ctl->met_detrend / 2.355;
9843 const double tssq = 2. * SQR(sigma);
9844
9845 /* Calculate box size in latitude... */
9846 int sy = (int) (3. * DY2DEG(sigma) / fabs(met->lat[1] - met->lat[0]));
9847 sy = MIN(MAX(1, sy), met->ny / 2);
9848
9849 /* Calculate background... */
9850#pragma omp parallel for default(shared) collapse(2)
9851 for (int ix = 0; ix < met->nx; ix++) {
9852 for (int iy = 0; iy < met->ny; iy++) {
9853
9854 /* Calculate Cartesian coordinates... */
9855 double x0[3];
9856 geo2cart(0.0, met->lon[ix], met->lat[iy], x0);
9857
9858 /* Calculate box size in longitude... */
9859 int sx =
9860 (int) (3. * DX2DEG(sigma, met->lat[iy]) /
9861 fabs(met->lon[1] - met->lon[0]));
9862 sx = MIN(MAX(1, sx), met->nx / 2);
9863
9864 /* Init... */
9865 float wsum = 0;
9866 for (int ip = 0; ip < met->np; ip++) {
9867 help->t[ix][iy][ip] = 0;
9868 help->u[ix][iy][ip] = 0;
9869 help->v[ix][iy][ip] = 0;
9870 help->w[ix][iy][ip] = 0;
9871 }
9872
9873 /* Loop over neighboring grid points... */
9874 for (int ix2 = ix - sx; ix2 <= ix + sx; ix2++) {
9875 int ix3 = ix2;
9876 if (ix3 < 0)
9877 ix3 += met->nx;
9878 else if (ix3 >= met->nx)
9879 ix3 -= met->nx;
9880 for (int iy2 = MAX(iy - sy, 0);
9881 iy2 <= MIN(iy + sy, met->ny - 1); iy2++) {
9882
9883 /* Calculate Cartesian coordinates... */
9884 double x1[3];
9885 geo2cart(0.0, met->lon[ix3], met->lat[iy2], x1);
9886
9887 /* Calculate weighting factor... */
9888 const float w = (float) exp(-DIST2(x0, x1) / tssq);
9889
9890 /* Add data... */
9891 wsum += w;
9892 for (int ip = 0; ip < met->np; ip++) {
9893 help->t[ix][iy][ip] += w * met->t[ix3][iy2][ip];
9894 help->u[ix][iy][ip] += w * met->u[ix3][iy2][ip];
9895 help->v[ix][iy][ip] += w * met->v[ix3][iy2][ip];
9896 help->w[ix][iy][ip] += w * met->w[ix3][iy2][ip];
9897 }
9898 }
9899 }
9900
9901 /* Normalize... */
9902 for (int ip = 0; ip < met->np; ip++) {
9903 help->t[ix][iy][ip] /= wsum;
9904 help->u[ix][iy][ip] /= wsum;
9905 help->v[ix][iy][ip] /= wsum;
9906 help->w[ix][iy][ip] /= wsum;
9907 }
9908 }
9909 }
9910
9911 /* Subtract background... */
9912#pragma omp parallel for default(shared) collapse(3)
9913 for (int ix = 0; ix < met->nx; ix++)
9914 for (int iy = 0; iy < met->ny; iy++)
9915 for (int ip = 0; ip < met->np; ip++) {
9916 met->t[ix][iy][ip] -= help->t[ix][iy][ip];
9917 met->u[ix][iy][ip] -= help->u[ix][iy][ip];
9918 met->v[ix][iy][ip] -= help->v[ix][iy][ip];
9919 met->w[ix][iy][ip] -= help->w[ix][iy][ip];
9920 }
9921
9922 /* Free... */
9923 free(help);
9924}
9925
9926/*****************************************************************************/
9927
9929 met_t *met) {
9930
9931 /* Set timer... */
9932 SELECT_TIMER("READ_MET_EXTRAPOLATE", "METPROC");
9933 LOG(2, "Extrapolate meteo data...");
9934
9935 /* Loop over columns... */
9936#pragma omp parallel for default(shared) collapse(2)
9937 for (int ix = 0; ix < met->nx; ix++)
9938 for (int iy = 0; iy < met->ny; iy++) {
9939
9940 /* Find lowest valid data point... */
9941 int ip0;
9942 for (ip0 = met->np - 1; ip0 >= 0; ip0--)
9943 if (!isfinite(met->t[ix][iy][ip0])
9944 || !isfinite(met->u[ix][iy][ip0])
9945 || !isfinite(met->v[ix][iy][ip0])
9946 || !isfinite(met->w[ix][iy][ip0]))
9947 break;
9948
9949 /* Extrapolate... */
9950 for (int ip = ip0; ip >= 0; ip--) {
9951 met->t[ix][iy][ip] = met->t[ix][iy][ip + 1];
9952 met->u[ix][iy][ip] = met->u[ix][iy][ip + 1];
9953 met->v[ix][iy][ip] = met->v[ix][iy][ip + 1];
9954 met->w[ix][iy][ip] = met->w[ix][iy][ip + 1];
9955 met->h2o[ix][iy][ip] = met->h2o[ix][iy][ip + 1];
9956 met->o3[ix][iy][ip] = met->o3[ix][iy][ip + 1];
9957 met->lwc[ix][iy][ip] = met->lwc[ix][iy][ip + 1];
9958 met->rwc[ix][iy][ip] = met->rwc[ix][iy][ip + 1];
9959 met->iwc[ix][iy][ip] = met->iwc[ix][iy][ip + 1];
9960 met->swc[ix][iy][ip] = met->swc[ix][iy][ip + 1];
9961 met->cc[ix][iy][ip] = met->cc[ix][iy][ip + 1];
9962 }
9963 }
9964}
9965
9966/*****************************************************************************/
9967
9969 const ctl_t *ctl,
9970 met_t *met) {
9971
9972 float *help;
9973
9974 double logp[EP];
9975
9976 int dx = ctl->met_geopot_sx, dy = ctl->met_geopot_sy;
9977
9978 /* Set timer... */
9979 SELECT_TIMER("READ_MET_GEOPOT", "METPROC");
9980 LOG(2, "Calculate geopotential heights...");
9981
9982 /* Allocate... */
9983 ALLOC(help, float,
9984 EX * EY * EP);
9985
9986 /* Calculate log pressure... */
9987#pragma omp parallel for default(shared)
9988 for (int ip = 0; ip < met->np; ip++)
9989 logp[ip] = log(met->p[ip]);
9990
9991 /* Apply hydrostatic equation to calculate geopotential heights... */
9992#pragma omp parallel for default(shared) collapse(2)
9993 for (int ix = 0; ix < met->nx; ix++)
9994 for (int iy = 0; iy < met->ny; iy++) {
9995
9996 /* Get surface height and pressure... */
9997 const double zs = met->zs[ix][iy];
9998 const double lnps = log(met->ps[ix][iy]);
9999
10000 /* Get temperature and water vapor at the surface... */
10001 const int ip0 = locate_irr(met->p, met->np, met->ps[ix][iy]);
10002 const double ts = LIN(met->p[ip0], met->t[ix][iy][ip0], met->p[ip0 + 1],
10003 met->t[ix][iy][ip0 + 1], met->ps[ix][iy]);
10004 const double h2os =
10005 LIN(met->p[ip0], met->h2o[ix][iy][ip0], met->p[ip0 + 1],
10006 met->h2o[ix][iy][ip0 + 1], met->ps[ix][iy]);
10007
10008 /* Upper part of profile... */
10009 met->z[ix][iy][ip0 + 1]
10010 = (float) (zs +
10011 ZDIFF(lnps, ts, h2os, logp[ip0 + 1],
10012 met->t[ix][iy][ip0 + 1], met->h2o[ix][iy][ip0 + 1]));
10013 for (int ip = ip0 + 2; ip < met->np; ip++)
10014 met->z[ix][iy][ip]
10015 = (float) (met->z[ix][iy][ip - 1] +
10016 ZDIFF(logp[ip - 1], met->t[ix][iy][ip - 1],
10017 met->h2o[ix][iy][ip - 1], logp[ip],
10018 met->t[ix][iy][ip], met->h2o[ix][iy][ip]));
10019
10020 /* Lower part of profile... */
10021 met->z[ix][iy][ip0]
10022 = (float) (zs +
10023 ZDIFF(lnps, ts, h2os, logp[ip0],
10024 met->t[ix][iy][ip0], met->h2o[ix][iy][ip0]));
10025 for (int ip = ip0 - 1; ip >= 0; ip--)
10026 met->z[ix][iy][ip]
10027 = (float) (met->z[ix][iy][ip + 1] +
10028 ZDIFF(logp[ip + 1], met->t[ix][iy][ip + 1],
10029 met->h2o[ix][iy][ip + 1], logp[ip],
10030 met->t[ix][iy][ip], met->h2o[ix][iy][ip]));
10031 }
10032
10033 /* Check control parameters... */
10034 if (dx == 0 || dy == 0)
10035 return;
10036
10037 /* Default smoothing parameters... */
10038 if (dx < 0 || dy < 0) {
10039 if (fabs(met->lon[1] - met->lon[0]) < 0.5) {
10040 dx = 3;
10041 dy = 2;
10042 } else {
10043 dx = 6;
10044 dy = 4;
10045 }
10046 }
10047
10048 /* Calculate weights for smoothing... */
10049 float ws[dx + 1][dy + 1];
10050#pragma omp parallel for default(shared) collapse(2)
10051 for (int ix = 0; ix <= dx; ix++)
10052 for (int iy = 0; iy < dy; iy++)
10053 ws[ix][iy] = (1.0f - (float) ix / (float) dx)
10054 * (1.0f - (float) iy / (float) dy);
10055
10056 /* Copy data... */
10057#pragma omp parallel for default(shared) collapse(3)
10058 for (int ix = 0; ix < met->nx; ix++)
10059 for (int iy = 0; iy < met->ny; iy++)
10060 for (int ip = 0; ip < met->np; ip++)
10061 help[ARRAY_3D(ip, ix, met->nx, iy, met->ny)] = met->z[ix][iy][ip];
10062
10063 /* Horizontal smoothing... */
10064#pragma omp parallel for default(shared) collapse(3)
10065 for (int ip = 0; ip < met->np; ip++)
10066 for (int ix = 0; ix < met->nx; ix++)
10067 for (int iy = 0; iy < met->ny; iy++) {
10068 float res = 0, wsum = 0;
10069 int iy0 = MAX(iy - dy + 1, 0);
10070 int iy1 = MIN(iy + dy - 1, met->ny - 1);
10071 for (int ix2 = ix - dx + 1; ix2 <= ix + dx - 1; ++ix2) {
10072 int ix3 = ix2;
10073 if (ix3 < 0)
10074 ix3 += met->nx;
10075 else if (ix3 >= met->nx)
10076 ix3 -= met->nx;
10077 for (int iy2 = iy0; iy2 <= iy1; ++iy2)
10078 if (isfinite(help[ARRAY_3D(ip, ix3, met->nx, iy2, met->ny)])) {
10079 float w = ws[abs(ix - ix2)][abs(iy - iy2)];
10080 res += w * help[ARRAY_3D(ip, ix3, met->nx, iy2, met->ny)];
10081 wsum += w;
10082 }
10083 }
10084 if (wsum > 0)
10085 met->z[ix][iy][ip] = res / wsum;
10086 else
10087 met->z[ix][iy][ip] = NAN;
10088 }
10089
10090 /* Free... */
10091 free(help);
10092}
10093
10094/*****************************************************************************/
10095
10097 const char *filename,
10098 const int ncid,
10099 const ctl_t *ctl,
10100 met_t *met,
10101 dd_t *dd) {
10102
10103 char levname[LEN], tstr[10];
10104
10105 double rtime = 0, r, r2;
10106
10107 int varid, ndims, dimids[NC_MAX_DIMS], year2, mon2, day2, hour2, min2, sec2,
10108 year, mon, day, hour, min, sec;
10109
10110 size_t dimlen;
10111
10112 /* Set timer... */
10113 SELECT_TIMER("READ_MET_NC_GRID", "INPUT");
10114 LOG(2, "Read meteo grid information...");
10115
10116 /* MPTRAC meteo files... */
10117 if (!ctl->met_clams) {
10118
10119 /* Get time from filename... */
10120 met->time = time_from_filename(filename, 16, 0);
10121
10122 /* Check time information from data file... */
10123 jsec2time(met->time, &year, &mon, &day, &hour, &min, &sec, &r);
10124 if (nc_inq_varid(ncid, "time", &varid) == NC_NOERR) {
10125 NC(nc_get_var_double(ncid, varid, &rtime));
10126 if (fabs(year * 10000. + mon * 100. + day + hour / 24. - rtime) > 1.0)
10127 WARN("Time information in meteo file does not match filename!");
10128 } else
10129 WARN("Time information in meteo file is missing!");
10130 }
10131
10132 /* CLaMS meteo files... */
10133 else {
10134
10135 /* Read time from file... */
10136 NC_GET_DOUBLE("time", &rtime, 0);
10137
10138 /* Get time from filename (considering the century)... */
10139 if (rtime < 0)
10140 sprintf(tstr, "19%.2s", &filename[strlen(filename) - 11]);
10141 else
10142 sprintf(tstr, "20%.2s", &filename[strlen(filename) - 11]);
10143 year = atoi(tstr);
10144 sprintf(tstr, "%.2s", &filename[strlen(filename) - 9]);
10145 mon = atoi(tstr);
10146 sprintf(tstr, "%.2s", &filename[strlen(filename) - 7]);
10147 day = atoi(tstr);
10148 sprintf(tstr, "%.2s", &filename[strlen(filename) - 5]);
10149 hour = atoi(tstr);
10150 time2jsec(year, mon, day, hour, 0, 0, 0, &met->time);
10151 }
10152
10153 /* Check time... */
10154 if (year < 1900 || year > 2100 || mon < 1 || mon > 12
10155 || day < 1 || day > 31 || hour < 0 || hour > 23)
10156 ERRMSG("Cannot read time from filename!");
10157 jsec2time(met->time, &year2, &mon2, &day2, &hour2, &min2, &sec2, &r2);
10158 LOG(2, "Time: %.2f (%d-%02d-%02d, %02d:%02d UTC)",
10159 met->time, year2, mon2, day2, hour2, min2);
10160
10161 /* Get vertical dimension... */
10162 if (nc_inq_varid(ncid, "u", &varid) != NC_NOERR)
10163 if (nc_inq_varid(ncid, "U", &varid) != NC_NOERR)
10164 ERRMSG
10165 ("Variable 'u' or 'U' not found, cannot determine vertical dimension!");
10166
10167 NC(nc_inq_varndims(ncid, varid, &ndims));
10168 NC(nc_inq_vardimid(ncid, varid, dimids));
10169
10170 if (ndims == 4) {
10171 NC(nc_inq_dim
10172 (ncid, dimids[ctl->met_convention == 0 ? 1 : 3], levname, &dimlen));
10173 } else if (ndims == 3) {
10174 NC(nc_inq_dim
10175 (ncid, dimids[ctl->met_convention == 0 ? 0 : 2], levname, &dimlen));
10176 } else
10177 ERRMSG("Cannot determine vertical dimension!")
10178 met->np = (int) dimlen;
10179
10180 LOG(2, "Number of levels: %d", met->np);
10181 if (met->np < 2 || met->np > EP)
10182 ERRMSG("Number of levels out of range!");
10183
10184 if (!ctl->dd) {
10185
10186 /* Get grid dimensions and coordinates... */
10187 if (met->coord_type == 0) {
10188 /* Longitude/latitude grid... */
10189 NC_INQ_DIM("lon", &met->nx, 2, EX, 1);
10190 LOG(2, "Number of longitudes: %d", met->nx);
10191
10192 NC_INQ_DIM("lat", &met->ny, 2, EY, 1);
10193 LOG(2, "Number of latitudes: %d", met->ny);
10194
10195 NC_GET_DOUBLE("lon", met->lon, 1);
10196 LOG(2, "Longitudes: %g, %g ... %g deg",
10197 met->lon[0], met->lon[1], met->lon[met->nx - 1]);
10198 NC_GET_DOUBLE("lat", met->lat, 1);
10199 LOG(2, "Latitudes: %g, %g ... %g deg",
10200 met->lat[0], met->lat[1], met->lat[met->ny - 1]);
10201
10202 } else {
10203 /* UTM grid... */
10204 NC_INQ_DIM("x", &met->nx, 2, EX, 1);
10205 LOG(2, "Number of x coordinates: %d", met->nx);
10206
10207 NC_INQ_DIM("y", &met->ny, 2, EY, 1);
10208 LOG(2, "Number of y coordinates: %d", met->ny);
10209
10210 NC_GET_DOUBLE("x", met->lon, 1);
10211 LOG(2, "X coordinates: %g, %g ... %g m",
10212 met->lon[0], met->lon[1], met->lon[met->nx - 1]);
10213 NC_GET_DOUBLE("y", met->lat, 1);
10214 LOG(2, "Y coordinates: %g, %g ... %g m",
10215 met->lat[0], met->lat[1], met->lat[met->ny - 1]);
10216 }
10217
10218 } else {
10219
10220 if (met->coord_type != 0)
10221 ERRMSG("Domain decomposition is only supported for lat/lon grids!");
10222
10223 /* Use equidistant lat-lon domain decomposition... */
10224 dd_read_met_nc_grid(dd, ctl, met, ncid);
10225
10226 }
10227
10228 /* Read pressure levels... */
10229 if (ctl->met_np <= 0) {
10230 NC_GET_DOUBLE(levname, met->p, 1);
10231 for (int ip = 0; ip < met->np; ip++)
10232 met->p[ip] /= 100.;
10233 LOG(2, "Altitude levels: %g, %g ... %g km",
10234 Z(met->p[0]), Z(met->p[1]), Z(met->p[met->np - 1]));
10235 LOG(2, "Pressure levels: %g, %g ... %g hPa",
10236 met->p[0], met->p[1], met->p[met->np - 1]);
10237 }
10238
10239 /* Read hybrid levels... */
10240 if (strcasecmp(levname, "hybrid") == 0)
10241 NC_GET_DOUBLE("hybrid", met->hybrid, 1);
10242
10243 /* Read model level coefficients from file... */
10244 if (ctl->met_vert_coord == 2) {
10245 NC_GET_DOUBLE("hyam", met->hyam, 1);
10246 NC_GET_DOUBLE("hybm", met->hybm, 1);
10247 }
10248
10249 /* Copy model level coefficients from control parameters... */
10250 else if (ctl->met_vert_coord == 3 || ctl->met_vert_coord == 4) {
10251 if (ctl->met_nlev <= 0)
10252 ERRMSG("You need to specify MET_NLEV, MET_LEV_HYAM, and MET_LEV_HYBM!");
10253 for (int ip = 0; ip < ctl->met_nlev; ip++) {
10254 met->hyam[ip] = ctl->met_lev_hyam[ip];
10255 met->hybm[ip] = ctl->met_lev_hybm[ip];
10256 }
10257 }
10258
10259 /* Calculate eta levels... */
10260 for (int k = 0; k < MAX(met->np, ctl->met_nlev); ++k) {
10261 met->eta[k] = met->hyam[k] / (100.0 * P0) + met->hybm[k];
10262 if (ctl->met_vert_coord >= 2 && k > 0 && met->eta[k] <= met->eta[k - 1])
10263 ERRMSG("Eta levels must be ascending!");
10264 }
10265
10266 /* Check horizontal grid spacing... */
10267 for (int ix = 2; ix < met->nx; ix++)
10268 if (fabs
10269 (fabs(met->lon[ix] - met->lon[ix - 1]) -
10270 fabs(met->lon[1] - met->lon[0])) > 0.001)
10271 ERRMSG("No regular grid spacing in longitudes!");
10272 for (int iy = 2; iy < met->ny; iy++)
10273 if (fabs
10274 (fabs(met->lat[iy] - met->lat[iy - 1]) -
10275 fabs(met->lat[1] - met->lat[0])) > 0.001) {
10276 WARN("No regular grid spacing in latitudes!");
10277 break;
10278 }
10279}
10280
10281/*****************************************************************************/
10282
10284 const int ncid,
10285 const ctl_t *ctl,
10286 met_t *met,
10287 dd_t *dd) {
10288
10289 /* Set timer... */
10290 SELECT_TIMER("READ_MET_SURFACE", "INPUT");
10291 LOG(2, "Read surface data...");
10292
10293 /* Read surface pressure... */
10294 if (read_met_nc_2d
10295 (ncid, (const char *const[]) { "lnsp", "LNSP" }, 2, ctl, met, dd,
10296 met->ps,
10297 1.0f, 1)) {
10298 for (int ix = 0; ix < met->nx; ix++)
10299 for (int iy = 0; iy < met->ny; iy++)
10300 met->ps[ix][iy] = (float) (exp(met->ps[ix][iy]) / 100.);
10301 } else
10302 if (!read_met_nc_2d
10303 (ncid, (const char *const[]) { "ps", "PS", "sp", "SP" }, 4, ctl, met,
10304 dd, met->ps,
10305 0.01f, 1)) {
10306 WARN("Cannot not read surface pressure data (use lowest level)!");
10307 for (int ix = 0; ix < met->nx; ix++)
10308 for (int iy = 0; iy < met->ny; iy++)
10309 met->ps[ix][iy]
10310 = (ctl->met_np > 0 ? (float) ctl->met_p[0] : (float) met->p[0]);
10311 }
10312
10313 /* MPTRAC meteo data... */
10314 if (ctl->met_clams == 0) {
10315
10316 /* Read geopotential height at the surface... */
10317 if (!read_met_nc_2d
10318 (ncid, (const char *const[]) { "z", "Z" }, 2, ctl, met, dd, met->zs,
10319 (float) (1. / (1000. * G0)), 1))
10320 if (!read_met_nc_2d
10321 (ncid, (const char *const[]) { "zm", "ZM" }, 2, ctl, met, dd,
10322 met->zs,
10323 (ctl->met_gp2z ? (float) (1e-3 / G0) : (float) (1. / 1000.)), 1))
10324 WARN("Cannot read surface geopotential height!");
10325 }
10326
10327 /* CLaMS meteo data... */
10328 else {
10329
10330 /* Read geopotential height at the surface
10331 (use lowermost level of 3-D data field)... */
10332 float *help;
10333 ALLOC(help, float,
10334 EX * EY * EP);
10335 memcpy(help, met->pl, sizeof(met->pl));
10336 if (!read_met_nc_3d
10337 (ncid, (const char *const[]) { "gph", "GPH" }, 2, ctl, met, dd,
10338 met->pl,
10339 (float) (1e-3 / G0)))
10340 ERRMSG("Cannot read geopotential height!");
10341 /* read_met_nc_3d hands back the levels timer... */
10342 SELECT_TIMER("READ_MET_SURFACE", "INPUT");
10343 for (int ix = 0; ix < met->nx; ix++)
10344 for (int iy = 0; iy < met->ny; iy++)
10345 met->zs[ix][iy] = met->pl[ix][iy][0];
10346 memcpy(met->pl, help, sizeof(met->pl));
10347 free(help);
10348 }
10349
10350 /* Read temperature at the surface... */
10351 if (!read_met_nc_2d
10352 (ncid, (const char *const[]) { "t2m", "T2M", "2t", "2T", "t2", "T2" },
10353 6, ctl, met, dd, met->ts, 1.0,
10354 1))
10355 WARN("Cannot read surface temperature!");
10356
10357 /* Read zonal wind at the surface... */
10358 if (!read_met_nc_2d
10359 (ncid, (const char *const[]) { "u10m", "U10M", "10u", "10U", "u10",
10360 "U10"
10361 }, 6, ctl, met, dd,
10362 met->us, 1.0, 1))
10363 WARN("Cannot read surface zonal wind!");
10364
10365 /* Read meridional wind at the surface... */
10366 if (!read_met_nc_2d
10367 (ncid, (const char *const[]) { "v10m", "V10M", "10v", "10V", "v10",
10368 "V10"
10369 }, 6, ctl, met, dd,
10370 met->vs, 1.0, 1))
10371 WARN("Cannot read surface meridional wind!");
10372
10373 /* Read eastward turbulent surface stress... */
10374 if (!read_met_nc_2d
10375 (ncid, (const char *const[]) { "iews", "IEWS" }, 2, ctl, met, dd,
10376 met->ess,
10377 1.0, 1))
10378 WARN("Cannot read eastward turbulent surface stress!");
10379
10380 /* Read northward turbulent surface stress... */
10381 if (!read_met_nc_2d
10382 (ncid, (const char *const[]) { "inss", "INSS" }, 2, ctl, met, dd,
10383 met->nss,
10384 1.0, 1))
10385 WARN("Cannot read northward turbulent surface stress!");
10386
10387 /* Read surface sensible heat flux... */
10388 if (!read_met_nc_2d
10389 (ncid, (const char *const[]) { "ishf", "ISHF" }, 2, ctl, met, dd,
10390 met->shf,
10391 1.0, 1))
10392 WARN("Cannot read surface sensible heat flux!");
10393
10394 /* Read land-sea mask... */
10395 if (!read_met_nc_2d
10396 (ncid, (const char *const[]) { "lsm", "LSM" }, 2, ctl, met, dd,
10397 met->lsm,
10398 1.0, 1))
10399 WARN("Cannot read land-sea mask!");
10400
10401 /* Read sea surface temperature... */
10402 if (!read_met_nc_2d
10403 (ncid, (const char *const[]) { "sstk", "SSTK", "sst", "SST" }, 4, ctl,
10404 met, dd, met->sst,
10405 1.0, 1))
10406 WARN("Cannot read sea surface temperature!");
10407
10408 /* Read PBL... */
10409 if (ctl->met_pbl == 0)
10410 if (!read_met_nc_2d
10411 (ncid, (const char *const[]) { "blp", "BLP" }, 2, ctl, met, dd,
10412 met->pbl,
10413 0.01f, 1))
10414 WARN("Cannot read planetary boundary layer pressure!");
10415 if (ctl->met_pbl == 1)
10416 if (!read_met_nc_2d
10417 (ncid, (const char *const[]) { "blh", "BLH" }, 2, ctl, met, dd,
10418 met->pbl,
10419 0.001f, 1))
10420 WARN("Cannot read planetary boundary layer height!");
10421
10422 /* Read CAPE... */
10423 if (ctl->met_cape == 0)
10424 if (!read_met_nc_2d
10425 (ncid, (const char *const[]) { "cape", "CAPE" }, 2, ctl, met, dd,
10426 met->cape, 1.0, 1))
10427 WARN("Cannot read CAPE!");
10428
10429 /* Read CIN... */
10430 if (ctl->met_cape == 0)
10431 if (!read_met_nc_2d
10432 (ncid, (const char *const[]) { "cin", "CIN" }, 2, ctl, met, dd,
10433 met->cin,
10434 1.0, 1))
10435 WARN("Cannot read convective inhibition!");
10436}
10437
10438/*****************************************************************************/
10439
10441 const int ncid,
10442 const ctl_t *ctl,
10443 met_t *met,
10444 dd_t *dd) {
10445
10446 /* Set timer... */
10447 SELECT_TIMER("READ_MET_NC_LEVELS", "INPUT");
10448 LOG(2, "Read level data...");
10449
10450 /* Read temperature... */
10451 if (!read_met_nc_3d
10452 (ncid, (const char *const[]) { "t", "T", "temp", "TEMP" }, 4, ctl, met,
10453 dd, met->t, 1.0))
10454 ERRMSG("Cannot read temperature!");
10455
10456 /* Read horizontal wind and vertical velocity... */
10457 if (!read_met_nc_3d
10458 (ncid, (const char *const[]) { "u", "U" }, 2, ctl, met, dd, met->u,
10459 1.0))
10460 ERRMSG("Cannot read zonal wind!");
10461 if (!read_met_nc_3d
10462 (ncid, (const char *const[]) { "v", "V" }, 2, ctl, met, dd, met->v,
10463 1.0))
10464 ERRMSG("Cannot read meridional wind!");
10465 if (!read_met_nc_3d
10466 (ncid, (const char *const[]) { "w", "W", "omega", "OMEGA" }, 4, ctl,
10467 met, dd, met->w, 0.01f))
10468 WARN("Cannot read vertical velocity!");
10469
10470 /* Read water vapor... */
10471 if (!ctl->met_relhum) {
10472 if (!read_met_nc_3d
10473 (ncid, (const char *const[]) { "q", "Q", "sh", "SH" }, 4, ctl, met,
10474 dd, met->h2o,
10475 (float) (MA / MH2O)))
10476 WARN("Cannot read specific humidity!");
10477 } else {
10478 if (!read_met_nc_3d
10479 (ncid, (const char *const[]) { "rh", "RH" }, 2, ctl, met, dd,
10480 met->h2o, 0.01f))
10481 WARN("Cannot read relative humidity!");
10482#pragma omp parallel for default(shared) collapse(2)
10483 for (int ix = 0; ix < met->nx; ix++)
10484 for (int iy = 0; iy < met->ny; iy++)
10485 for (int ip = 0; ip < met->np; ip++) {
10486 double pw = met->h2o[ix][iy][ip] * PSAT(met->t[ix][iy][ip]);
10487 met->h2o[ix][iy][ip] =
10488 (float) (pw / (met->p[ip] - (1.0 - EPS) * pw));
10489 }
10490 }
10491
10492 /* Read ozone... */
10493 if (!read_met_nc_3d
10494 (ncid, (const char *const[]) { "o3", "O3" }, 2, ctl, met, dd, met->o3,
10495 (float) (MA / MO3)))
10496 WARN("Cannot read ozone data!");
10497
10498 /* Read cloud data... */
10499 if (!read_met_nc_3d
10500 (ncid, (const char *const[]) { "clwc", "CLWC" }, 2, ctl, met, dd,
10501 met->lwc, 1.0))
10502 WARN("Cannot read cloud liquid water content!");
10503 if (!read_met_nc_3d
10504 (ncid, (const char *const[]) { "crwc", "CRWC" }, 2, ctl, met, dd,
10505 met->rwc, 1.0))
10506 WARN("Cannot read cloud rain water content!");
10507 if (!read_met_nc_3d
10508 (ncid, (const char *const[]) { "ciwc", "CIWC" }, 2, ctl, met, dd,
10509 met->iwc, 1.0))
10510 WARN("Cannot read cloud ice water content!");
10511 if (!read_met_nc_3d
10512 (ncid, (const char *const[]) { "cswc", "CSWC" }, 2, ctl, met, dd,
10513 met->swc, 1.0))
10514 WARN("Cannot read cloud snow water content!");
10515 if (!read_met_nc_3d
10516 (ncid, (const char *const[]) { "cc", "CC" }, 2, ctl, met, dd, met->cc,
10517 1.0))
10518 WARN("Cannot read cloud cover!");
10519
10520 /* Read zeta and zeta_dot... */
10521 if (ctl->advect_vert_coord == 1) {
10522 if (!read_met_nc_3d
10523 (ncid, (const char *const[]) { "ZETA", "zeta" }, 2, ctl, met, dd,
10524 met->zetal, 1.0))
10525 WARN("Cannot read ZETA!");
10526 if (!read_met_nc_3d
10527 (ncid, (const char *const[]) { "ZETA_DOT_TOT", "ZETA_DOT_clr",
10528 "zeta_dot_clr"
10529 }, 3, ctl, met, dd, met->zeta_dotl, 0.00001157407f))
10530 ERRMSG("Cannot read ZETA_DOT!");
10531 }
10532
10533 /* Set eta coordinate on native model levels... */
10534 if (ctl->advect_vert_coord == 3 || ctl->qnt_eta_d >= 0) {
10535#pragma omp parallel for default(shared)
10536 for (int ix = 0; ix < met->nx; ix++)
10537 for (int iy = 0; iy < met->ny; iy++)
10538 for (int ip = 0; ip < met->np; ip++)
10539 met->zetal[ix][iy][ip] =
10540 (float) (met->hyam[ip] / (100.0 * P0) + met->hybm[ip]);
10541 }
10542
10543 /* Read eta_dot... */
10544 if (ctl->advect_vert_coord == 3)
10545 if (!read_met_nc_3d
10546 (ncid, (const char *const[]) { "etadot", "ETADOT" }, 2, ctl, met, dd,
10547 met->zeta_dotl,
10548 1.0))
10549 ERRMSG("Cannot read eta vertical velocity!");
10550
10551 /* Store velocities on model levels... */
10552 if (ctl->met_vert_coord != 0) {
10553#pragma omp parallel for default(shared)
10554 for (int ix = 0; ix < met->nx; ix++)
10555 for (int iy = 0; iy < met->ny; iy++)
10556 for (int ip = 0; ip < met->np; ip++) {
10557 met->ul[ix][iy][ip] = met->u[ix][iy][ip];
10558 met->vl[ix][iy][ip] = met->v[ix][iy][ip];
10559 met->wl[ix][iy][ip] = met->w[ix][iy][ip];
10560 }
10561
10562 /* Save number of model levels... */
10563 met->npl = met->np;
10564 }
10565
10566 /* Get pressure on model levels... */
10567 if (ctl->met_np > 0 || ctl->met_vert_coord != 0) {
10568
10569 /* Read 3-D pressure field... */
10570 if (ctl->met_vert_coord == 1) {
10571 if (!read_met_nc_3d
10572 (ncid, (const char *const[]) { "pl", "PL", "pressure", "PRESSURE" },
10573 4, ctl, met, dd, met->pl,
10574 0.01f))
10575 if (!read_met_nc_3d
10576 (ncid, (const char *const[]) { "press", "PRESS" }, 2, ctl, met,
10577 dd, met->pl, 1.0))
10578 ERRMSG("Cannot read pressure on model levels!");
10579 }
10580
10581 /* Use a and b coefficients for full levels (at layer midpoints)... */
10582 else if (ctl->met_vert_coord == 2 || ctl->met_vert_coord == 3) {
10583
10584 /* Check number of levels... */
10585 if (ctl->met_vert_coord == 3 && met->np != ctl->met_nlev)
10586 ERRMSG("Mismatch in number of model levels!");
10587
10588 /* Calculate pressure... */
10589 for (int ix = 0; ix < met->nx; ix++)
10590 for (int iy = 0; iy < met->ny; iy++)
10591 for (int ip = 0; ip < met->np; ip++)
10592 met->pl[ix][iy][ip] =
10593 (float) (met->hyam[ip] / 100. +
10594 met->hybm[ip] * met->ps[ix][iy]);
10595 }
10596
10597 /* Use a and b coefficients for half levels (at layer interfaces)... */
10598 else if (ctl->met_vert_coord == 4) {
10599
10600 /* Check number of levels... */
10601 if (met->np + 1 != ctl->met_nlev)
10602 ERRMSG("Mismatch in number of model levels!");
10603
10604 /* Calculate pressure... */
10605#pragma omp parallel for default(shared) collapse(2)
10606 for (int ix = 0; ix < met->nx; ix++)
10607 for (int iy = 0; iy < met->ny; iy++)
10608 for (int ip = 0; ip < met->np; ip++) {
10609 const double p0 =
10610 met->hyam[ip] / 100. + met->hybm[ip] * met->ps[ix][iy];
10611 const double p1 =
10612 met->hyam[ip + 1] / 100. + met->hybm[ip + 1] * met->ps[ix][iy];
10613 met->pl[ix][iy][ip] = (float) ((p1 - p0) / log(p1 / p0));
10614 }
10615 }
10616
10617 /* Check ordering of pressure levels... */
10618 for (int ix = 0; ix < met->nx; ix++)
10619 for (int iy = 0; iy < met->ny; iy++)
10620 for (int ip = 1; ip < met->np; ip++)
10621 if ((met->pl[ix][iy][0] > met->pl[ix][iy][1]
10622 && met->pl[ix][iy][ip - 1] <= met->pl[ix][iy][ip])
10623 || (met->pl[ix][iy][0] < met->pl[ix][iy][1]
10624 && met->pl[ix][iy][ip - 1] >= met->pl[ix][iy][ip]))
10625 ERRMSG("Pressure profiles are not monotonic!");
10626 }
10627
10628 /* Interpolate from model levels to pressure levels... */
10629 if (ctl->met_np > 0) {
10630
10631 /* Interpolate variables... */
10632 read_met_ml2pl(ctl, met, met->t, "T");
10633 read_met_ml2pl(ctl, met, met->u, "U");
10634 read_met_ml2pl(ctl, met, met->v, "V");
10635 read_met_ml2pl(ctl, met, met->w, "W");
10636 read_met_ml2pl(ctl, met, met->h2o, "H2O");
10637 read_met_ml2pl(ctl, met, met->o3, "O3");
10638 read_met_ml2pl(ctl, met, met->lwc, "LWC");
10639 read_met_ml2pl(ctl, met, met->rwc, "RWC");
10640 read_met_ml2pl(ctl, met, met->iwc, "IWC");
10641 read_met_ml2pl(ctl, met, met->swc, "SWC");
10642 read_met_ml2pl(ctl, met, met->cc, "CC");
10643
10644 /* Set new pressure levels... */
10645 met->np = ctl->met_np;
10646 for (int ip = 0; ip < met->np; ip++)
10647 met->p[ip] = ctl->met_p[ip];
10648 }
10649
10650 /* Check ordering of pressure levels... */
10651 for (int ip = 1; ip < met->np; ip++)
10652 if (met->p[ip - 1] < met->p[ip])
10653 ERRMSG("Pressure levels must be descending!");
10654}
10655
10656/*****************************************************************************/
10657
10658#ifdef DD
10659/* One file-local redistribution workspace. The eight entries per rank are
10660 * y/x starts, y/x counts, periodic x start/count, and the two x offsets. */
10661static struct {
10662 unsigned long long *geometry;
10663 float *global, *packed, *local;
10666} met_global_2d;
10667
10669static void met_global_2d_init(const dd_t *dd, const met_t *met) {
10670
10671 MPI_Comm_rank(MPI_COMM_WORLD, &met_global_2d.rank);
10672 MPI_Comm_size(MPI_COMM_WORLD, &met_global_2d.size);
10673 const unsigned long long own[8] = {
10674 dd->subdomain_start[2], dd->subdomain_start[3],
10675 dd->subdomain_count[2], dd->subdomain_count[3],
10676 dd->halo_bnd_start[3], dd->halo_bnd_count[3],
10677 (unsigned long long) dd->halo_offset_start,
10678 (unsigned long long) dd->halo_offset_end
10679 };
10680 ALLOC(met_global_2d.geometry, unsigned long long,
10681 8 * (size_t) met_global_2d.size);
10682 MPI_Allgather(own, 8, MPI_UNSIGNED_LONG_LONG, met_global_2d.geometry,
10683 8, MPI_UNSIGNED_LONG_LONG, MPI_COMM_WORLD);
10684
10685 const size_t nx = (size_t) dd->nx_glob, ny = (size_t) dd->ny_glob;
10686 if (nx > SIZE_MAX / ny || nx * ny > SIZE_MAX / sizeof(float))
10687 ERRMSG("Global 2-D meteorological field exceeds addressable memory!");
10688 met_global_2d.global_elements = nx * ny;
10689 met_global_2d.local_elements = (size_t) met->nx * (size_t) met->ny;
10690 if (met_global_2d.local_elements > SIZE_MAX / sizeof(float))
10691 ERRMSG("Local 2-D meteorological field exceeds addressable memory!");
10692 ALLOC(met_global_2d.local, float, met_global_2d.local_elements);
10693
10694 for (int rank = 0; rank < met_global_2d.size; rank++) {
10695 const unsigned long long *g = met_global_2d.geometry + 8 * rank;
10696 if (g[3] > SIZE_MAX - g[5])
10697 ERRMSG("Invalid DD window width for global 2-D redistribution!");
10698 const size_t width = (size_t) g[3] + (size_t) g[5];
10699 if (width == 0 || g[2] > SIZE_MAX / width)
10700 ERRMSG("Invalid DD window size for global 2-D redistribution!");
10701 const size_t count = (size_t) g[2] * width;
10702 if (g[6] > width || g[3] > width - g[6]
10703 || g[7] > width || g[5] > width - g[7]
10704 || count > SIZE_MAX / sizeof(float)
10705 || g[0] + g[2] > ny || g[1] + g[3] > nx
10706 || g[4] + g[5] > nx)
10707 ERRMSG("Invalid DD window for global 2-D redistribution!");
10708 if (count > met_global_2d.packed_elements)
10709 met_global_2d.packed_elements = count;
10710 }
10711 ALLOC(met_global_2d.packed, float, met_global_2d.packed_elements);
10712}
10713
10715static void met_global_2d_free(void) {
10716
10717 free(met_global_2d.geometry);
10718 free(met_global_2d.global);
10719 free(met_global_2d.packed);
10720 free(met_global_2d.local);
10721 memset(&met_global_2d, 0, sizeof(met_global_2d));
10722}
10723
10725static void met_global_2d_pack(
10726 const float *global,
10727 float *packed,
10728 const unsigned long long *g,
10729 const size_t nx) {
10730
10731 const size_t width = (size_t) g[3] + (size_t) g[5];
10732 for (size_t y = 0; y < (size_t) g[2]; y++) {
10733 float *row = packed + y * width;
10734 const float *source = global + ((size_t) g[0] + y) * nx;
10735 memcpy(row + (size_t) g[6], source + (size_t) g[1],
10736 (size_t) g[3] * sizeof(float));
10737 if (g[5])
10738 memcpy(row + (size_t) g[7], source + (size_t) g[4],
10739 (size_t) g[5] * sizeof(float));
10740 }
10741}
10742
10744static const float *met_global_2d_read(
10745 const int ncid,
10746 const int varid,
10747 const int ndims,
10748 const int level,
10749 const dd_t *dd) {
10750
10751 const int reader = level % met_global_2d.size;
10752 if (met_global_2d.rank == reader) {
10753 SELECT_TIMER("MET_READ", "INPUT");
10754 if (met_global_2d.global == NULL)
10755 ALLOC(met_global_2d.global, float, met_global_2d.global_elements);
10756 if (ndims == 4) {
10757 const size_t start[4] = { 0, (size_t) level, 0, 0 };
10758 const size_t count[4] = { 1, 1, (size_t) dd->ny_glob,
10759 (size_t) dd->nx_glob };
10760 NC(nc_get_vara_float(ncid, varid, start, count,
10761 met_global_2d.global));
10762 } else {
10763 const size_t start[3] = { 0, 0, 0 };
10764 const size_t count[3] = { 1, (size_t) dd->ny_glob,
10765 (size_t) dd->nx_glob };
10766 NC(nc_get_vara_float(ncid, varid, start, count,
10767 met_global_2d.global));
10768 }
10769 }
10770
10771 for (int rank = 0; rank < met_global_2d.size; rank++) {
10772 const unsigned long long *g = met_global_2d.geometry + 8 * rank;
10773 const size_t elements = (size_t) g[2] * ((size_t) g[3] + (size_t) g[5]);
10774 if (met_global_2d.rank == reader) {
10775 SELECT_TIMER("MET_PACK", "INPUT");
10776 float *target = rank == reader ? met_global_2d.local :
10777 met_global_2d.packed;
10778 met_global_2d_pack(met_global_2d.global, target, g,
10779 (size_t) dd->nx_glob);
10780 if (rank != reader) {
10781 SELECT_TIMER("MET_MPI", "INPUT");
10782 for (size_t offset = 0; offset < elements;) {
10783 const int count = (int) MIN(elements - offset, (size_t) INT_MAX);
10784 if (MPI_Send(target + offset, count, MPI_FLOAT, rank, 0,
10785 MPI_COMM_WORLD) != MPI_SUCCESS)
10786 ERRMSG("Failed to distribute meteorological field!");
10787 offset += (size_t) count;
10788 }
10789 }
10790 } else if (met_global_2d.rank == rank) {
10791 SELECT_TIMER("MET_MPI", "INPUT");
10792 for (size_t offset = 0; offset < elements;) {
10793 const int count = (int) MIN(elements - offset, (size_t) INT_MAX);
10794 if (MPI_Recv(met_global_2d.local + offset, count, MPI_FLOAT, reader,
10795 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE) != MPI_SUCCESS)
10796 ERRMSG("Failed to receive meteorological field!");
10797 offset += (size_t) count;
10798 }
10799 }
10800 }
10801 return met_global_2d.local;
10802}
10803
10805static void met_global_2d_check_var(
10806 const int ncid,
10807 const int varid,
10808 const int ndims,
10809 const dd_t *dd,
10810 const int levels) {
10811
10812 int actual, ids[NC_MAX_VAR_DIMS];
10813 char name[NC_MAX_NAME + 1];
10814 size_t length;
10815 NC(nc_inq_varndims(ncid, varid, &actual));
10816 if (actual != ndims)
10817 ERRMSG("MET_NC_READ_MODE=1 requires time-first NetCDF variables!");
10818 NC(nc_inq_vardimid(ncid, varid, ids));
10819 NC(nc_inq_dimname(ncid, ids[0], name));
10820 if (strcmp(name, "time") != 0)
10821 ERRMSG("MET_NC_READ_MODE=1 requires time as first dimension!");
10822 NC(nc_inq_dimlen(ncid, ids[0], &length));
10823 if (length < 1)
10824 ERRMSG("MET_NC_READ_MODE=1 requires a nonempty time dimension!");
10825 NC(nc_inq_dimname(ncid, ids[ndims - 1], name));
10826 if (strcmp(name, "lon") != 0)
10827 ERRMSG("MET_NC_READ_MODE=1 requires longitude as last dimension!");
10828 NC(nc_inq_dimlen(ncid, ids[ndims - 1], &length));
10829 if (length != (size_t) dd->nx_glob)
10830 ERRMSG("MET_NC_READ_MODE=1 requires longitude as fastest dimension!");
10831 NC(nc_inq_dimname(ncid, ids[ndims - 2], name));
10832 if (strcmp(name, "lat") != 0)
10833 ERRMSG("MET_NC_READ_MODE=1 requires latitude before longitude!");
10834 NC(nc_inq_dimlen(ncid, ids[ndims - 2], &length));
10835 if (length != (size_t) dd->ny_glob)
10836 ERRMSG("MET_NC_READ_MODE=1 requires latitude before longitude!");
10837 if (ndims == 4) {
10838 NC(nc_inq_dimlen(ncid, ids[1], &length));
10839 if (length != (size_t) levels)
10840 ERRMSG("MET_NC_READ_MODE=1 requires all vertical levels!");
10841 }
10842}
10843#endif
10844
10845/*****************************************************************************/
10846
10848 const int ncid,
10849 const char *const varnames[],
10850 const size_t nvarnames,
10851 const ctl_t *ctl,
10852 const met_t *met,
10853 dd_t *dd,
10854 float dest[EX][EY],
10855 const float scl,
10856 const int init) {
10857
10858 const char *varsel = NULL;
10859
10860 float offset, scalfac;
10861
10862 int varid;
10863
10864 /* Check if variable exists... */
10865 for (size_t i = 0; i < nvarnames; i++)
10866 if (nc_inq_varid(ncid, varnames[i], &varid) == NC_NOERR) {
10867 varsel = varnames[i];
10868 break;
10869 }
10870 if (varsel == NULL)
10871 return 0;
10872
10873 /* Read packed data... */
10874 if (ctl->met_nc_scale && !ctl->dd
10875 && nc_get_att_float(ncid, varid, "add_offset", &offset) == NC_NOERR
10876 && nc_get_att_float(ncid, varid, "scale_factor",
10877 &scalfac) == NC_NOERR) {
10878
10879 /* Allocate... */
10880 short *help;
10881 ALLOC(help, short,
10882 EX * EY * EP);
10883
10884 /* Read fill value and missing value... */
10885 short fillval, missval;
10886 if (nc_get_att_short(ncid, varid, "_FillValue", &fillval) != NC_NOERR)
10887 fillval = 0;
10888 if (nc_get_att_short(ncid, varid, "missing_value", &missval) != NC_NOERR)
10889 missval = 0;
10890
10891 /* Write info... */
10892 LOG(2, "Read 2-D variable: %s"
10893 " (FILL = %d, MISS = %d, SCALE = %g, OFFSET = %g)",
10894 varsel, fillval, missval, scalfac, offset);
10895
10896 /* Read data... */
10897 NC(nc_get_var_short(ncid, varid, help));
10898
10899 /* Check meteo data layout... */
10900 if (ctl->met_convention != 0)
10901 ERRMSG("Meteo data layout not implemented for packed netCDF files!");
10902
10903 /* Copy and check data... */
10904 omp_set_dynamic(1);
10905#pragma omp parallel for default(shared)
10906 for (int ix = 0; ix < met->nx; ix++)
10907 for (int iy = 0; iy < met->ny; iy++) {
10908 if (init)
10909 dest[ix][iy] = 0;
10910 const short aux = help[ARRAY_2D(iy, ix, met->nx)];
10911 if ((fillval == 0 || aux != fillval)
10912 && (missval == 0 || aux != missval)
10913 && fabsf(aux * scalfac + offset) < 1e14f)
10914 dest[ix][iy] += scl * (aux * scalfac + offset);
10915 else
10916 dest[ix][iy] = NAN;
10917 }
10918 omp_set_dynamic(0);
10919
10920 /* Free... */
10921 free(help);
10922 }
10923
10924 /* Unpacked data... */
10925 else if (!ctl->dd) {
10926
10927 /* Allocate... */
10928 float *help;
10929 ALLOC(help, float,
10930 EX * EY);
10931
10932 /* Read fill value and missing value... */
10933 float fillval, missval;
10934 if (nc_get_att_float(ncid, varid, "_FillValue", &fillval) != NC_NOERR)
10935 fillval = 0;
10936 if (nc_get_att_float(ncid, varid, "missing_value", &missval) != NC_NOERR)
10937 missval = 0;
10938
10939 /* Write info... */
10940 LOG(2, "Read 2-D variable: %s (FILL = %g, MISS = %g)",
10941 varsel, fillval, missval);
10942
10943 /* Read data... */
10944 NC(nc_get_var_float(ncid, varid, help));
10945
10946 /* Check meteo data layout... */
10947 if (ctl->met_convention == 0) {
10948
10949 /* Copy and check data (ordering: lat, lon)... */
10950 omp_set_dynamic(1);
10951#pragma omp parallel for default(shared)
10952 for (int ix = 0; ix < met->nx; ix++)
10953 for (int iy = 0; iy < met->ny; iy++) {
10954 if (init)
10955 dest[ix][iy] = 0;
10956 const float aux = help[ARRAY_2D(iy, ix, met->nx)];
10957 if ((fillval == 0 || aux != fillval)
10958 && (missval == 0 || aux != missval)
10959 && fabsf(aux) < 1e14f)
10960 dest[ix][iy] += scl * aux;
10961 else
10962 dest[ix][iy] = NAN;
10963 }
10964 omp_set_dynamic(0);
10965
10966 } else {
10967
10968 /* Copy and check data (ordering: lon, lat)... */
10969 omp_set_dynamic(1);
10970#pragma omp parallel for default(shared)
10971 for (int iy = 0; iy < met->ny; iy++)
10972 for (int ix = 0; ix < met->nx; ix++) {
10973 if (init)
10974 dest[ix][iy] = 0;
10975 const float aux = help[ARRAY_2D(ix, iy, met->ny)];
10976 if ((fillval == 0 || aux != fillval)
10977 && (missval == 0 || aux != missval)
10978 && fabsf(aux) < 1e14f)
10979 dest[ix][iy] += scl * aux;
10980 else
10981 dest[ix][iy] = NAN;
10982 }
10983 omp_set_dynamic(0);
10984 }
10985
10986 /* Free... */
10987 free(help);
10988 }
10989
10990 /* Domain decomposed data... */
10991 else {
10992
10993 /* Read fill value and missing value... */
10994 float fillval, missval;
10995 if (nc_get_att_float(ncid, varid, "_FillValue", &fillval) != NC_NOERR)
10996 fillval = 0;
10997 if (nc_get_att_float(ncid, varid, "missing_value", &missval) != NC_NOERR)
10998 missval = 0;
10999
11000 /* Write info... */
11001 LOG(2, "Read 2-D variable: %s (FILL = %g, MISS = %g)",
11002 varsel, fillval, missval);
11003
11004#ifdef DD
11005 if (ctl->met_nc_read_mode == 1) {
11006 met_global_2d_check_var(ncid, varid, 3, dd, 0);
11007 NC(nc_var_par_access(ncid, varid, NC_INDEPENDENT));
11008 const float *help = met_global_2d_read(ncid, varid, 3, varid, dd);
11009 SELECT_TIMER("MET_CONVERT", "INPUT");
11010 omp_set_dynamic(1);
11011#pragma omp parallel for default(shared)
11012 for (int iy = 0; iy < met->ny; iy++)
11013 for (int ix = 0; ix < met->nx; ix++) {
11014 const float aux = help[(size_t) iy * (size_t) met->nx + (size_t) ix];
11015 if (init)
11016 dest[ix][iy] = 0;
11017 if ((fillval == 0 || aux != fillval)
11018 && (missval == 0 || aux != missval) && fabsf(aux) < 1e14f)
11019 dest[ix][iy] += scl * aux;
11020 else
11021 dest[ix][iy] = NAN;
11022 }
11023 omp_set_dynamic(0);
11024 /* Return to the caller's timer (only called from read_met_nc_surface)... */
11025 SELECT_TIMER("READ_MET_SURFACE", "INPUT");
11026 return 1;
11027 }
11028#endif
11029
11030 /* Define hyperslab... */
11031 float *help;
11032 size_t help_subdomain_start[3];
11033 size_t help_subdomain_count[3];
11034
11035 help_subdomain_start[0] = 0;
11036 if (ctl->met_convention == 0) {
11037 help_subdomain_start[1] = dd->subdomain_start[2];
11038 help_subdomain_start[2] = dd->subdomain_start[3];
11039 } else {
11040 help_subdomain_start[1] = dd->subdomain_start[3];
11041 help_subdomain_start[2] = dd->subdomain_start[2];
11042 }
11043
11044 help_subdomain_count[0] = 1;
11045 if (ctl->met_convention == 0) {
11046 help_subdomain_count[1] = dd->subdomain_count[2]; //y
11047 help_subdomain_count[2] = dd->subdomain_count[3]; //x
11048 } else {
11049 help_subdomain_count[1] = dd->subdomain_count[3]; //x
11050 help_subdomain_count[2] = dd->subdomain_count[2]; //y
11051 }
11052
11053 ALLOC(help, float,
11054 (int) dd->subdomain_count[2] * (int) dd->subdomain_count[3]);
11055
11056 /* Read data... */
11057 SELECT_TIMER("MET_READ", "INPUT");
11058#ifdef DD
11059 nc_var_par_access(ncid, varid, NC_COLLECTIVE);
11060#endif
11061 NC(nc_get_vara_float
11062 (ncid, varid, help_subdomain_start, help_subdomain_count, help));
11063
11064 /* Read halos at boundaries... */
11065 size_t help_halo_bnd_start[3];
11066 size_t help_halo_bnd_count[3];
11067
11068 help_halo_bnd_start[0] = 0;
11069 if (ctl->met_convention == 0) {
11070 help_halo_bnd_start[1] = dd->halo_bnd_start[2];
11071 help_halo_bnd_start[2] = dd->halo_bnd_start[3];
11072 } else {
11073 help_halo_bnd_start[1] = dd->halo_bnd_start[3];
11074 help_halo_bnd_start[2] = dd->halo_bnd_start[2];
11075 }
11076
11077 help_halo_bnd_count[0] = 1;
11078 if (ctl->met_convention == 0) {
11079 help_halo_bnd_count[1] = dd->halo_bnd_count[2]; //y
11080 help_halo_bnd_count[2] = dd->halo_bnd_count[3]; //x
11081 } else {
11082 help_halo_bnd_count[1] = dd->halo_bnd_count[3]; //x
11083 help_halo_bnd_count[2] = dd->halo_bnd_count[2]; //y
11084 }
11085
11086 float *help_halo;
11087 ALLOC(help_halo, float,
11088 help_halo_bnd_count[1] * help_halo_bnd_count[2]);
11089
11090#ifdef DD
11091 nc_var_par_access(ncid, varid, NC_COLLECTIVE);
11092#endif
11093 NC(nc_get_vara_float
11094 (ncid, varid, help_halo_bnd_start, help_halo_bnd_count,
11095 help_halo));
11096
11097 SELECT_TIMER("MET_CONVERT", "INPUT");
11098
11099 /* Check meteo data layout... */
11100 if (ctl->met_convention == 0) {
11101
11102 /* Copy and check data (ordering: lat, lon)... */
11103 omp_set_dynamic(1);
11104#pragma omp parallel for default(shared)
11105 for (int ix = 0; ix < (int) help_subdomain_count[2]; ix++)
11106 for (int iy = 0; iy < (int) help_subdomain_count[1]; iy++) {
11107 if (init == 1)
11108 dest[ix + dd->halo_offset_start][iy] = 0;
11109 const float aux =
11110 help[ARRAY_2D(iy, ix, (int) help_subdomain_count[2])];
11111 if ((fillval == 0 || aux != fillval)
11112 && (missval == 0 || aux != missval)
11113 && fabsf(aux) < 1e14f) {
11114 dest[ix + dd->halo_offset_start][iy] += scl * aux;
11115 } else
11116 dest[ix + dd->halo_offset_start][iy] = NAN;
11117 }
11118
11119#pragma omp parallel for default(shared)
11120 for (int ix = 0; ix < (int) help_halo_bnd_count[2]; ix++)
11121 for (int iy = 0; iy < (int) help_halo_bnd_count[1]; iy++) {
11122 if (init == 1)
11123 dest[ix + dd->halo_offset_end][iy] = 0;
11124 const float aux =
11125 help_halo[ARRAY_2D(iy, ix, (int) help_halo_bnd_count[2])];
11126 if ((fillval == 0 || aux != fillval)
11127 && (missval == 0 || aux != missval)
11128 && fabsf(aux) < 1e14f)
11129 dest[ix + dd->halo_offset_end][iy] += scl * aux;
11130 else {
11131 dest[ix + dd->halo_offset_end][iy] = NAN;
11132 }
11133 }
11134 omp_set_dynamic(0);
11135
11136 } else {
11137
11138 /* Copy and check data (ordering: lon, lat)... */
11139 omp_set_dynamic(1);
11140#pragma omp parallel for default(shared)
11141 for (int ix = 0; ix < (int) help_subdomain_count[1]; ix++)
11142 for (int iy = 0; iy < (int) help_subdomain_count[2]; iy++) {
11143 if (init == 1)
11144 dest[ix + dd->halo_offset_start][iy] = 0;
11145 const float aux =
11146 help[ARRAY_2D(ix, iy, (int) help_subdomain_count[1])];
11147 if ((fillval == 0 || aux != fillval)
11148 && (missval == 0 || aux != missval)
11149 && fabsf(aux) < 1e14f)
11150 dest[ix + dd->halo_offset_start][iy] += scl * aux;
11151 else
11152 dest[ix + dd->halo_offset_start][iy] = NAN;
11153 }
11154
11155#pragma omp parallel for default(shared)
11156 for (int ix = 0; ix < (int) help_halo_bnd_count[1]; ix++)
11157 for (int iy = 0; iy < (int) help_halo_bnd_count[2]; iy++) {
11158 if (init == 1)
11159 dest[ix + dd->halo_offset_end][iy] = 0;
11160 const float aux =
11161 help_halo[ARRAY_2D(ix, iy, (int) help_halo_bnd_count[1])];
11162 if ((fillval == 0 || aux != fillval)
11163 && (missval == 0 || aux != missval)
11164 && fabsf(aux) < 1e14f)
11165 dest[ix + dd->halo_offset_end][iy] += scl * aux;
11166 else
11167 dest[ix + dd->halo_offset_end][iy] = NAN;
11168 }
11169 omp_set_dynamic(0);
11170 }
11171 /* Return to the caller's timer (only called from read_met_nc_surface)... */
11172 SELECT_TIMER("READ_MET_SURFACE", "INPUT");
11173
11174 /* Free... */
11175 free(help);
11176 free(help_halo);
11177 }
11178
11179 /* Return... */
11180 return 1;
11181}
11182
11183/*****************************************************************************/
11184
11186 const int ncid,
11187 const char *const varnames[],
11188 const size_t nvarnames,
11189 const ctl_t *ctl,
11190 const met_t *met,
11191 dd_t *dd,
11192 float dest[EX][EY][EP],
11193 const float scl) {
11194
11195 const char *varsel = NULL;
11196
11197 float offset, scalfac;
11198
11199 int varid;
11200
11201 /* Check if variable exists... */
11202 for (size_t i = 0; i < nvarnames; i++)
11203 if (nc_inq_varid(ncid, varnames[i], &varid) == NC_NOERR) {
11204 varsel = varnames[i];
11205 break;
11206 }
11207 if (varsel == NULL)
11208 return 0;
11209
11210 /* Read packed data... */
11211 if (ctl->met_nc_scale && !ctl->dd
11212 && nc_get_att_float(ncid, varid, "add_offset", &offset) == NC_NOERR
11213 && nc_get_att_float(ncid, varid, "scale_factor",
11214 &scalfac) == NC_NOERR) {
11215
11216 /* Allocate... */
11217 short *help;
11218 ALLOC(help, short,
11219 EX * EY * EP);
11220
11221 /* Read fill value and missing value... */
11222 short fillval, missval;
11223 if (nc_get_att_short(ncid, varid, "_FillValue", &fillval) != NC_NOERR)
11224 fillval = 0;
11225 if (nc_get_att_short(ncid, varid, "missing_value", &missval) != NC_NOERR)
11226 missval = 0;
11227
11228 /* Write info... */
11229 LOG(2, "Read 3-D variable: %s "
11230 "(FILL = %d, MISS = %d, SCALE = %g, OFFSET = %g)",
11231 varsel, fillval, missval, scalfac, offset);
11232
11233 /* Read data... */
11234 NC(nc_get_var_short(ncid, varid, help));
11235
11236 /* Check meteo data layout... */
11237 if (ctl->met_convention != 0)
11238 ERRMSG("Meteo data layout not implemented for packed netCDF files!");
11239
11240 /* Copy and check data... */
11241 omp_set_dynamic(1);
11242#pragma omp parallel for default(shared)
11243 for (int ix = 0; ix < met->nx; ix++)
11244 for (int iy = 0; iy < met->ny; iy++)
11245 for (int ip = 0; ip < met->np; ip++) {
11246 const short aux = help[ARRAY_3D(ip, iy, met->ny, ix, met->nx)];
11247 if ((fillval == 0 || aux != fillval)
11248 && (missval == 0 || aux != missval)
11249 && fabsf(aux * scalfac + offset) < 1e14f)
11250 dest[ix][iy][ip] = scl * (aux * scalfac + offset);
11251 else
11252 dest[ix][iy][ip] = NAN;
11253 }
11254 omp_set_dynamic(0);
11255
11256 /* Free... */
11257 free(help);
11258 }
11259
11260 /* Unpacked data... */
11261 else if (!ctl->dd) {
11262
11263 /* Allocate... */
11264 float *help;
11265 ALLOC(help, float,
11266 EX * EY * EP);
11267
11268 /* Read fill value and missing value... */
11269 float fillval, missval;
11270 if (nc_get_att_float(ncid, varid, "_FillValue", &fillval) != NC_NOERR)
11271 fillval = 0;
11272 if (nc_get_att_float(ncid, varid, "missing_value", &missval) != NC_NOERR)
11273 missval = 0;
11274
11275 /* Write info... */
11276 LOG(2, "Read 3-D variable: %s (FILL = %g, MISS = %g)",
11277 varsel, fillval, missval);
11278
11279 /* Read data... */
11280 NC(nc_get_var_float(ncid, varid, help));
11281
11282 /* Check meteo data layout... */
11283 if (ctl->met_convention == 0) {
11284
11285 /* Copy and check data (ordering: lev, lat, lon)... */
11286 omp_set_dynamic(1);
11287#pragma omp parallel for default(shared)
11288 for (int ix = 0; ix < met->nx; ix++)
11289 for (int iy = 0; iy < met->ny; iy++)
11290 for (int ip = 0; ip < met->np; ip++) {
11291 const float aux = help[ARRAY_3D(ip, iy, met->ny, ix, met->nx)];
11292 if ((fillval == 0 || aux != fillval)
11293 && (missval == 0 || aux != missval)
11294 && fabsf(aux) < 1e14f)
11295 dest[ix][iy][ip] = scl * aux;
11296 else
11297 dest[ix][iy][ip] = NAN;
11298 }
11299 omp_set_dynamic(0);
11300
11301 } else {
11302
11303 /* Copy and check data (ordering: lon, lat, lev)... */
11304 omp_set_dynamic(1);
11305#pragma omp parallel for default(shared)
11306 for (int ip = 0; ip < met->np; ip++)
11307 for (int iy = 0; iy < met->ny; iy++)
11308 for (int ix = 0; ix < met->nx; ix++) {
11309 const float aux = help[ARRAY_3D(ix, iy, met->ny, ip, met->np)];
11310 if ((fillval == 0 || aux != fillval)
11311 && (missval == 0 || aux != missval)
11312 && fabsf(aux) < 1e14f)
11313 dest[ix][iy][ip] = scl * aux;
11314 else
11315 dest[ix][iy][ip] = NAN;
11316 }
11317 omp_set_dynamic(0);
11318 }
11319
11320 /* Free... */
11321 free(help);
11322 }
11323
11324 /* Domain decomposed data... */
11325 else {
11326
11327 /* Read fill value and missing value... */
11328 float fillval, missval;
11329 if (nc_get_att_float(ncid, varid, "_FillValue", &fillval) != NC_NOERR)
11330 fillval = 0;
11331 if (nc_get_att_float(ncid, varid, "missing_value", &missval) != NC_NOERR)
11332 missval = 0;
11333
11334 /* Write info... */
11335 LOG(2, "Read 3-D variable: %s (FILL = %g, MISS = %g)",
11336 varsel, fillval, missval);
11337
11338#ifdef DD
11339 if (ctl->met_nc_read_mode == 1) {
11340 met_global_2d_check_var(ncid, varid, 4, dd, met->np);
11341 NC(nc_var_par_access(ncid, varid, NC_INDEPENDENT));
11342 for (int ip = 0; ip < met->np; ip++) {
11343 const float *help = met_global_2d_read(ncid, varid, 4, ip, dd);
11344 SELECT_TIMER("MET_CONVERT", "INPUT");
11345 omp_set_dynamic(1);
11346#pragma omp parallel for default(shared)
11347 for (int iy = 0; iy < met->ny; iy++)
11348 for (int ix = 0; ix < met->nx; ix++) {
11349 const float aux =
11350 help[(size_t) iy * (size_t) met->nx + (size_t) ix];
11351 if ((fillval == 0 || aux != fillval)
11352 && (missval == 0 || aux != missval) && fabsf(aux) < 1e14f)
11353 dest[ix][iy][ip] = scl * aux;
11354 else
11355 dest[ix][iy][ip] = NAN;
11356 }
11357 omp_set_dynamic(0);
11358 }
11359 /* Return to the caller's timer (only called from read_met_nc_levels)... */
11360 SELECT_TIMER("READ_MET_NC_LEVELS", "INPUT");
11361 return 1;
11362 }
11363#endif
11364
11365 /* Define hyperslab... */
11366 size_t help_subdomain_start[4];
11367 size_t help_subdomain_count[4];
11368 size_t help_halo_bnd_start[4];
11369 size_t help_halo_bnd_count[4];
11370
11371 if (ctl->met_convention == 0) {
11372 for (int i = 0; i < 4; i++) {
11373 help_subdomain_start[i] = dd->subdomain_start[i];
11374 help_subdomain_count[i] = dd->subdomain_count[i];
11375 help_halo_bnd_start[i] = dd->halo_bnd_start[i];
11376 help_halo_bnd_count[i] = dd->halo_bnd_count[i];
11377 }
11378 } else {
11379 help_subdomain_start[0] = dd->subdomain_start[0];
11380 help_subdomain_start[1] = dd->subdomain_start[3];
11381 help_subdomain_start[2] = dd->subdomain_start[2];
11382 help_subdomain_start[3] = dd->subdomain_start[1];
11383
11384 help_subdomain_count[0] = dd->subdomain_count[0];
11385 help_subdomain_count[1] = dd->subdomain_count[3];
11386 help_subdomain_count[2] = dd->subdomain_count[2];
11387 help_subdomain_count[3] = dd->subdomain_count[1];
11388
11389 help_halo_bnd_start[0] = dd->halo_bnd_start[0];
11390 help_halo_bnd_start[1] = dd->halo_bnd_start[3];
11391 help_halo_bnd_start[2] = dd->halo_bnd_start[2];
11392 help_halo_bnd_start[3] = dd->halo_bnd_start[1];
11393
11394 help_halo_bnd_count[0] = dd->halo_bnd_count[0];
11395 help_halo_bnd_count[1] = dd->halo_bnd_count[3];
11396 help_halo_bnd_count[2] = dd->halo_bnd_count[2];
11397 help_halo_bnd_count[3] = dd->halo_bnd_count[1];
11398 }
11399
11400 /* Allocate... */
11401 float *help;
11402 ALLOC(help, float,
11403 (int) dd->subdomain_count[0] * (int) dd->subdomain_count[1]
11404 * (int) dd->subdomain_count[2] * (int) dd->subdomain_count[3]);
11405
11406 /* Use default NetCDF parallel I/O behavior */
11407 SELECT_TIMER("MET_READ", "INPUT");
11408#ifdef DD
11409 NC(nc_var_par_access(ncid, varid, NC_INDEPENDENT));
11410#endif
11411 NC(nc_get_vara_float
11412 (ncid, varid, help_subdomain_start, help_subdomain_count, help));
11413
11414 /* Read halos separately at boundaries... */
11415 float *help_halo;
11416 ALLOC(help_halo, float,
11417 dd->halo_bnd_count[0] * dd->halo_bnd_count[1] *
11418 dd->halo_bnd_count[2] * dd->halo_bnd_count[3]);
11419
11420#ifdef DD
11421 NC(nc_var_par_access(ncid, varid, NC_INDEPENDENT));
11422#endif
11423 if (dd->halo_bnd_count[1] > 0 && dd->halo_bnd_count[2] > 0
11424 && dd->halo_bnd_count[3] > 0) {
11425 NC(nc_get_vara_float
11426 (ncid, varid, help_halo_bnd_start, help_halo_bnd_count,
11427 help_halo));
11428 }
11429
11430 SELECT_TIMER("MET_CONVERT", "INPUT");
11431
11432 /* Check meteo data layout... */
11433 if (ctl->met_convention == 0) {
11434
11435 /* Copy and check data (ordering: lev, lat, lon)... */
11436 omp_set_dynamic(1);
11437#pragma omp parallel for default(shared)
11438 for (int ix = 0; ix < (int) dd->subdomain_count[3]; ix++)
11439 for (int iy = 0; iy < (int) dd->subdomain_count[2]; iy++)
11440 for (int ip = 0; ip < met->np; ip++) {
11441 const float aux =
11442 help[ARRAY_3D(ip, iy, (int) dd->subdomain_count[2], ix,
11443 (int) dd->subdomain_count[3])];
11444 if ((fillval == 0 || aux != fillval)
11445 && (missval == 0 || aux != missval)
11446 && fabsf(aux) < 1e14f) {
11447 dest[ix + dd->halo_offset_start][iy][ip] = scl * aux;
11448
11449 } else
11450 dest[ix + dd->halo_offset_start][iy][ip] = NAN;
11451 }
11452
11453#pragma omp parallel for default(shared)
11454 for (int ix = 0; ix < (int) dd->halo_bnd_count[3]; ix++)
11455 for (int iy = 0; iy < (int) dd->halo_bnd_count[2]; iy++)
11456 for (int ip = 0; ip < met->np; ip++) {
11457 const float aux =
11458 help_halo[ARRAY_3D(ip, iy, (int) dd->halo_bnd_count[2], ix,
11459 (int) dd->halo_bnd_count[3])];
11460 if ((fillval == 0 || aux != fillval)
11461 && (missval == 0 || aux != missval)
11462 && fabsf(aux) < 1e14f)
11463 dest[ix + dd->halo_offset_end][iy][ip] = scl * aux;
11464 else
11465 dest[ix + dd->halo_offset_end][iy][ip] = NAN;
11466 }
11467 omp_set_dynamic(0);
11468
11469 } else {
11470
11471 /* Copy and check data (ordering: lon, lat, lev)... */
11472 omp_set_dynamic(1);
11473#pragma omp parallel for default(shared)
11474 for (int ip = 0; ip < met->np; ip++)
11475 for (int iy = 0; iy < (int) dd->subdomain_count[2]; iy++)
11476 for (int ix = 0; ix < (int) dd->subdomain_count[3]; ix++) {
11477 const float aux =
11478 help[ARRAY_3D
11479 (ix, iy, (int) dd->subdomain_count[2], ip, met->np)];
11480 if ((fillval == 0 || aux != fillval)
11481 && (missval == 0 || aux != missval)
11482 && fabsf(aux) < 1e14f)
11483 dest[ix + dd->halo_offset_start][iy][ip] = scl * aux;
11484 else
11485 dest[ix + dd->halo_offset_start][iy][ip] = NAN;
11486 }
11487
11488#pragma omp parallel for default(shared)
11489 for (int ip = 0; ip < met->np; ip++)
11490 for (int iy = 0; iy < (int) dd->halo_bnd_count[2]; iy++)
11491 for (int ix = 0; ix < (int) dd->halo_bnd_count[3]; ix++) {
11492 const float aux =
11493 help_halo[ARRAY_3D(ix, iy, (int) dd->halo_bnd_count[2], ip,
11494 met->np)];
11495 if ((fillval == 0 || aux != fillval)
11496 && (missval == 0 || aux != missval)
11497 && fabsf(aux) < 1e14f)
11498 dest[ix + dd->halo_offset_end][iy][ip] = scl * aux;
11499 else
11500 dest[ix + dd->halo_offset_end][iy][ip] = NAN;
11501 }
11502 omp_set_dynamic(0);
11503 }
11504 /* Return to the caller's timer (only called from read_met_nc_levels)... */
11505 SELECT_TIMER("READ_MET_NC_LEVELS", "INPUT");
11506
11507 /* Free... */
11508 free(help);
11509 free(help_halo);
11510 }
11511
11512 /* Return... */
11513 return 1;
11514}
11515
11516/*****************************************************************************/
11517
11518#ifdef ECCODES
11519int read_met_grib(
11520 const char *filename,
11521 const ctl_t *ctl,
11522 met_t *met) {
11523
11524 /* Set filenames... */
11525 size_t filename_len = strlen(filename) + 1;
11526 char sf_filename[filename_len];
11527 char ml_filename[filename_len];
11528 strcpy(sf_filename, filename);
11529 strcpy(ml_filename, filename);
11530 get_met_replace(ml_filename, "XX", "ml");
11531 get_met_replace(sf_filename, "XX", "sf");
11532
11533 /* Open files... */
11534 FILE *ml_file = fopen(ml_filename, "rb");
11535 FILE *sf_file = fopen(sf_filename, "rb");
11536 if (ml_file == NULL || sf_file == NULL) {
11537 if (ml_file != NULL) {
11538 fclose(ml_file);
11539 WARN("Cannot open file: %s", sf_filename);
11540 }
11541 if (sf_file != NULL) {
11542 fclose(sf_file);
11543 WARN("Cannot open file: %s", ml_filename);
11544 }
11545 return 0;
11546 }
11547
11548 /* Get handles for model level data... */
11549 int ml_num_messages = 0, err = 0;
11550 ECC(codes_count_in_file(0, ml_file, &ml_num_messages));
11551 codes_handle **ml_handles =
11552 (codes_handle **) malloc(sizeof(codes_handle *) *
11553 (size_t) ml_num_messages);
11554 for (int i = 0; i < ml_num_messages; i++) {
11555 codes_handle *h = NULL;
11556 if ((h = codes_grib_handle_new_from_file(0, ml_file, &err)) != NULL)
11557 ml_handles[i] = h;
11558 }
11559
11560 /* Get handles for surface data... */
11561 int sf_num_messages = 0;
11562 ECC(codes_count_in_file(0, sf_file, &sf_num_messages));
11563 codes_handle **sf_handles =
11564 (codes_handle **) malloc(sizeof(codes_handle *) *
11565 (size_t) sf_num_messages);
11566 for (int i = 0; i < sf_num_messages; i++) {
11567 codes_handle *h = NULL;
11568 if ((h = codes_grib_handle_new_from_file(0, sf_file, &err)) != NULL)
11569 sf_handles[i] = h;
11570 }
11571
11572 /* Close files... */
11573 fclose(ml_file);
11574 fclose(sf_file);
11575
11576 /* Read grid data... */
11577 read_met_grib_grid(ml_handles, ml_num_messages, met);
11578
11579 /* Read surface data... */
11580 read_met_grib_surface(sf_handles, sf_num_messages, ctl, met);
11581 for (int i = 0; i < sf_num_messages; i++)
11582 codes_handle_delete(sf_handles[i]);
11583 free(sf_handles);
11584
11585 /* Compute 3D pressure field... */
11586 size_t value_count = 0;
11587 ECC(codes_get_size(ml_handles[0], "pv", &value_count));
11588 if (value_count % 2 != 0)
11589 ERRMSG("Unexpected pv array length!");
11590 size_t nlevels = value_count / 2 - 1; /* number of full model levels */
11591 double *values;
11592 ALLOC(values, double,
11593 value_count);
11594 ECC(codes_get_double_array(ml_handles[0], "pv", values, &value_count));
11595 double *a_vals = values;
11596 double *b_vals = values + nlevels;
11597 if (met->npl > (int) nlevels)
11598 ERRMSG("met->npl exceeds number of pressure levels in GRIB!");
11599 for (int nx = 0; nx < met->nx; nx++)
11600 for (int ny = 0; ny < met->ny; ny++)
11601 for (int level = 0; level <= met->npl; level++) {
11602 const float p1 = (float) (a_vals[level] * 0.01f +
11603 met->ps[nx][ny] * b_vals[level]);
11604 const float p2 = (float) (a_vals[level + 1] * 0.01f +
11605 met->ps[nx][ny] * b_vals[level + 1]);
11606 met->pl[nx][ny][level] = 0.5f * (p1 + p2);
11607 }
11608 free(values);
11609
11610 /* Read model level data... */
11611 read_met_grib_levels(ml_handles, ml_num_messages, ctl, met);
11612 for (int i = 0; i < ml_num_messages; i++)
11613 codes_handle_delete(ml_handles[i]);
11614 free(ml_handles);
11615
11616 /* Return success... */
11617 return 1;
11618}
11619#endif
11620
11621/*****************************************************************************/
11622
11623#ifdef ECCODES
11625 codes_handle **handles,
11626 int count_handles,
11627 met_t *met) {
11628
11629 /* Set timer... */
11630 SELECT_TIMER("READ_MET_GRIB_GRID", "INPUT");
11631 LOG(2, "Read meteo grid information...");
11632
11633 /* Read date and time... */
11634 char datestr[LEN], timestr[LEN];
11635 size_t s_date = sizeof(datestr);
11636 ECC(codes_get_string(handles[0], "dataDate", datestr, &s_date));
11637 size_t s_time = sizeof(timestr);
11638 ECC(codes_get_string(handles[0], "dataTime", timestr, &s_time));
11639 int year, month, day, hour;
11640 if (sscanf(datestr, "%4d%2d%2d", &year, &month, &day) != 3)
11641 ERRMSG("Failed to parse dataDate: %s", datestr);
11642 if (sscanf(timestr, "%2d", &hour) != 1)
11643 ERRMSG("Failed to parse dataTime: %s", timestr);
11644 time2jsec(year, month, day, hour, 0, 0, 0, &(met->time));
11645 LOG(2, "Time: %.2f (%d-%02d-%02d, %02d:%02d UTC)", met->time, year, month,
11646 day, hour, 0);
11647
11648 /* Read grid information... */
11649 long count_lat = 0, count_lon = 0;
11650 ECC(codes_get_long(handles[0], "Nj", &count_lat));
11651 ECC(codes_get_long(handles[0], "Ni", &count_lon));
11652 met->ny = (int) count_lat;
11653 met->nx = (int) count_lon;
11654
11655 /* Check grid dimensions... */
11656 LOG(2, "Number of longitudes: %d", met->nx);
11657 if (met->nx < 2 || met->nx > EX)
11658 ERRMSG("Number of longitudes out of range!");
11659 LOG(2, "Number of latitudes: %d", met->ny);
11660 if (met->ny < 2 || met->ny > EY)
11661 ERRMSG("Number of latitudes out of range!");
11662
11663 double first_lon, last_lon, first_lat, last_lat, inc_lon, inc_lat;
11664 ECC(codes_get_double
11665 (handles[0], "longitudeOfFirstGridPointInDegrees", &first_lon));
11666 ECC(codes_get_double
11667 (handles[0], "latitudeOfFirstGridPointInDegrees", &first_lat));
11668 ECC(codes_get_double
11669 (handles[0], "longitudeOfLastGridPointInDegrees", &last_lon));
11670 ECC(codes_get_double
11671 (handles[0], "latitudeOfLastGridPointInDegrees", &last_lat));
11672 ECC(codes_get_double(handles[0], "iDirectionIncrementInDegrees", &inc_lon));
11673 ECC(codes_get_double(handles[0], "jDirectionIncrementInDegrees", &inc_lat));
11674
11675 long jscanpos, iscanneg;
11676 ECC(codes_get_long(handles[0], "iScansNegatively", &iscanneg));
11677 ECC(codes_get_long(handles[0], "jScansPositively", &jscanpos));
11678
11679 /* Compute longitude-latitude grid... */
11680 int counter = 0;
11681 if (iscanneg == 0)
11682 for (double i = first_lon; i <= last_lon + 1e-6; i += inc_lon) {
11683 met->lon[counter] = i;
11684 counter++;
11685 } else
11686 for (double i = first_lon; i > last_lon - 1e-6; i -= inc_lon) {
11687 met->lon[counter] = i;
11688 counter++;
11689 }
11690
11691 counter = 0;
11692 if (jscanpos == 0)
11693 for (double i = first_lat; i > last_lat - 1e-6; i -= inc_lat) {
11694 met->lat[counter] = i;
11695 counter++;
11696 } else
11697 for (double i = first_lat; i <= last_lat + 1e-6; i += inc_lat) {
11698 met->lat[counter] = i;
11699 counter++;
11700 }
11701
11702 /* Write info... */
11703 LOG(2, "Longitudes: %g, %g ... %g deg",
11704 met->lon[0], met->lon[1], met->lon[met->nx - 1]);
11705 LOG(2, "Latitudes: %g, %g ... %g deg",
11706 met->lat[0], met->lat[1], met->lat[met->ny - 1]);
11707
11708 /* Read vertical levels... */
11709 int max_level = 0;
11710 for (int i = 0; i < count_handles; i++) {
11711 long level;
11712 ECC(codes_get_long(handles[i], "level", &level));
11713 if (level > max_level)
11714 max_level = (int) level;
11715 }
11716 met->npl = max_level;
11717
11718 /* Check number of levels... */
11719 LOG(2, "Number of levels: %d", met->npl);
11720 if (met->npl < 2 || met->npl > EP)
11721 ERRMSG("Number of levels out of range!");
11722}
11723#endif
11724
11725/*****************************************************************************/
11726
11727#ifdef ECCODES
11729 codes_handle **handles,
11730 const int num_messages,
11731 const ctl_t *ctl,
11732 met_t *met) {
11733
11734 /* Set timer... */
11735 SELECT_TIMER("READ_MET_GRIB_LEVELS", "INPUT");
11736 LOG(2, "Read level data...");
11737
11738 /* Init... */
11739 int t_flag = 0, u_flag = 0, v_flag = 0, w_flag = 0, o3_flag = 0, h2o_flag =
11740 0, lwc_flag = 0, rwc_flag = 0, iwc_flag = 0, swc_flag = 0, cc_flag = 0;
11741
11742 /* Iterate over all messages... */
11743 for (int i = 0; i < num_messages; i++) {
11744
11745 size_t max_size = LEN;
11746 char short_name[max_size];
11747 size_t value_count;
11748 double *values;
11749
11750 /* Get the current level */
11751 long current_level;
11752 ECC(codes_get_long(handles[i], "level", &current_level));
11753 current_level -= 1;
11754
11755 /* Retrieve data from current message */
11756 ECC(codes_get_string(handles[i], "shortName", short_name, &max_size));
11757 ECC(codes_get_size(handles[i], "values", &value_count));
11758 ALLOC(values, double,
11759 value_count);
11760 ECC(codes_get_double_array(handles[i], "values", values, &value_count));
11761
11762 /* Read temperature... */
11763 ECC_READ_3D("t", current_level, met->t, 1.0, t_flag);
11764
11765 /* Read horizontal wind and vertical velocity... */
11766 ECC_READ_3D("u", current_level, met->u, 1.0, u_flag);
11767 ECC_READ_3D("v", current_level, met->v, 1.0, v_flag);
11768 ECC_READ_3D("w", current_level, met->w, 0.01f, w_flag);
11769
11770 /* Read water vapor and ozone... */
11771 ECC_READ_3D("q", current_level, met->h2o, (float) (MA / MH2O), h2o_flag);
11772 ECC_READ_3D("o3", current_level, met->o3, (float) (MA / MO3), o3_flag);
11773
11774 /* Read cloud data... */
11775 ECC_READ_3D("clwc", current_level, met->lwc, 1.0, lwc_flag);
11776 ECC_READ_3D("crwc", current_level, met->rwc, 1.0, rwc_flag);
11777 ECC_READ_3D("ciwc", current_level, met->iwc, 1.0, iwc_flag);
11778 ECC_READ_3D("cswc", current_level, met->swc, 1.0, swc_flag);
11779 ECC_READ_3D("cc", current_level, met->cc, 1.0, cc_flag);
11780
11781 /*Free allocated array */
11782 free(values);
11783 }
11784
11785 /* Check whether data were found... */
11786 if (t_flag != met->npl)
11787 ERRMSG("Cannot read temperature!");
11788 if (u_flag != met->npl)
11789 ERRMSG("Cannot read zonal wind!");
11790 if (v_flag != met->npl)
11791 ERRMSG("Cannot read meridional wind!");
11792 if (w_flag != met->npl)
11793 WARN("Cannot read vertical velocity!");
11794 if (h2o_flag != met->npl)
11795 WARN("Cannot read specific humidity!");
11796 if (o3_flag != met->npl)
11797 WARN("Cannot read ozone data!");
11798 if (lwc_flag != met->npl)
11799 WARN("Cannot read cloud liquid water content!");
11800 if (rwc_flag != met->npl)
11801 WARN("Cannot read cloud rain water content!");
11802 if (iwc_flag != met->npl)
11803 WARN("Cannot read cloud ice water content!");
11804 if (swc_flag != met->npl)
11805 WARN("Cannot read cloud snow water content!");
11806 if (cc_flag != met->npl)
11807 WARN("Cannot read cloud cover!");
11808
11809 /* Check ordering of pressure levels... */
11810 for (int ix = 0; ix < met->nx; ix++)
11811 for (int iy = 0; iy < met->ny; iy++)
11812 for (int ip = 1; ip < met->np; ip++)
11813 if ((met->pl[ix][iy][0] > met->pl[ix][iy][1]
11814 && met->pl[ix][iy][ip - 1] <= met->pl[ix][iy][ip])
11815 || (met->pl[ix][iy][0] < met->pl[ix][iy][1]
11816 && met->pl[ix][iy][ip - 1] >= met->pl[ix][iy][ip])) {
11817 LOG(1, "%f %f %f %f", met->pl[ix][iy][0], met->pl[ix][iy][1],
11818 met->pl[ix][iy][ip - 1], met->pl[ix][iy][ip]);
11819 ERRMSG("Pressure profiles are not monotonic!");
11820 }
11821
11822 /* Interpolate from model levels to pressure levels... */
11823 if (ctl->met_np > 0) {
11824 met->np = ctl->met_np;
11825
11826 /* Interpolate variables... */
11827 read_met_ml2pl(ctl, met, met->t, "T");
11828 read_met_ml2pl(ctl, met, met->u, "U");
11829 read_met_ml2pl(ctl, met, met->v, "V");
11830 read_met_ml2pl(ctl, met, met->w, "W");
11831 read_met_ml2pl(ctl, met, met->h2o, "H2O");
11832 read_met_ml2pl(ctl, met, met->o3, "O3");
11833 read_met_ml2pl(ctl, met, met->lwc, "LWC");
11834 read_met_ml2pl(ctl, met, met->rwc, "RWC");
11835 read_met_ml2pl(ctl, met, met->iwc, "IWC");
11836 read_met_ml2pl(ctl, met, met->swc, "SWC");
11837 read_met_ml2pl(ctl, met, met->cc, "CC");
11838
11839 /* Set new pressure levels... */
11840 for (int ip = 0; ip < met->np; ip++)
11841 met->p[ip] = ctl->met_p[ip];
11842 }
11843
11844 /* Check ordering of pressure levels... */
11845 for (int ip = 1; ip < met->np; ip++)
11846 if (met->p[ip - 1] < met->p[ip])
11847 ERRMSG("Pressure levels must be descending!");
11848}
11849#endif
11850
11851/*****************************************************************************/
11852
11853#ifdef ECCODES
11855 codes_handle **handles,
11856 const int num_messages,
11857 const ctl_t *ctl,
11858 met_t *met) {
11859
11860 /* Set timer... */
11861 SELECT_TIMER("READ_MET_GRIB_SURFACE", "INPUT");
11862 LOG(2, "Read surface data...");
11863
11864 /* Init... */
11865 int sp_flag = 0, z_flag = 0, t_flag = 0, u_flag = 0, v_flag = 0, ess_flag =
11866 0, nss_flag = 0, shf_flag = 0, lsm_flag = 0, sst_flag = 0, cape_flag = 0,
11867 cin_flag = 0, pbl_flag = 0;
11868
11869 /* Iterate over all messages... */
11870 for (int i = 0; i < num_messages; i++) {
11871
11872 size_t max_size = LEN, value_count;
11873
11874 char short_name[max_size];
11875
11876 /* Store values with shortname... */
11877 ECC(codes_get_string(handles[i], "shortName", short_name, &max_size));
11878 ECC(codes_get_size(handles[i], "values", &value_count));
11879 double *values = (double *) malloc(value_count * sizeof(double));
11880 ECC(codes_get_double_array(handles[i], "values", values, &value_count));
11881
11882 /*Read surface pressure... */
11883 ECC_READ_2D("sp", met->ps, 0.01f, sp_flag);
11884
11885 /*Read geopotential height at the surface... */
11886 ECC_READ_2D("z", met->zs, (float) (1. / (1000. * G0)), z_flag);
11887
11888 /* Read temperature at the surface... */
11889 ECC_READ_2D("2t", met->ts, 1.0f, t_flag);
11890
11891 /* Read zonal wind at the surface... */
11892 ECC_READ_2D("10u", met->us, 1.0f, u_flag);
11893
11894 /* Read meridional wind at the surface... */
11895 ECC_READ_2D("10v", met->vs, 1.0f, v_flag);
11896
11897 /* Read eastward turbulent surface stress... */
11898 ECC_READ_2D("iews", met->ess, 1.0f, ess_flag);
11899
11900 /* Read northward turbulent surface stress... */
11901 ECC_READ_2D("inss", met->nss, 1.0f, nss_flag);
11902
11903 /* Read surface sensible heat flux... */
11904 ECC_READ_2D("ishf", met->shf, 1.0f, shf_flag);
11905
11906 /* Read land-sea mask... */
11907 ECC_READ_2D("lsm", met->lsm, 1.0f, lsm_flag);
11908
11909 /* Read sea surface temperature... */
11910 ECC_READ_2D("sst", met->sst, 1.0f, sst_flag);
11911 if (ctl->met_cape == 0) {
11912
11913 /* Read CAPE... */
11914 ECC_READ_2D("cape", met->cape, 1.0f, cape_flag);
11915
11916 /* Read CIN... */
11917 ECC_READ_2D("cin", met->cin, 1.0f, cin_flag);
11918 }
11919
11920 /* Read PBL... */
11921 if (ctl->met_pbl == 1)
11922 ECC_READ_2D("blh", met->pbl, 0.001f, pbl_flag);
11923 }
11924
11925 /* Check whether data have been read... */
11926 if (sp_flag == 0)
11927 WARN("Cannot read surface pressure data!");
11928 if (z_flag == 0)
11929 WARN("Cannot read surface geopotential height!");
11930 if (t_flag == 0)
11931 WARN("Cannot read surface temperature!");
11932 if (u_flag == 0)
11933 WARN("Cannot read surface zonal wind!");
11934 if (v_flag == 0)
11935 WARN("Cannot read surface meridional wind!");
11936 if (ess_flag == 0)
11937 WARN("Cannot read eastward turbulent surface stress!");
11938 if (nss_flag == 0)
11939 WARN("Cannot read northward turbulent surface stress!");
11940 if (shf_flag == 0)
11941 WARN("Cannot read surface sensible heat flux!");
11942 if (lsm_flag == 0)
11943 WARN("Cannot read land-sea mask!");
11944 if (sst_flag == 0)
11945 WARN("Cannot read sea surface temperature!");
11946 if (ctl->met_cape == 0) {
11947 if (cape_flag == 0)
11948 WARN("Cannot read CAPE!");
11949 if (cin_flag == 0)
11950 WARN("Cannot read convective inhibition!");
11951 }
11952 if (ctl->met_pbl == 1 && pbl_flag == 0)
11953 WARN("Cannot read planetary boundary layer height!");
11954}
11955#endif
11956
11957/*****************************************************************************/
11958
11960 const ctl_t *ctl,
11961 const met_t *met,
11962 float var[EX][EY][EP],
11963 const char *varname) {
11964
11965 double aux[EP], p[EP];
11966
11967 /* Set timer... */
11968 SELECT_TIMER("READ_MET_ML2PL", "METPROC");
11969 LOG(2, "Interpolate meteo data to pressure levels: %s", varname);
11970
11971 /* Loop over columns... */
11972#pragma omp parallel for default(shared) private(aux,p) collapse(2)
11973 for (int ix = 0; ix < met->nx; ix++)
11974 for (int iy = 0; iy < met->ny; iy++) {
11975
11976 /* Copy pressure profile... */
11977 for (int ip = 0; ip < met->np; ip++)
11978 p[ip] = met->pl[ix][iy][ip];
11979
11980 /* Interpolate... */
11981 for (int ip = 0; ip < ctl->met_np; ip++) {
11982 double pt = ctl->met_p[ip];
11983 if ((pt > p[0] && p[0] > p[1]) || (pt < p[0] && p[0] < p[1]))
11984 pt = p[0];
11985 else if ((pt > p[met->np - 1] && p[1] > p[0])
11986 || (pt < p[met->np - 1] && p[1] < p[0]))
11987 pt = p[met->np - 1];
11988 const int ip2 = locate_irr(p, met->np, pt);
11989 aux[ip] = LIN(p[ip2], var[ix][iy][ip2],
11990 p[ip2 + 1], var[ix][iy][ip2 + 1], pt);
11991 }
11992
11993 /* Copy data... */
11994 for (int ip = 0; ip < ctl->met_np; ip++)
11995 var[ix][iy][ip] = (float) aux[ip];
11996 }
11997}
11998
11999/*****************************************************************************/
12000
12002 const ctl_t *ctl,
12003 met_t *met) {
12004
12005 /* Check parameters... */
12006 if (ctl->advect_vert_coord != 1)
12007 return;
12008
12009 /* Set timer... */
12010 SELECT_TIMER("READ_MET_MONOTONIZE", "METPROC");
12011 LOG(2, "Make zeta profiles monotone...");
12012
12013 /* Create monotone zeta profiles... */
12014#pragma omp parallel for default(shared) collapse(2)
12015 for (int i = 0; i < met->nx; i++)
12016 for (int j = 0; j < met->ny; j++) {
12017 int k = 1;
12018
12019 while (k < met->npl) { /* Check if there is an inversion at level k... */
12020 if ((met->zetal[i][j][k - 1] >= met->zetal[i][j][k])) {
12021 /* Find the upper level k+l over the inversion... */
12022 int l = 0;
12023 do {
12024 l++;
12025 }
12026 while ((met->zetal[i][j][k - 1] >=
12027 met->zetal[i][j][k + l]) & (k + l < met->npl));
12028
12029 /* Interpolate linear between the top and bottom
12030 of the inversion... */
12031 float s =
12032 (float) (met->zetal[i][j][k + l] - met->zetal[i][j][k - 1])
12033 / (float) (met->hybrid[k + l] - met->hybrid[k - 1]);
12034
12035 for (int m = k; m < k + l; m++) {
12036 float d = (float) (met->hybrid[m] - met->hybrid[k - 1]);
12037 met->zetal[i][j][m] = s * d + met->zetal[i][j][k - 1];
12038 }
12039
12040 /* Search for more inversions above the last inversion ... */
12041 k = k + l;
12042 } else {
12043 k++;
12044 }
12045 }
12046 }
12047
12048 /* Create monotone pressure profiles... */
12049#pragma omp parallel for default(shared) collapse(2)
12050 for (int i = 0; i < met->nx; i++)
12051 for (int j = 0; j < met->ny; j++) {
12052 int k = 1;
12053
12054 while (k < met->npl) { /* Check if there is an inversion at level k... */
12055 if ((met->pl[i][j][k - 1] <= met->pl[i][j][k])) {
12056
12057 /* Find the upper level k+l over the inversion... */
12058 int l = 0;
12059 do {
12060 l++;
12061 }
12062 while ((met->pl[i][j][k - 1] <= met->pl[i][j][k + l]) & (k + l <
12063 met->npl));
12064
12065 /* Interpolate linear between the top and bottom
12066 of the inversion... */
12067 float s = (float) (met->pl[i][j][k + l] - met->pl[i][j][k - 1])
12068 / (float) (met->hybrid[k + l] - met->hybrid[k - 1]);
12069
12070 for (int m = k; m < k + l; m++) {
12071 float d = (float) (met->hybrid[m] - met->hybrid[k - 1]);
12072 met->pl[i][j][m] = s * d + met->pl[i][j][k - 1];
12073 }
12074
12075 /* Search for more inversions above the last inversion ... */
12076 k += l;
12077 } else {
12078 k++;
12079 }
12080 }
12081 }
12082}
12083
12084/*****************************************************************************/
12085
12087 const char *filename,
12088 const ctl_t *ctl,
12089 met_t *met,
12090 dd_t *dd) {
12091
12092 int ncid = 0;
12093 int have_ncid = 0;
12094
12095 /* With domain decomposition, all tasks collectively open the file in
12096 parallel mode and each task reads its own subdomain... */
12097#ifdef DD
12098 if (ctl->dd) {
12099 NC(nc_open_par
12100 (filename, NC_NOWRITE | NC_SHARE, MPI_COMM_WORLD, MPI_INFO_NULL,
12101 &ncid));
12102 have_ncid = 1;
12103 }
12104#endif
12105
12106 /* Without domain decomposition, the file is opened in serial mode... */
12107 if (!have_ncid && nc_open(filename, NC_NOWRITE, &ncid) != NC_NOERR) {
12108 WARN("Cannot open file!");
12109 return 0;
12110 }
12111
12112 /* Set coordinate system of meteo data... */
12113 met->coord_type = ctl->met_coord_type;
12114
12115 /* Read coordinates of meteo data... */
12116 read_met_nc_grid(filename, ncid, ctl, met, dd);
12117
12118 /* Setup for global read mode happens under the general input timer... */
12119 SELECT_TIMER("GET_MET", "INPUT");
12120#ifdef DD
12121 if (ctl->dd && ctl->met_nc_read_mode == 1)
12122 met_global_2d_init(dd, met);
12123#endif
12124
12125 /* Read surface data... */
12126 read_met_nc_surface(ncid, ctl, met, dd);
12127
12128 /* Read meteo data on vertical levels... */
12129 read_met_nc_levels(ncid, ctl, met, dd);
12130
12131 /* The remaining work happens under the general input timer... */
12132 SELECT_TIMER("GET_MET", "INPUT");
12133
12134#ifdef DD
12135 if (ctl->dd && ctl->met_nc_read_mode == 1)
12136 met_global_2d_free();
12137#endif
12138
12139 /* Close file... */
12140 NC(nc_close(ncid));
12141
12142 /* Return success... */
12143 return 1;
12144}
12145
12146/*****************************************************************************/
12147
12149 dd_t *dd,
12150 const ctl_t *ctl,
12151 met_t *met,
12152 const int ncid) {
12153
12154 int varid;
12155
12156 /* Get the MPI information... */
12157 int rank = 0, size = 1;
12158#ifdef MPI
12159 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
12160 MPI_Comm_size(MPI_COMM_WORLD, &size);
12161#endif
12162
12163 /* Get grid dimensions... */
12164 NC_INQ_DIM("lon", &dd->nx_glob, 0, 0, 0);
12165 NC_INQ_DIM("lat", &dd->ny_glob, 0, 0, 0);
12166
12167 LOG(2, "Number of longitudes: %d", dd->nx_glob);
12168 LOG(2, "Number of latitudes: %d", dd->ny_glob);
12169
12170 /* Check grid... */
12171 if (dd->nx_glob > DD_EX_GLOB || dd->ny_glob > DD_EY_GLOB)
12172 ERRMSG("Global grid is too large!");
12173
12174 if (ctl->dd_subdomains_zonal > dd->nx_glob)
12175 ERRMSG("Too many zonal subdomains for global x grid!");
12176
12177 if (ctl->dd_subdomains_meridional > dd->ny_glob)
12178 ERRMSG("Too many meridional subdomains for global y grid!");
12179
12180 /* Read global longitudes and latitudes... */
12181 NC_GET_DOUBLE("lon", dd->lon_glob, 1);
12182 NC_GET_DOUBLE("lat", dd->lat_glob, 1);
12183
12184 LOG(2, "Longitudes: %g, %g ... %g deg",
12185 dd->lon_glob[0], dd->lon_glob[1], dd->lon_glob[dd->nx_glob - 1]);
12186 LOG(2, "Latitudes: %g, %g ... %g deg",
12187 dd->lat_glob[0], dd->lat_glob[1], dd->lat_glob[dd->ny_glob - 1]);
12188
12189 /* Rank coordinates in DD layout... */
12190 const int zonal_rank = rank / ctl->dd_subdomains_meridional;
12191 const int merid_rank = rank % ctl->dd_subdomains_meridional;
12192
12193 /* Check for edge cases... */
12194 const int left = (zonal_rank == 0);
12195 const int right = (zonal_rank == ctl->dd_subdomains_zonal - 1);
12196 const int top = (merid_rank == 0);
12197 const int bottom = (merid_rank == ctl->dd_subdomains_meridional - 1);
12198
12199 /* Core owned block (without halos)... */
12200 const int nx_block = dd->nx_glob / ctl->dd_subdomains_zonal;
12201 const int ny_block = dd->ny_glob / ctl->dd_subdomains_meridional;
12202
12203 const int ix0 = zonal_rank * nx_block;
12204 const int iy0 = merid_rank * ny_block;
12205
12206 int nx_core = nx_block;
12207 int ny_core = ny_block;
12208
12209 if (right)
12210 nx_core += dd->nx_glob - ctl->dd_subdomains_zonal * nx_block;
12211 if (bottom)
12212 ny_core += dd->ny_glob - ctl->dd_subdomains_meridional * ny_block;
12213
12214 /* Check halo extents before converting grid indices to size_t... */
12215 const int halo = ctl->dd_halos_size;
12216 if ((!left && halo > ix0)
12217 || (!right && halo > dd->nx_glob - ix0 - nx_core)
12218 || (!top && halo > iy0)
12219 || (!bottom && halo > dd->ny_glob - iy0 - ny_core))
12220 ERRMSG("DD_HALOS_SIZE exceeds the available global grid!");
12221
12222 const int nx_local = nx_core
12223 + (ctl->dd_subdomains_zonal > 1 ? 2 * halo : 0);
12224 const int ny_local = ny_core + (!top ? halo : 0) + (!bottom ? halo : 0);
12225 if (nx_local > EX || ny_local > EY)
12226 ERRMSG("DD halo grid exceeds EX or EY. Increase the grid capacity!");
12227 if (nx_local < 2 || ny_local < 2)
12228 ERRMSG("DD halo grid needs at least two points in each direction!");
12229
12230 /* Store core met size first... */
12231 met->nx = nx_core;
12232 met->ny = ny_core;
12233
12234 /* Set hyperslab for core subdomain... */
12235 dd->subdomain_start[0] = 0;
12236 dd->subdomain_start[1] = 0;
12237 dd->subdomain_start[2] = (size_t) iy0;
12238 dd->subdomain_start[3] = (size_t) ix0;
12239
12240 dd->subdomain_count[0] = 1;
12241 dd->subdomain_count[1] = (size_t) met->np;
12242 dd->subdomain_count[2] = (size_t) ny_core;
12243 dd->subdomain_count[3] = (size_t) nx_core;
12244
12245 /* Add inner halos to read window... */
12246 if (!left && !right) {
12247 dd->subdomain_start[3] -= (size_t) ctl->dd_halos_size;
12248 dd->subdomain_count[3] += (size_t) (2 * ctl->dd_halos_size);
12249 } else if (left ^ right) {
12250 dd->subdomain_count[3] += (size_t) ctl->dd_halos_size;
12251 if (!left)
12252 dd->subdomain_start[3] -= (size_t) ctl->dd_halos_size;
12253 }
12254
12255 if (!top && !bottom) {
12256 dd->subdomain_start[2] -= (size_t) ctl->dd_halos_size;
12257 dd->subdomain_count[2] += (size_t) (2 * ctl->dd_halos_size);
12258 } else if (top ^ bottom) {
12259 dd->subdomain_count[2] += (size_t) ctl->dd_halos_size;
12260 if (!top)
12261 dd->subdomain_start[2] -= (size_t) ctl->dd_halos_size;
12262 }
12263
12264 /* Set boundary halo hyperslabs... */
12265 double lon_shift = 0.0;
12266
12267 if (left ^ right) {
12268 dd->halo_bnd_start[0] = 0;
12269 dd->halo_bnd_start[1] = 0;
12270 dd->halo_bnd_start[2] = dd->subdomain_start[2];
12271 dd->halo_bnd_start[3] =
12272 (size_t) (left ? (dd->nx_glob - ctl->dd_halos_size) : 0);
12273
12274 dd->halo_bnd_count[0] = 1;
12275 dd->halo_bnd_count[1] = (size_t) met->np;
12276 dd->halo_bnd_count[2] = dd->subdomain_count[2];
12277 dd->halo_bnd_count[3] = (size_t) ctl->dd_halos_size;
12278
12279 dd->halo_offset_start = left ? (int) dd->halo_bnd_count[3] : 0;
12280 dd->halo_offset_end = left ? 0 : (int) dd->subdomain_count[3];
12281 lon_shift = left ? -360.0 : 360.0;
12282 } else {
12283 dd->halo_bnd_start[0] = 0;
12284 dd->halo_bnd_start[1] = 0;
12285 dd->halo_bnd_start[2] = 0;
12286 dd->halo_bnd_start[3] = 0;
12287
12288 dd->halo_bnd_count[0] = 0;
12289 dd->halo_bnd_count[1] = 0;
12290 dd->halo_bnd_count[2] = 0;
12291 dd->halo_bnd_count[3] = 0;
12292
12293 dd->halo_offset_start = 0;
12294 dd->halo_offset_end = 0;
12295 }
12296
12297 /* Focus on subdomain latitudes... */
12298 for (int iy = 0; iy < (int) dd->subdomain_count[2]; iy++)
12299 met->lat[iy] = dd->lat_glob[(int) dd->subdomain_start[2] + iy];
12300
12301 /* Focus on subdomain longitudes... */
12302 for (int ix = 0; ix < (int) dd->subdomain_count[3]; ix++)
12303 met->lon[ix + dd->halo_offset_start] =
12304 dd->lon_glob[(int) dd->subdomain_start[3] + ix];
12305
12306 for (int ix = 0; ix < (int) dd->halo_bnd_count[3]; ix++)
12307 met->lon[ix + dd->halo_offset_end] =
12308 dd->lon_glob[(int) dd->halo_bnd_start[3] + ix] + lon_shift;
12309
12310 /* Reset halo-extended grid dimensions... */
12311 met->nx = (int) dd->subdomain_count[3] + (int) dd->halo_bnd_count[3];
12312 met->ny = (int) dd->subdomain_count[2];
12313
12314 LOG(2, "Define subdomain properties.");
12315 LOG(2, "MPI information: Rank %d, Size %d", rank, size);
12316 LOG(2, "Edge position: l=%d,r=%d,t=%d,b=%d", left, right, top, bottom);
12317 LOG(2, "Total size for subdomain meteo data: nx %d ny %d np %d",
12318 met->nx, met->ny, met->np);
12319 LOG(2, "Hyperslab sizes for boundary halos: nx %d ny %d np %d",
12320 (int) dd->halo_bnd_count[3], (int) dd->halo_bnd_count[2],
12321 (int) dd->halo_bnd_count[1]);
12322 LOG(2, "Hyperslab sizes for subdomain and inner halos: nx %d ny %d np %d",
12323 (int) dd->subdomain_count[3], (int) dd->subdomain_count[2],
12324 (int) dd->subdomain_count[1]);
12325 LOG(2, "Subdomain start: nx %ld ny %ld np %ld",
12326 dd->subdomain_start[3], dd->subdomain_start[2], dd->subdomain_start[1]);
12327 LOG(2, "Boundary halo start: nx %ld ny %ld np %ld",
12328 dd->halo_bnd_start[3], dd->halo_bnd_start[2], dd->halo_bnd_start[1]);
12329 LOG(2, "Offsets: nx %d ny %d", dd->halo_offset_start, dd->halo_offset_end);
12330 LOG(2, "%d Subdomain longitudes: %g, %g ... %g deg",
12331 rank, met->lon[0], met->lon[1], met->lon[met->nx - 1]);
12332 LOG(2, "%d Subdomain latitudes: %g, %g ... %g deg",
12333 rank, met->lat[0], met->lat[1], met->lat[met->ny - 1]);
12334}
12335
12336/*****************************************************************************/
12337
12339 const ctl_t *ctl,
12340 met_t *met) {
12341
12342 /* Set timer... */
12343 SELECT_TIMER("READ_MET_PBL", "METPROC");
12344 LOG(2, "Calculate planetary boundary layer...");
12345
12346 /* Convert PBL height from meteo file to pressure... */
12347 if (ctl->met_pbl == 1) {
12348
12349 /* Loop over grid points... */
12350#pragma omp parallel for default(shared) collapse(2)
12351 for (int ix = 0; ix < met->nx; ix++)
12352 for (int iy = 0; iy < met->ny; iy++) {
12353
12354 /* Get pressure at top of PBL... */
12355 const float z = met->zs[ix][iy] + met->pbl[ix][iy];
12356 const int ip = locate_irr_float(met->z[ix][iy], met->np, z, 0);
12357 met->pbl[ix][iy] =
12358 (float) (LIN(met->z[ix][iy][ip], met->p[ip],
12359 met->z[ix][iy][ip + 1], met->p[ip + 1], z));
12360 }
12361 }
12362
12363 /* Determine PBL based on Richardson number... */
12364 else if (ctl->met_pbl == 2) {
12365
12366 /* Parameters used to estimate the height of the PBL
12367 (e.g., Vogelezang and Holtslag, 1996; Seidel et al., 2012)... */
12368 const double rib_crit = 0.25, dz = 0.05, umin = 5.0;
12369
12370 /* Loop over grid points... */
12371#pragma omp parallel for default(shared) collapse(2)
12372 for (int ix = 0; ix < met->nx; ix++)
12373 for (int iy = 0; iy < met->ny; iy++) {
12374
12375 /* Set bottom level of PBL... */
12376 const double pbl_bot = met->ps[ix][iy] * exp(-dz / H0);
12377
12378 /* Find lowest level near the bottom... */
12379 int ip;
12380 for (ip = 1; ip < met->np; ip++)
12381 if (met->p[ip] < pbl_bot)
12382 break;
12383
12384 /* Get near surface data... */
12385 const double h2os = LIN(met->p[ip - 1], met->h2o[ix][iy][ip - 1],
12386 met->p[ip], met->h2o[ix][iy][ip], pbl_bot);
12387 const double tvs = THETAVIRT(pbl_bot, met->ts[ix][iy], h2os);
12388
12389 /* Init... */
12390 double rib_old = 0;
12391
12392 /* Loop over levels... */
12393 for (; ip < met->np; ip++) {
12394
12395 /* Get squared horizontal wind speed... */
12396 double vh2 = SQR(met->u[ix][iy][ip] - met->us[ix][iy])
12397 + SQR(met->v[ix][iy][ip] - met->vs[ix][iy]);
12398 vh2 = MAX(vh2, SQR(umin));
12399
12400 /* Calculate bulk Richardson number... */
12401 const double rib =
12402 G0 * 1e3 * (met->z[ix][iy][ip] - met->zs[ix][iy]) / tvs
12403 * (THETAVIRT(met->p[ip], met->t[ix][iy][ip],
12404 met->h2o[ix][iy][ip]) - tvs) / vh2;
12405
12406 /* Check for critical value... */
12407 if (rib >= rib_crit) {
12408 met->pbl[ix][iy] = (float) (LIN(rib_old, met->p[ip - 1],
12409 rib, met->p[ip], rib_crit));
12410 if (met->pbl[ix][iy] > pbl_bot)
12411 met->pbl[ix][iy] = (float) pbl_bot;
12412 break;
12413 }
12414
12415 /* Save Richardson number... */
12416 rib_old = rib;
12417 }
12418 }
12419 }
12420
12421 /* Determine PBL based on potential temperature... */
12422 if (ctl->met_pbl == 3) {
12423
12424 /* Parameters used to estimate the height of the PBL
12425 (following HYSPLIT model)... */
12426 const double dtheta = 2.0, zmin = 0.1;
12427
12428 /* Loop over grid points... */
12429#pragma omp parallel for default(shared) collapse(2)
12430 for (int ix = 0; ix < met->nx; ix++)
12431 for (int iy = 0; iy < met->ny; iy++) {
12432
12433 /* Potential temperature at the surface... */
12434 const double theta0 = THETA(met->ps[ix][iy], met->ts[ix][iy]);
12435
12436 /* Find topmost level where theta exceeds surface value by 2 K... */
12437 int ip;
12438 for (ip = met->np - 2; ip > 0; ip--)
12439 if (met->p[ip] >= 300.)
12440 if (met->p[ip] > met->ps[ix][iy]
12441 || THETA(met->p[ip], met->t[ix][iy][ip]) <= theta0 + dtheta)
12442 break;
12443
12444 /* Interpolate... */
12445 met->pbl[ix][iy]
12446 = (float) (LIN(THETA(met->p[ip + 1], met->t[ix][iy][ip + 1]),
12447 met->p[ip + 1],
12448 THETA(met->p[ip], met->t[ix][iy][ip]),
12449 met->p[ip], theta0 + dtheta));
12450
12451 /* Check minimum value... */
12452 double pbl_min = met->ps[ix][iy] * exp(-zmin / H0);
12453 if (met->pbl[ix][iy] > pbl_min || met->p[ip] > met->ps[ix][iy])
12454 met->pbl[ix][iy] = (float) pbl_min;
12455 }
12456 }
12457
12458 /* Loop over grid points... */
12459#pragma omp parallel for default(shared) collapse(2)
12460 for (int ix = 0; ix < met->nx; ix++)
12461 for (int iy = 0; iy < met->ny; iy++) {
12462
12463 /* Check minimum value... */
12464 double pbl_min = met->ps[ix][iy] * exp(-ctl->met_pbl_min / H0);
12465 met->pbl[ix][iy] = MIN(met->pbl[ix][iy], (float) pbl_min);
12466
12467 /* Check maximum value... */
12468 double pbl_max = met->ps[ix][iy] * exp(-ctl->met_pbl_max / H0);
12469 met->pbl[ix][iy] = MAX(met->pbl[ix][iy], (float) pbl_max);
12470 }
12471}
12472
12473/*****************************************************************************/
12474
12476 met_t *met) {
12477
12478 /* Set timer... */
12479 SELECT_TIMER("READ_MET_PERIODIC", "METPROC");
12480 LOG(2, "Apply periodic boundary conditions...");
12481
12482 /* Check longitudes... */
12483 if (!(fabs(met->lon[met->nx - 1] - met->lon[0]
12484 + met->lon[1] - met->lon[0] - 360) < 0.01))
12485 return;
12486
12487 /* Increase longitude counter... */
12488 if ((++met->nx) >= EX)
12489 ERRMSG("Cannot create periodic boundary conditions!");
12490
12491 /* Set longitude... */
12492 met->lon[met->nx - 1] = met->lon[met->nx - 2] + met->lon[1] - met->lon[0];
12493
12494 /* Loop over latitudes and pressure levels... */
12495#pragma omp parallel for default(shared)
12496 for (int iy = 0; iy < met->ny; iy++) {
12497 met->ps[met->nx - 1][iy] = met->ps[0][iy];
12498 met->zs[met->nx - 1][iy] = met->zs[0][iy];
12499 met->ts[met->nx - 1][iy] = met->ts[0][iy];
12500 met->us[met->nx - 1][iy] = met->us[0][iy];
12501 met->vs[met->nx - 1][iy] = met->vs[0][iy];
12502 met->ess[met->nx - 1][iy] = met->ess[0][iy];
12503 met->nss[met->nx - 1][iy] = met->nss[0][iy];
12504 met->shf[met->nx - 1][iy] = met->shf[0][iy];
12505 met->lsm[met->nx - 1][iy] = met->lsm[0][iy];
12506 met->sst[met->nx - 1][iy] = met->sst[0][iy];
12507 met->pbl[met->nx - 1][iy] = met->pbl[0][iy];
12508 met->cape[met->nx - 1][iy] = met->cape[0][iy];
12509 met->cin[met->nx - 1][iy] = met->cin[0][iy];
12510 for (int ip = 0; ip < met->np; ip++) {
12511 met->t[met->nx - 1][iy][ip] = met->t[0][iy][ip];
12512 met->u[met->nx - 1][iy][ip] = met->u[0][iy][ip];
12513 met->v[met->nx - 1][iy][ip] = met->v[0][iy][ip];
12514 met->w[met->nx - 1][iy][ip] = met->w[0][iy][ip];
12515 met->h2o[met->nx - 1][iy][ip] = met->h2o[0][iy][ip];
12516 met->o3[met->nx - 1][iy][ip] = met->o3[0][iy][ip];
12517 met->lwc[met->nx - 1][iy][ip] = met->lwc[0][iy][ip];
12518 met->rwc[met->nx - 1][iy][ip] = met->rwc[0][iy][ip];
12519 met->iwc[met->nx - 1][iy][ip] = met->iwc[0][iy][ip];
12520 met->swc[met->nx - 1][iy][ip] = met->swc[0][iy][ip];
12521 met->cc[met->nx - 1][iy][ip] = met->cc[0][iy][ip];
12522 }
12523 for (int ip = 0; ip < met->npl; ip++) {
12524 met->ul[met->nx - 1][iy][ip] = met->ul[0][iy][ip];
12525 met->vl[met->nx - 1][iy][ip] = met->vl[0][iy][ip];
12526 met->wl[met->nx - 1][iy][ip] = met->wl[0][iy][ip];
12527 met->pl[met->nx - 1][iy][ip] = met->pl[0][iy][ip];
12528 met->zetal[met->nx - 1][iy][ip] = met->zetal[0][iy][ip];
12529 met->zeta_dotl[met->nx - 1][iy][ip] = met->zeta_dotl[0][iy][ip];
12530 }
12531 }
12532}
12533
12534/*****************************************************************************/
12535
12537 met_t *met) {
12538
12539 /* Set timer... */
12540 SELECT_TIMER("READ_MET_POLAR_WINDS", "METPROC");
12541 LOG(2, "Apply fix for polar winds...");
12542
12543 if (met->coord_type != 0)
12544 return;
12545
12546 /* Check latitudes... */
12547 if (fabs(met->lat[0]) < 89.999 || fabs(met->lat[met->ny - 1]) < 89.999)
12548 return;
12549
12550 /* Loop over hemispheres... */
12551 for (int ihem = 0; ihem < 2; ihem++) {
12552
12553 /* Set latitude indices... */
12554 int i89 = 1, i90 = 0, sign = 1;
12555 if (ihem == 1) {
12556 i89 = met->ny - 2;
12557 i90 = met->ny - 1;
12558 }
12559 if (met->lat[i90] < 0)
12560 sign = -1;
12561
12562 /* Look-up table of cosinus and sinus... */
12563 double clon[EX], slon[EX];
12564#pragma omp parallel for default(shared)
12565 for (int ix = 0; ix < met->nx; ix++) {
12566 clon[ix] = cos(sign * DEG2RAD(met->lon[ix]));
12567 slon[ix] = sin(sign * DEG2RAD(met->lon[ix]));
12568 }
12569
12570 /* Loop over levels... */
12571#pragma omp parallel for default(shared)
12572 for (int ip = 0; ip < met->np; ip++) {
12573
12574 /* Transform 89 degree u and v winds into Cartesian coordinates and take the mean... */
12575 double vel89x = 0, vel89y = 0;
12576 for (int ix = 0; ix < met->nx; ix++) {
12577 vel89x +=
12578 (met->u[ix][i89][ip] * clon[ix] -
12579 met->v[ix][i89][ip] * slon[ix]) / met->nx;
12580 vel89y +=
12581 (met->u[ix][i89][ip] * slon[ix] +
12582 met->v[ix][i89][ip] * clon[ix]) / met->nx;
12583 }
12584
12585 /* Replace 90 degree winds by 89 degree mean... */
12586 for (int ix = 0; ix < met->nx; ix++) {
12587 met->u[ix][i90][ip]
12588 = (float) (vel89x * clon[ix] + vel89y * slon[ix]);
12589 met->v[ix][i90][ip]
12590 = (float) (-vel89x * slon[ix] + vel89y * clon[ix]);
12591 }
12592 }
12593 }
12594}
12595
12596/*****************************************************************************/
12597
12599 met_t *met) {
12600
12601 double pows[EP];
12602
12603 /* Set timer... */
12604 SELECT_TIMER("READ_MET_PV", "METPROC");
12605 LOG(2, "Calculate potential vorticity...");
12606
12607 /* Set powers... */
12608#pragma omp parallel for default(shared)
12609 for (int ip = 0; ip < met->np; ip++)
12610 pows[ip] = pow(1000. / met->p[ip], KAPPA);
12611
12612 /* Loop over grid points... */
12613#pragma omp parallel for default(shared)
12614 for (int ix = 0; ix < met->nx; ix++) {
12615
12616 /* Set indices... */
12617 const int ix0 = MAX(ix - 1, 0);
12618 const int ix1 = MIN(ix + 1, met->nx - 1);
12619
12620 /* Loop over grid points... */
12621 for (int iy = 0; iy < met->ny; iy++) {
12622
12623 /* Set indices... */
12624 const int iy0 = MAX(iy - 1, 0);
12625 const int iy1 = MIN(iy + 1, met->ny - 1);
12626
12627 /* Set auxiliary variables... */
12628 const double latr = 0.5 * (met->lat[iy1] + met->lat[iy0]);
12629 double dx, dy, c0, c1, cr, vort;
12630
12631 // Calculate potential vorticity..
12632 if (met->coord_type == 0) { // coords are lat/lon
12633 dx = 1000. * DEG2DX(met->lon[ix1] - met->lon[ix0], latr);
12634 dy = 1000. * DEG2DY(met->lat[iy1] - met->lat[iy0]);
12635 c0 = cos(DEG2RAD(met->lat[iy0]));
12636 c1 = cos(DEG2RAD(met->lat[iy1]));
12637 cr = cos(DEG2RAD(latr));
12638 vort = 2 * OMEGA_EARTH * sin(DEG2RAD(latr));
12639 } else { // coords are in meters
12640 dx = met->lon[ix1] - met->lon[ix0];
12641 dy = met->lat[iy1] - met->lat[iy0];
12642
12643 c0 = 1.0;
12644 c1 = 1.0;
12645 cr = 1.0;
12646
12647 vort = 2 * OMEGA_EARTH * sin(latr / (RE * 1000));
12648 }
12649
12650 /* Loop over grid points... */
12651 for (int ip = 0; ip < met->np; ip++) {
12652
12653 /* Get gradients in longitude... */
12654 const double dtdx
12655 = (met->t[ix1][iy][ip] - met->t[ix0][iy][ip]) * pows[ip] / dx;
12656 const double dvdx = (met->v[ix1][iy][ip] - met->v[ix0][iy][ip]) / dx;
12657
12658 /* Get gradients in latitude... */
12659 const double dtdy
12660 = (met->t[ix][iy1][ip] - met->t[ix][iy0][ip]) * pows[ip] / dy;
12661 const double dudy
12662 = (met->u[ix][iy1][ip] * c1 - met->u[ix][iy0][ip] * c0) / dy;
12663
12664 /* Set indices... */
12665 const int ip0 = MAX(ip - 1, 0);
12666 const int ip1 = MIN(ip + 1, met->np - 1);
12667
12668 /* Get gradients in pressure... */
12669 double dtdp, dudp, dvdp;
12670 const double dp0 = 100. * (met->p[ip] - met->p[ip0]);
12671 const double dp1 = 100. * (met->p[ip1] - met->p[ip]);
12672 if (ip != ip0 && ip != ip1) {
12673 double denom = dp0 * dp1 * (dp0 + dp1);
12674 dtdp = (dp0 * dp0 * met->t[ix][iy][ip1] * pows[ip1]
12675 - dp1 * dp1 * met->t[ix][iy][ip0] * pows[ip0]
12676 + (dp1 * dp1 - dp0 * dp0) * met->t[ix][iy][ip] * pows[ip])
12677 / denom;
12678 dudp = (dp0 * dp0 * met->u[ix][iy][ip1]
12679 - dp1 * dp1 * met->u[ix][iy][ip0]
12680 + (dp1 * dp1 - dp0 * dp0) * met->u[ix][iy][ip])
12681 / denom;
12682 dvdp = (dp0 * dp0 * met->v[ix][iy][ip1]
12683 - dp1 * dp1 * met->v[ix][iy][ip0]
12684 + (dp1 * dp1 - dp0 * dp0) * met->v[ix][iy][ip])
12685 / denom;
12686 } else {
12687 const double denom = dp0 + dp1;
12688 dtdp =
12689 (met->t[ix][iy][ip1] * pows[ip1] -
12690 met->t[ix][iy][ip0] * pows[ip0]) / denom;
12691 dudp = (met->u[ix][iy][ip1] - met->u[ix][iy][ip0]) / denom;
12692 dvdp = (met->v[ix][iy][ip1] - met->v[ix][iy][ip0]) / denom;
12693 }
12694
12695 /* Calculate PV... */
12696 met->pv[ix][iy][ip] = (float)
12697 (1e6 * G0 *
12698 (-dtdp * (dvdx - dudy / cr + vort) + dvdp * dtdx - dudp * dtdy));
12699 }
12700 }
12701 }
12702
12703 /* Fix for polar regions... */
12704#pragma omp parallel for default(shared)
12705 for (int ix = 0; ix < met->nx; ix++)
12706 for (int ip = 0; ip < met->np; ip++) {
12707 met->pv[ix][0][ip]
12708 = met->pv[ix][1][ip]
12709 = met->pv[ix][2][ip];
12710 met->pv[ix][met->ny - 1][ip]
12711 = met->pv[ix][met->ny - 2][ip]
12712 = met->pv[ix][met->ny - 3][ip];
12713 }
12714}
12715
12716/*****************************************************************************/
12717
12719 met_t *met) {
12720
12721 /* Set timer... */
12722 SELECT_TIMER("READ_MET_OZONE", "METPROC");
12723 LOG(2, "Calculate total column ozone...");
12724
12725 /* Loop over columns... */
12726#pragma omp parallel for default(shared) collapse(2)
12727 for (int ix = 0; ix < met->nx; ix++)
12728 for (int iy = 0; iy < met->ny; iy++) {
12729
12730 /* Integrate... */
12731 double cd = 0;
12732 for (int ip = 1; ip < met->np; ip++)
12733 if (met->p[ip - 1] <= met->ps[ix][iy]) {
12734 const double vmr =
12735 0.5 * (met->o3[ix][iy][ip - 1] + met->o3[ix][iy][ip]);
12736 const double dp = met->p[ip - 1] - met->p[ip];
12737 cd += vmr * MO3 / MA * dp * 1e2 / G0;
12738 }
12739
12740 /* Convert to Dobson units... */
12741 met->o3c[ix][iy] = (float) (cd / DOBSON_UNIT);
12742 }
12743}
12744
12745/*****************************************************************************/
12746
12748 const ctl_t *ctl,
12749 met_t *met) {
12750
12751 met_t *help;
12752
12753 /* Check parameters... */
12754 if (ctl->met_dp <= 1 && ctl->met_dx <= 1 && ctl->met_dy <= 1
12755 && ctl->met_sp <= 1 && ctl->met_sx <= 1 && ctl->met_sy <= 1)
12756 return;
12757
12758 /* Set timer... */
12759 SELECT_TIMER("READ_MET_SAMPLE", "METPROC");
12760 LOG(2, "Downsampling of meteo data...");
12761
12762 /* Allocate... */
12763 ALLOC(help, met_t, 1);
12764
12765 /* Copy data... */
12766 help->nx = met->nx;
12767 help->ny = met->ny;
12768 help->np = met->np;
12769 memcpy(help->lon, met->lon, sizeof(met->lon));
12770 memcpy(help->lat, met->lat, sizeof(met->lat));
12771 memcpy(help->p, met->p, sizeof(met->p));
12772
12773 /* Smoothing... */
12774 for (int ix = 0; ix < met->nx; ix += ctl->met_dx) {
12775 for (int iy = 0; iy < met->ny; iy += ctl->met_dy) {
12776 for (int ip = 0; ip < met->np; ip += ctl->met_dp) {
12777 help->ps[ix][iy] = 0;
12778 help->zs[ix][iy] = 0;
12779 help->ts[ix][iy] = 0;
12780 help->us[ix][iy] = 0;
12781 help->vs[ix][iy] = 0;
12782 help->ess[ix][iy] = 0;
12783 help->nss[ix][iy] = 0;
12784 help->shf[ix][iy] = 0;
12785 help->lsm[ix][iy] = 0;
12786 help->sst[ix][iy] = 0;
12787 help->pbl[ix][iy] = 0;
12788 help->cape[ix][iy] = 0;
12789 help->cin[ix][iy] = 0;
12790 help->t[ix][iy][ip] = 0;
12791 help->u[ix][iy][ip] = 0;
12792 help->v[ix][iy][ip] = 0;
12793 help->w[ix][iy][ip] = 0;
12794 help->h2o[ix][iy][ip] = 0;
12795 help->o3[ix][iy][ip] = 0;
12796 help->lwc[ix][iy][ip] = 0;
12797 help->rwc[ix][iy][ip] = 0;
12798 help->iwc[ix][iy][ip] = 0;
12799 help->swc[ix][iy][ip] = 0;
12800 help->cc[ix][iy][ip] = 0;
12801 float wsum = 0;
12802 for (int ix2 = ix - ctl->met_sx + 1; ix2 <= ix + ctl->met_sx - 1;
12803 ix2++) {
12804 int ix3 = ix2;
12805 if (ix3 < 0)
12806 ix3 += met->nx;
12807 else if (ix3 >= met->nx)
12808 ix3 -= met->nx;
12809
12810 for (int iy2 = MAX(iy - ctl->met_sy + 1, 0);
12811 iy2 <= MIN(iy + ctl->met_sy - 1, met->ny - 1); iy2++)
12812 for (int ip2 = MAX(ip - ctl->met_sp + 1, 0);
12813 ip2 <= MIN(ip + ctl->met_sp - 1, met->np - 1); ip2++) {
12814 const float w =
12815 (1.0f - (float) abs(ix - ix2) / (float) ctl->met_sx)
12816 * (1.0f - (float) abs(iy - iy2) / (float) ctl->met_sy)
12817 * (1.0f - (float) abs(ip - ip2) / (float) ctl->met_sp);
12818 help->ps[ix][iy] += w * met->ps[ix3][iy2];
12819 help->zs[ix][iy] += w * met->zs[ix3][iy2];
12820 help->ts[ix][iy] += w * met->ts[ix3][iy2];
12821 help->us[ix][iy] += w * met->us[ix3][iy2];
12822 help->vs[ix][iy] += w * met->vs[ix3][iy2];
12823 help->ess[ix][iy] += w * met->ess[ix3][iy2];
12824 help->nss[ix][iy] += w * met->nss[ix3][iy2];
12825 help->shf[ix][iy] += w * met->shf[ix3][iy2];
12826 help->lsm[ix][iy] += w * met->lsm[ix3][iy2];
12827 help->sst[ix][iy] += w * met->sst[ix3][iy2];
12828 help->pbl[ix][iy] += w * met->pbl[ix3][iy2];
12829 help->cape[ix][iy] += w * met->cape[ix3][iy2];
12830 help->cin[ix][iy] += w * met->cin[ix3][iy2];
12831 help->t[ix][iy][ip] += w * met->t[ix3][iy2][ip2];
12832 help->u[ix][iy][ip] += w * met->u[ix3][iy2][ip2];
12833 help->v[ix][iy][ip] += w * met->v[ix3][iy2][ip2];
12834 help->w[ix][iy][ip] += w * met->w[ix3][iy2][ip2];
12835 help->h2o[ix][iy][ip] += w * met->h2o[ix3][iy2][ip2];
12836 help->o3[ix][iy][ip] += w * met->o3[ix3][iy2][ip2];
12837 help->lwc[ix][iy][ip] += w * met->lwc[ix3][iy2][ip2];
12838 help->rwc[ix][iy][ip] += w * met->rwc[ix3][iy2][ip2];
12839 help->iwc[ix][iy][ip] += w * met->iwc[ix3][iy2][ip2];
12840 help->swc[ix][iy][ip] += w * met->swc[ix3][iy2][ip2];
12841 help->cc[ix][iy][ip] += w * met->cc[ix3][iy2][ip2];
12842 wsum += w;
12843 }
12844 }
12845 help->ps[ix][iy] /= wsum;
12846 help->zs[ix][iy] /= wsum;
12847 help->ts[ix][iy] /= wsum;
12848 help->us[ix][iy] /= wsum;
12849 help->vs[ix][iy] /= wsum;
12850 help->ess[ix][iy] /= wsum;
12851 help->nss[ix][iy] /= wsum;
12852 help->shf[ix][iy] /= wsum;
12853 help->lsm[ix][iy] /= wsum;
12854 help->sst[ix][iy] /= wsum;
12855 help->pbl[ix][iy] /= wsum;
12856 help->cape[ix][iy] /= wsum;
12857 help->cin[ix][iy] /= wsum;
12858 help->t[ix][iy][ip] /= wsum;
12859 help->u[ix][iy][ip] /= wsum;
12860 help->v[ix][iy][ip] /= wsum;
12861 help->w[ix][iy][ip] /= wsum;
12862 help->h2o[ix][iy][ip] /= wsum;
12863 help->o3[ix][iy][ip] /= wsum;
12864 help->lwc[ix][iy][ip] /= wsum;
12865 help->rwc[ix][iy][ip] /= wsum;
12866 help->iwc[ix][iy][ip] /= wsum;
12867 help->swc[ix][iy][ip] /= wsum;
12868 help->cc[ix][iy][ip] /= wsum;
12869 }
12870 }
12871 }
12872
12873 /* Downsampling... */
12874 met->nx = 0;
12875 for (int ix = 0; ix < help->nx; ix += ctl->met_dx) {
12876 met->lon[met->nx] = help->lon[ix];
12877 met->ny = 0;
12878 for (int iy = 0; iy < help->ny; iy += ctl->met_dy) {
12879 met->lat[met->ny] = help->lat[iy];
12880 met->ps[met->nx][met->ny] = help->ps[ix][iy];
12881 met->zs[met->nx][met->ny] = help->zs[ix][iy];
12882 met->ts[met->nx][met->ny] = help->ts[ix][iy];
12883 met->us[met->nx][met->ny] = help->us[ix][iy];
12884 met->vs[met->nx][met->ny] = help->vs[ix][iy];
12885 met->ess[met->nx][met->ny] = help->ess[ix][iy];
12886 met->nss[met->nx][met->ny] = help->nss[ix][iy];
12887 met->shf[met->nx][met->ny] = help->shf[ix][iy];
12888 met->lsm[met->nx][met->ny] = help->lsm[ix][iy];
12889 met->sst[met->nx][met->ny] = help->sst[ix][iy];
12890 met->pbl[met->nx][met->ny] = help->pbl[ix][iy];
12891 met->cape[met->nx][met->ny] = help->cape[ix][iy];
12892 met->cin[met->nx][met->ny] = help->cin[ix][iy];
12893 met->np = 0;
12894 for (int ip = 0; ip < help->np; ip += ctl->met_dp) {
12895 met->p[met->np] = help->p[ip];
12896 met->t[met->nx][met->ny][met->np] = help->t[ix][iy][ip];
12897 met->u[met->nx][met->ny][met->np] = help->u[ix][iy][ip];
12898 met->v[met->nx][met->ny][met->np] = help->v[ix][iy][ip];
12899 met->w[met->nx][met->ny][met->np] = help->w[ix][iy][ip];
12900 met->h2o[met->nx][met->ny][met->np] = help->h2o[ix][iy][ip];
12901 met->o3[met->nx][met->ny][met->np] = help->o3[ix][iy][ip];
12902 met->lwc[met->nx][met->ny][met->np] = help->lwc[ix][iy][ip];
12903 met->rwc[met->nx][met->ny][met->np] = help->rwc[ix][iy][ip];
12904 met->iwc[met->nx][met->ny][met->np] = help->iwc[ix][iy][ip];
12905 met->swc[met->nx][met->ny][met->np] = help->swc[ix][iy][ip];
12906 met->cc[met->nx][met->ny][met->np] = help->cc[ix][iy][ip];
12907 met->np++;
12908 }
12909 met->ny++;
12910 }
12911 met->nx++;
12912 }
12913
12914 /* Free... */
12915 free(help);
12916}
12917
12918/*****************************************************************************/
12919
12921 const ctl_t *ctl,
12922 const clim_t *clim,
12923 met_t *met) {
12924
12925 double p2[200], pv[EP], pv2[200], t[EP], t2[200], th[EP],
12926 th2[200], z[EP], z2[200];
12927
12928 /* Set timer... */
12929 SELECT_TIMER("READ_MET_TROPO", "METPROC");
12930 LOG(2, "Calculate tropopause...");
12931
12932 /* Get altitude and pressure profiles... */
12933#pragma omp parallel for default(shared)
12934 for (int iz = 0; iz < met->np; iz++)
12935 z[iz] = Z(met->p[iz]);
12936#pragma omp parallel for default(shared)
12937 for (int iz = 0; iz <= 190; iz++) {
12938 z2[iz] = 4.5 + 0.1 * iz;
12939 p2[iz] = P(z2[iz]);
12940 }
12941
12942 /* Do not calculate tropopause... */
12943 if (ctl->met_tropo == 0)
12944#pragma omp parallel for default(shared) collapse(2)
12945 for (int ix = 0; ix < met->nx; ix++)
12946 for (int iy = 0; iy < met->ny; iy++)
12947 met->pt[ix][iy] = NAN;
12948
12949 /* Use tropopause climatology... */
12950 else if (ctl->met_tropo == 1) {
12951 if (met->coord_type != 0)
12952 ERRMSG("Only lat/lon grid supported");
12953#pragma omp parallel for default(shared) collapse(2)
12954 for (int ix = 0; ix < met->nx; ix++)
12955 for (int iy = 0; iy < met->ny; iy++)
12956 met->pt[ix][iy] = (float) clim_tropo(clim, met->time, met->lat[iy]);
12957 }
12958
12959 /* Use cold point... */
12960 else if (ctl->met_tropo == 2) {
12961
12962 /* Loop over grid points... */
12963#pragma omp parallel for default(shared) private(t,t2) collapse(2)
12964 for (int ix = 0; ix < met->nx; ix++)
12965 for (int iy = 0; iy < met->ny; iy++) {
12966
12967 /* Interpolate temperature profile... */
12968 for (int iz = 0; iz < met->np; iz++)
12969 t[iz] = met->t[ix][iy][iz];
12970 spline(z, t, met->np, z2, t2, 171, ctl->met_tropo_spline);
12971
12972 /* Find minimum... */
12973 int iz = (int) gsl_stats_min_index(t2, 1, 171);
12974 if (iz > 0 && iz < 170)
12975 met->pt[ix][iy] = (float) p2[iz];
12976 else
12977 met->pt[ix][iy] = NAN;
12978 }
12979 }
12980
12981 /* Use WMO definition... */
12982 else if (ctl->met_tropo == 3 || ctl->met_tropo == 4) {
12983
12984 /* Loop over grid points... */
12985#pragma omp parallel for default(shared) private(t,t2) collapse(2)
12986 for (int ix = 0; ix < met->nx; ix++)
12987 for (int iy = 0; iy < met->ny; iy++) {
12988
12989 /* Interpolate temperature profile... */
12990 int iz;
12991 for (iz = 0; iz < met->np; iz++)
12992 t[iz] = met->t[ix][iy][iz];
12993 spline(z, t, met->np, z2, t2, 191, ctl->met_tropo_spline);
12994
12995 /* Find 1st tropopause... */
12996 met->pt[ix][iy] = NAN;
12997 for (iz = 0; iz <= 170; iz++) {
12998 int found = 1;
12999 for (int iz2 = iz + 1; iz2 <= iz + 20; iz2++)
13000 if (LAPSE(p2[iz], t2[iz], p2[iz2], t2[iz2]) > 2.0) {
13001 found = 0;
13002 break;
13003 }
13004 if (found) {
13005 if (iz > 0 && iz < 170)
13006 met->pt[ix][iy] = (float) p2[iz];
13007 break;
13008 }
13009 }
13010
13011 /* Find 2nd tropopause... */
13012 if (ctl->met_tropo == 4 && isfinite(met->pt[ix][iy])) {
13013 met->pt[ix][iy] = NAN;
13014 for (; iz <= 170; iz++) {
13015 int found = 1;
13016 for (int iz2 = iz + 1; iz2 <= iz + 10; iz2++)
13017 if (LAPSE(p2[iz], t2[iz], p2[iz2], t2[iz2]) < 3.0) {
13018 found = 0;
13019 break;
13020 }
13021 if (found)
13022 break;
13023 }
13024 for (; iz <= 170; iz++) {
13025 int found = 1;
13026 for (int iz2 = iz + 1; iz2 <= iz + 20; iz2++)
13027 if (LAPSE(p2[iz], t2[iz], p2[iz2], t2[iz2]) > 2.0) {
13028 found = 0;
13029 break;
13030 }
13031 if (found) {
13032 if (iz > 0 && iz < 170)
13033 met->pt[ix][iy] = (float) p2[iz];
13034 break;
13035 }
13036 }
13037 }
13038 }
13039 }
13040
13041 /* Use dynamical tropopause... */
13042 else if (ctl->met_tropo == 5) {
13043
13044 /* Loop over grid points... */
13045#pragma omp parallel for default(shared) private(pv,pv2,th,th2) collapse(2)
13046 for (int ix = 0; ix < met->nx; ix++)
13047 for (int iy = 0; iy < met->ny; iy++) {
13048
13049 /* Interpolate potential vorticity profile... */
13050 for (int iz = 0; iz < met->np; iz++)
13051 pv[iz] = met->pv[ix][iy][iz];
13052 spline(z, pv, met->np, z2, pv2, 171, ctl->met_tropo_spline);
13053
13054 /* Interpolate potential temperature profile... */
13055 for (int iz = 0; iz < met->np; iz++)
13056 th[iz] = THETA(met->p[iz], met->t[ix][iy][iz]);
13057 spline(z, th, met->np, z2, th2, 171, ctl->met_tropo_spline);
13058
13059 /* Find dynamical tropopause... */
13060 met->pt[ix][iy] = NAN;
13061 for (int iz = 0; iz <= 170; iz++)
13062 if (fabs(pv2[iz]) >= ctl->met_tropo_pv
13063 || th2[iz] >= ctl->met_tropo_theta) {
13064 if (iz > 0 && iz < 170)
13065 met->pt[ix][iy] = (float) p2[iz];
13066 break;
13067 }
13068 }
13069 }
13070
13071 else
13072 ERRMSG("Cannot calculate tropopause!");
13073
13074 /* Interpolate temperature, geopotential height, and water vapor... */
13075#pragma omp parallel for default(shared) collapse(2)
13076 for (int ix = 0; ix < met->nx; ix++)
13077 for (int iy = 0; iy < met->ny; iy++) {
13078 double h2ot, tt, zt;
13080 intpol_met_space_3d(met, met->t, met->pt[ix][iy], met->lon[ix],
13081 met->lat[iy], &tt, ci, cw, 1);
13082 intpol_met_space_3d(met, met->z, met->pt[ix][iy], met->lon[ix],
13083 met->lat[iy], &zt, ci, cw, 0);
13084 intpol_met_space_3d(met, met->h2o, met->pt[ix][iy], met->lon[ix],
13085 met->lat[iy], &h2ot, ci, cw, 0);
13086 met->tt[ix][iy] = (float) tt;
13087 met->zt[ix][iy] = (float) zt;
13088 met->h2ot[ix][iy] = (float) h2ot;
13089 }
13090}
13091
13092/*****************************************************************************/
13093
13095 const char *filename,
13096 const ctl_t *ctl,
13097 double *rt,
13098 double *rz,
13099 double *rlon,
13100 double *rlat,
13101 double *robs,
13102 int *nobs) {
13103
13104 /* Write info... */
13105 LOG(1, "Read observation data: %s", filename);
13106
13107 /* Read data... */
13108 if (ctl->obs_type == 0)
13109 read_obs_asc(filename, rt, rz, rlon, rlat, robs, nobs);
13110 else if (ctl->obs_type == 1)
13111 read_obs_nc(filename, rt, rz, rlon, rlat, robs, nobs);
13112 else
13113 ERRMSG("Set OBS_TYPE to 0 or 1!");
13114
13115 /* Check time... */
13116 for (int i = 1; i < *nobs; i++)
13117 if (rt[i] < rt[i - 1])
13118 ERRMSG("Time must be ascending!");
13119
13120 /* Write info... */
13121 int n = *nobs;
13122 double mini, maxi;
13123 LOG(2, "Number of observations: %d", *nobs);
13124 gsl_stats_minmax(&mini, &maxi, rt, 1, (size_t) n);
13125 LOG(2, "Time range: %.2f ... %.2f s", mini, maxi);
13126 gsl_stats_minmax(&mini, &maxi, rz, 1, (size_t) n);
13127 LOG(2, "Altitude range: %g ... %g km", mini, maxi);
13128 gsl_stats_minmax(&mini, &maxi, rlon, 1, (size_t) n);
13129 LOG(2, "Longitude range: %g ... %g deg", mini, maxi);
13130 gsl_stats_minmax(&mini, &maxi, rlat, 1, (size_t) n);
13131 LOG(2, "Latitude range: %g ... %g deg", mini, maxi);
13132 gsl_stats_minmax(&mini, &maxi, robs, 1, (size_t) n);
13133 LOG(2, "Observation range: %g ... %g", mini, maxi);
13134}
13135
13136/*****************************************************************************/
13137
13139 const char *filename,
13140 double *rt,
13141 double *rz,
13142 double *rlon,
13143 double *rlat,
13144 double *robs,
13145 int *nobs) {
13146
13147 /* Open observation data file... */
13148 FILE *in;
13149 if (!(in = fopen(filename, "r")))
13150 ERRMSG("Cannot open file!");
13151
13152 /* Read observations... */
13153 char line[LEN];
13154 while (fgets(line, LEN, in))
13155 if (sscanf(line, "%lg %lg %lg %lg %lg", &rt[*nobs], &rz[*nobs],
13156 &rlon[*nobs], &rlat[*nobs], &robs[*nobs]) == 5)
13157 if ((++(*nobs)) >= NOBS)
13158 ERRMSG("Too many observations!");
13159
13160 /* Close observation data file... */
13161 fclose(in);
13162}
13163
13164/*****************************************************************************/
13165
13167 const char *filename,
13168 double *rt,
13169 double *rz,
13170 double *rlon,
13171 double *rlat,
13172 double *robs,
13173 int *nobs) {
13174
13175 int ncid, varid;
13176
13177 /* Open netCDF file... */
13178 if (nc_open(filename, NC_NOWRITE, &ncid) != NC_NOERR)
13179 ERRMSG("Cannot open file!");
13180
13181 /* Read the observations from the NetCDF file... */
13182 NC_INQ_DIM("nobs", nobs, 1, NOBS, 1);
13183 NC_GET_DOUBLE("time", rt, 1);
13184 NC_GET_DOUBLE("alt", rz, 1);
13185 NC_GET_DOUBLE("lon", rlon, 1);
13186 NC_GET_DOUBLE("lat", rlat, 1);
13187 NC_GET_DOUBLE("obs", robs, 1);
13188
13189 /* Close file... */
13190 NC(nc_close(ncid));
13191}
13192
13193/*****************************************************************************/
13194
13196 const char *filename,
13197 int argc,
13198 char *argv[],
13199 const char *varname,
13200 const int arridx,
13201 const char *defvalue,
13202 char *value) {
13203
13204 FILE *in = NULL;
13205
13206 char fullname1[LEN], fullname2[LEN], rval[LEN];
13207
13208 int contain = 0, i;
13209
13210 /* Open file... */
13211 if (filename[strlen(filename) - 1] != '-')
13212 if (!(in = fopen(filename, "r")))
13213 ERRMSG("Cannot open file!");
13214
13215 /* Set full variable name... */
13216 if (arridx >= 0) {
13217 sprintf(fullname1, "%s[%d]", varname, arridx);
13218 sprintf(fullname2, "%s[*]", varname);
13219 } else {
13220 sprintf(fullname1, "%s", varname);
13221 sprintf(fullname2, "%s", varname);
13222 }
13223
13224 /* Read data... */
13225 if (in != NULL) {
13226 char dummy[LEN], line[LEN], rvarname[LEN];
13227 while (fgets(line, LEN, in)) {
13228 if (sscanf(line, "%4999s %4999s %4999s", rvarname, dummy, rval) == 3)
13229 if (strcasecmp(rvarname, fullname1) == 0 ||
13230 strcasecmp(rvarname, fullname2) == 0) {
13231 contain = 1;
13232 break;
13233 }
13234 }
13235 }
13236 for (i = 1; i < argc - 1; i++)
13237 if (strcasecmp(argv[i], fullname1) == 0 ||
13238 strcasecmp(argv[i], fullname2) == 0) {
13239 sprintf(rval, "%s", argv[i + 1]);
13240 contain = 1;
13241 break;
13242 }
13243
13244 /* Close file... */
13245 if (in != NULL)
13246 fclose(in);
13247
13248 /* Check for missing variables... */
13249 if (!contain) {
13250 if (strlen(defvalue) > 0)
13251 sprintf(rval, "%s", defvalue);
13252 else
13253 ERRMSG("Missing variable %s!\n", fullname1);
13254 }
13255
13256 /* Write info... */
13257 LOG(1, "%s = %s", fullname1, rval);
13258
13259 /* Return values... */
13260 if (value != NULL)
13261 sprintf(value, "%s", rval);
13262 return atof(rval);
13263}
13264
13265/*****************************************************************************/
13266
13267double sedi(
13268 const double p,
13269 const double T,
13270 const double rp,
13271 const double rhop) {
13272
13273 /* Convert particle radius from microns to m... */
13274 const double rp_help = rp * 1e-6;
13275
13276 /* Density of dry air [kg / m^3]... */
13277 const double rho = RHO(p, T);
13278
13279 /* Dynamic viscosity of air [kg / (m s)]... */
13280 const double eta = 1.8325e-5 * (416.16 / (T + 120.)) * pow(T / 296.16, 1.5);
13281
13282 /* Thermal velocity of an air molecule [m / s]... */
13283 const double v = sqrt(8. * KB * T / (M_PI * M_AIR_MOLECULE));
13284
13285 /* Mean free path of an air molecule [m]... */
13286 const double lambda = 2. * eta / (rho * v);
13287
13288 /* Knudsen number for air (dimensionless)... */
13289 const double K = lambda / rp_help;
13290
13291 /* Cunningham slip-flow correction (dimensionless)... */
13292 const double G = 1. + K * (1.249 + 0.42 * exp(-0.87 / K));
13293
13294 /* Sedimentation velocity [m / s]... */
13295 return 2. * SQR(rp_help) * (rhop - rho) * G0 / (9. * eta) * G;
13296}
13297
13298/*****************************************************************************/
13299
13301 const double *x,
13302 const double *y,
13303 const int n,
13304 const double *x2,
13305 double *y2,
13306 const int n2,
13307 const int method) {
13308
13309 /* Cubic spline interpolation... */
13310 if (method == 1) {
13311
13312 /* Allocate... */
13313 gsl_interp_accel *acc = gsl_interp_accel_alloc();
13314 gsl_spline *s = gsl_spline_alloc(gsl_interp_cspline, (size_t) n);
13315
13316 /* Interpolate profile... */
13317 gsl_spline_init(s, x, y, (size_t) n);
13318 for (int i = 0; i < n2; i++)
13319 if (x2[i] <= x[0])
13320 y2[i] = y[0];
13321 else if (x2[i] >= x[n - 1])
13322 y2[i] = y[n - 1];
13323 else
13324 y2[i] = gsl_spline_eval(s, x2[i], acc);
13325
13326 /* Free... */
13327 gsl_spline_free(s);
13328 gsl_interp_accel_free(acc);
13329 }
13330
13331 /* Linear interpolation... */
13332 else {
13333 for (int i = 0; i < n2; i++)
13334 if (x2[i] <= x[0])
13335 y2[i] = y[0];
13336 else if (x2[i] >= x[n - 1])
13337 y2[i] = y[n - 1];
13338 else {
13339 const int idx = locate_irr(x, n, x2[i]);
13340 y2[i] = LIN(x[idx], y[idx], x[idx + 1], y[idx + 1], x2[i]);
13341 }
13342 }
13343}
13344
13345/*****************************************************************************/
13346
13348 const float *data,
13349 const int n) {
13350
13351 if (n <= 0)
13352 return 0;
13353
13354 float mean = 0, var = 0;
13355
13356 for (int i = 0; i < n; ++i) {
13357 mean += data[i];
13358 var += SQR(data[i]);
13359 }
13360
13361 var = var / (float) n - SQR(mean / (float) n);
13362
13363 return (var > 0 ? sqrtf(var) : 0);
13364}
13365
13366/*****************************************************************************/
13367
13369 const int year,
13370 const int mon,
13371 const int day,
13372 const int hour,
13373 const int min,
13374 const int sec,
13375 const double remain,
13376 double *jsec) {
13377
13378 struct tm t0, t1;
13379
13380 t0.tm_year = 100;
13381 t0.tm_mon = 0;
13382 t0.tm_mday = 1;
13383 t0.tm_hour = 0;
13384 t0.tm_min = 0;
13385 t0.tm_sec = 0;
13386
13387 t1.tm_year = year - 1900;
13388 t1.tm_mon = mon - 1;
13389 t1.tm_mday = day;
13390 t1.tm_hour = hour;
13391 t1.tm_min = min;
13392 t1.tm_sec = sec;
13393
13394 *jsec = (double) timegm(&t1) - (double) timegm(&t0) + remain;
13395}
13396
13397/*****************************************************************************/
13398
13400 const char *name,
13401 const char *group,
13402 const int output) {
13403
13404 static char names[NTIMER][100], groups[NTIMER][100];
13405
13406 static double rt_name[NTIMER], rt_group[NTIMER],
13407 rt_min[NTIMER], rt_max[NTIMER], dt, t0, t1;
13408
13409 static int iname = -1, igroup = -1, nname, ngroup, ct_name[NTIMER];
13410
13411 /* Get time... */
13412 t1 = omp_get_wtime();
13413 dt = t1 - t0;
13414
13415 /* Add elapsed time to current timers... */
13416 if (iname >= 0) {
13417 rt_name[iname] += dt;
13418 rt_min[iname] = (ct_name[iname] <= 0 ? dt : MIN(rt_min[iname], dt));
13419 rt_max[iname] = (ct_name[iname] <= 0 ? dt : MAX(rt_max[iname], dt));
13420 ct_name[iname]++;
13421 }
13422 if (igroup >= 0)
13423 rt_group[igroup] += t1 - t0;
13424
13425 /* Report timers... */
13426 if (output) {
13427 for (int i = 0; i < nname; i++)
13428 LOG(1, "TIMER_%s = %.3f s (min= %g s, mean= %g s,"
13429 " max= %g s, n= %d)", names[i], rt_name[i], rt_min[i],
13430 rt_name[i] / ct_name[i], rt_max[i], ct_name[i]);
13431 for (int i = 0; i < ngroup; i++)
13432 LOG(1, "TIMER_GROUP_%s = %.3f s", groups[i], rt_group[i]);
13433 double total = 0.0;
13434 for (int i = 0; i < nname; i++)
13435 total += rt_name[i];
13436 LOG(1, "TIMER_TOTAL = %.3f s", total);
13437 }
13438
13439 /* Identify IDs of next timer... */
13440 for (iname = 0; iname < nname; iname++)
13441 if (strcasecmp(name, names[iname]) == 0)
13442 break;
13443 for (igroup = 0; igroup < ngroup; igroup++)
13444 if (strcasecmp(group, groups[igroup]) == 0)
13445 break;
13446
13447 /* Check whether this is a new timer... */
13448 if (iname >= nname) {
13449 sprintf(names[iname], "%s", name);
13450 if ((++nname) >= NTIMER)
13451 ERRMSG("Too many timers!");
13452 }
13453
13454 /* Check whether this is a new group... */
13455 if (igroup >= ngroup) {
13456 sprintf(groups[igroup], "%s", group);
13457 if ((++ngroup) >= NTIMER)
13458 ERRMSG("Too many groups!");
13459 }
13460
13461 /* Save starting time... */
13462 t0 = t1;
13463}
13464
13465/*****************************************************************************/
13466
13468 const char *filename,
13469 const int offset,
13470 const int with_seconds) {
13471
13472 char tstr[10];
13473
13474 double t;
13475
13476 /* Get time from filename... */
13477 int len = (int) strlen(filename);
13478 sprintf(tstr, "%.4s", &filename[len - offset]);
13479 int year = atoi(tstr);
13480 sprintf(tstr, "%.2s", &filename[len - offset + 5]);
13481 int mon = atoi(tstr);
13482 sprintf(tstr, "%.2s", &filename[len - offset + 8]);
13483 int day = atoi(tstr);
13484 sprintf(tstr, "%.2s", &filename[len - offset + 11]);
13485 int hour = atoi(tstr);
13486 sprintf(tstr, "%.2s", &filename[len - offset + 14]);
13487 int min = atoi(tstr);
13488
13489 int sec = 0;
13490 if (with_seconds) {
13491 sprintf(tstr, "%.2s", &filename[len - offset + 17]);
13492 sec = atoi(tstr);
13493 }
13494
13495 /* Check time... */
13496 if (year < 1900 || year > 2100 || mon < 1 || mon > 12 || day < 1
13497 || day > 31 || hour < 0 || hour > 23 || min < 0 || min > 59)
13498 ERRMSG("Cannot read time from filename!");
13499
13500 /* Convert time to Julian seconds... */
13501 time2jsec(year, mon, day, hour, min, sec, 0.0, &t);
13502
13503 /* Return time... */
13504 return t;
13505}
13506
13507/*****************************************************************************/
13508
13510 const ctl_t *ctl,
13511 const clim_t *clim,
13512 const atm_t *atm,
13513 const int ip) {
13514
13515 /* Get tropopause pressure... */
13516 const double pt = clim_tropo(clim, atm->time[ip],
13517 ctl->met_coord_type ==
13518 0 ? atm->lat[ip] : ctl->met_utm_ref_lat);
13519
13520 /* Get pressure range... */
13521 const double p1 = pt * 0.866877899;
13522 const double p0 = pt / 0.866877899;
13523
13524 /* Get weighting factor... */
13525 if (atm->p[ip] > p0)
13526 return 1;
13527 else if (atm->p[ip] < p1)
13528 return 0;
13529 else
13530 return LIN(p0, 1.0, p1, 0.0, atm->p[ip]);
13531}
13532
13533/*****************************************************************************/
13534
13536 const char *filename,
13537 const ctl_t *ctl,
13538 const atm_t *atm,
13539 const double t) {
13540
13541 FILE *out;
13542
13543 /* Set time interval for output... */
13544 const double t0 = t - 0.5 * ctl->dt_mod;
13545 const double t1 = t + 0.5 * ctl->dt_mod;
13546
13547 /* Check if gnuplot output is requested... */
13548 if (ctl->atm_gpfile[0] != '-') {
13549
13550 /* Create gnuplot pipe... */
13551 if (!(out = popen("gnuplot", "w")))
13552 ERRMSG("Cannot create pipe to gnuplot!");
13553
13554 /* Set plot filename... */
13555 fprintf(out, "set out \"%s.png\"\n", filename);
13556
13557 /* Set time string... */
13558 double r;
13559 int year, mon, day, hour, min, sec;
13560 jsec2time(t, &year, &mon, &day, &hour, &min, &sec, &r);
13561 fprintf(out, "timestr=\"%d-%02d-%02d, %02d:%02d UTC\"\n",
13562 year, mon, day, hour, min);
13563
13564 /* Dump gnuplot file to pipe... */
13565 FILE *in;
13566 if (!(in = fopen(ctl->atm_gpfile, "r")))
13567 ERRMSG("Cannot open file!");
13568 char line[LEN];
13569 while (fgets(line, LEN, in))
13570 fprintf(out, "%s", line);
13571 fclose(in);
13572 }
13573
13574 else {
13575
13576 /* Create file... */
13577 if (!(out = fopen(filename, "w")))
13578 ERRMSG("Cannot create file!");
13579 }
13580
13581 /* Write header... */
13582
13583 if (ctl->met_coord_type == 0) {
13584 fprintf(out,
13585 "# $1 = time [s]\n"
13586 "# $2 = altitude [km]\n"
13587 "# $3 = longitude [deg]\n" "# $4 = latitude [deg]\n");
13588 } else {
13589 fprintf(out,
13590 "# $1 = time [s]\n"
13591 "# $2 = altitude [km]\n" "# $3 = x [m]\n" "# $4 = y [m]\n");
13592 }
13593
13594 for (int iq = 0; iq < ctl->nq; iq++)
13595 fprintf(out, "# $%i = %s [%s]\n", iq + 5, ctl->qnt_name[iq],
13596 ctl->qnt_unit[iq]);
13597 fprintf(out, "\n");
13598
13599 /* Write data... */
13600 for (int ip = 0; ip < atm->np; ip += ctl->atm_stride) {
13601
13602 /* Check time... */
13603 if (ctl->atm_filter == 2 && (atm->time[ip] < t0 || atm->time[ip] > t1))
13604 continue;
13605
13606 /* Write output... */
13607 if (ctl->met_coord_type == 0) {
13608 fprintf(out, "%.2f %g %g %g",
13609 atm->time[ip], Z(atm->p[ip]), atm->lon[ip], atm->lat[ip]
13610 );
13611 } else {
13612 fprintf(out, "%.2f %g %.2f %.2f",
13613 atm->time[ip], Z(atm->p[ip]), atm->lon[ip], atm->lat[ip]
13614 );
13615 }
13616
13617 for (int iq = 0; iq < ctl->nq; iq++) {
13618 fprintf(out, " ");
13619 if (ctl->atm_filter == 1 && (atm->time[ip] < t0 || atm->time[ip] > t1))
13620 fprintf(out, ctl->qnt_format[iq], NAN);
13621 else
13622 fprintf(out, ctl->qnt_format[iq], atm->q[iq][ip]);
13623 }
13624 fprintf(out, "\n");
13625 }
13626
13627 /* Close file... */
13628 fclose(out);
13629}
13630
13631/*****************************************************************************/
13632
13634 const char *filename,
13635 const ctl_t *ctl,
13636 const atm_t *atm) {
13637
13638 FILE *out;
13639
13640 /* Create file... */
13641 if (!(out = fopen(filename, "w")))
13642 ERRMSG("Cannot create file!");
13643
13644 /* Write version of binary data... */
13645 int version = 100;
13646 FWRITE(&version, int,
13647 1,
13648 out);
13649
13650 /* Write data... */
13651 FWRITE(&atm->np, int,
13652 1,
13653 out);
13654 FWRITE(atm->time, double,
13655 (size_t) atm->np,
13656 out);
13657 FWRITE(atm->p, double,
13658 (size_t) atm->np,
13659 out);
13660 FWRITE(atm->lon, double,
13661 (size_t) atm->np,
13662 out);
13663 FWRITE(atm->lat, double,
13664 (size_t) atm->np,
13665 out);
13666 for (int iq = 0; iq < ctl->nq; iq++)
13667 FWRITE(atm->q[iq], double,
13668 (size_t) atm->np,
13669 out);
13670
13671 /* Write final flag... */
13672 int final = 999;
13673 FWRITE(&final, int,
13674 1,
13675 out);
13676
13677 /* Close file... */
13678 fclose(out);
13679}
13680
13681/*****************************************************************************/
13682
13684 const char *filename,
13685 const ctl_t *ctl,
13686 const atm_t *atm) {
13687
13688 if (ctl->met_coord_type != 0)
13689 ERRMSG("CLaMS atmospheric files support only lat/lon grids");
13690
13691 int tid, pid, ncid, varid;
13692 size_t start[2], count[2];
13693
13694 /* Create file... */
13695 NC(nc_create(filename, NC_NETCDF4, &ncid));
13696
13697 /* Define dimensions... */
13698 NC(nc_def_dim(ncid, "time", 1, &tid));
13699 NC(nc_def_dim(ncid, "NPARTS", (size_t) atm->np, &pid));
13700
13701 /* Define variables and their attributes... */
13702 int dim_ids[2] = { tid, pid };
13703 NC_DEF_VAR("time", NC_DOUBLE, 1, &tid, "Time",
13704 "seconds since 2000-01-01 00:00:00 UTC", ctl->atm_nc_level, 0);
13705 NC_DEF_VAR("LAT", NC_DOUBLE, 1, &pid, "Latitude", "deg",
13706 ctl->atm_nc_level, 0);
13707 NC_DEF_VAR("LON", NC_DOUBLE, 1, &pid, "Longitude", "deg",
13708 ctl->atm_nc_level, 0);
13709 NC_DEF_VAR("PRESS", NC_DOUBLE, 1, &pid, "Pressure", "hPa",
13710 ctl->atm_nc_level, 0);
13711 NC_DEF_VAR("ZETA", NC_DOUBLE, 1, &pid, "Zeta", "K", ctl->atm_nc_level, 0);
13712 for (int iq = 0; iq < ctl->nq; iq++)
13713 NC_DEF_VAR(ctl->qnt_name[iq], NC_DOUBLE, 2, dim_ids,
13714 ctl->qnt_name[iq], ctl->qnt_unit[iq],
13715 ctl->atm_nc_level, ctl->atm_nc_quant[iq]);
13716
13717 /* Define global attributes... */
13718 NC_PUT_ATT_GLOBAL("exp_VERTCOOR_name", "zeta");
13719 NC_PUT_ATT_GLOBAL("model", "MPTRAC");
13720
13721 /* End definitions... */
13722 NC(nc_enddef(ncid));
13723
13724 /* Write data... */
13725 NC_PUT_DOUBLE("time", atm->time, 0);
13726 NC_PUT_DOUBLE("LAT", atm->lat, 0);
13727 NC_PUT_DOUBLE("LON", atm->lon, 0);
13728 NC_PUT_DOUBLE("PRESS", atm->p, 0);
13729 NC_PUT_DOUBLE("ZETA", atm->q[ctl->qnt_zeta_d], 0);
13730 for (int iq = 0; iq < ctl->nq; iq++)
13731 NC_PUT_DOUBLE(ctl->qnt_name[iq], atm->q[iq], 0);
13732
13733 /* Close file... */
13734 NC(nc_close(ncid));
13735}
13736
13737/*****************************************************************************/
13738
13740 const char *dirname,
13741 const ctl_t *ctl,
13742 const atm_t *atm,
13743 const double t) {
13744
13745 if (ctl->met_coord_type != 0)
13746 ERRMSG("CLaMS atmospheric files support only lat/lon grids");
13747
13748 /* Global Counter... */
13749 static size_t out_cnt = 0;
13750
13751 double r, r_start, r_stop;
13752 int year, mon, day, hour, min, sec;
13753 int year_start, mon_start, day_start, hour_start, min_start, sec_start;
13754 int year_stop, mon_stop, day_stop, hour_stop, min_stop, sec_stop;
13755 char filename_out[2 * LEN] = "traj_fix_3d_YYYYMMDDHH_YYYYMMDDHH.nc";
13756
13757 int ncid, varid, tid, pid, cid;
13758 int dim_ids[2];
13759
13760 /* time, nparc */
13761 size_t start[2];
13762 size_t count[2];
13763
13764 /* Determine start and stop times of calculation... */
13765 jsec2time(t, &year, &mon, &day, &hour, &min, &sec, &r);
13766 jsec2time(ctl->t_start, &year_start, &mon_start, &day_start, &hour_start,
13767 &min_start, &sec_start, &r_start);
13768 jsec2time(ctl->t_stop, &year_stop, &mon_stop, &day_stop, &hour_stop,
13769 &min_stop, &sec_stop, &r_stop);
13770
13771 sprintf(filename_out,
13772 "%s/traj_fix_3d_%02d%02d%02d%02d_%02d%02d%02d%02d.nc", dirname,
13773 year_start % 100, mon_start, day_start, hour_start,
13774 year_stop % 100, mon_stop, day_stop, hour_stop);
13775 LOG(1, "Write traj file: %s", filename_out);
13776
13777 /* Define hyperslap for the traj_file... */
13778 start[0] = out_cnt;
13779 start[1] = 0;
13780 count[0] = 1;
13781 count[1] = (size_t) atm->np;
13782
13783 /* Create the file at the first timestep... */
13784 if (out_cnt == 0) {
13785
13786 /* Create file... */
13787 NC(nc_create(filename_out, NC_NETCDF4, &ncid));
13788
13789 /* Define dimensions... */
13790 NC(nc_def_dim(ncid, "time", NC_UNLIMITED, &tid));
13791 NC(nc_def_dim(ncid, "NPARTS", (size_t) atm->np, &pid));
13792 NC(nc_def_dim(ncid, "TMDT", 7, &cid));
13793 dim_ids[0] = tid;
13794 dim_ids[1] = pid;
13795
13796 /* Define variables and their attributes... */
13797 NC_DEF_VAR("time", NC_DOUBLE, 1, &tid, "Time",
13798 "seconds since 2000-01-01 00:00:00 UTC", ctl->atm_nc_level, 0);
13799 NC_DEF_VAR("LAT", NC_DOUBLE, 2, dim_ids, "Latitude", "deg",
13800 ctl->atm_nc_level, 0);
13801 NC_DEF_VAR("LON", NC_DOUBLE, 2, dim_ids, "Longitude", "deg",
13802 ctl->atm_nc_level, 0);
13803 NC_DEF_VAR("PRESS", NC_DOUBLE, 2, dim_ids, "Pressure", "hPa",
13804 ctl->atm_nc_level, 0);
13805 NC_DEF_VAR("ZETA", NC_DOUBLE, 2, dim_ids, "Zeta", "K",
13806 ctl->atm_nc_level, 0);
13807 for (int iq = 0; iq < ctl->nq; iq++)
13808 NC_DEF_VAR(ctl->qnt_name[iq], NC_DOUBLE, 2, dim_ids,
13809 ctl->qnt_name[iq], ctl->qnt_unit[iq],
13810 ctl->atm_nc_level, ctl->atm_nc_quant[iq]);
13811
13812 /* Define global attributes... */
13813 NC_PUT_ATT_GLOBAL("exp_VERTCOOR_name", "zeta");
13814 NC_PUT_ATT_GLOBAL("model", "MPTRAC");
13815
13816 /* End definitions... */
13817 NC(nc_enddef(ncid));
13818 NC(nc_close(ncid));
13819 }
13820
13821 /* Increment global counter to change hyperslap... */
13822 out_cnt++;
13823
13824 /* Open file... */
13825 NC(nc_open(filename_out, NC_WRITE, &ncid));
13826
13827 /* Write data... */
13828 NC_PUT_DOUBLE("time", atm->time, 1);
13829 NC_PUT_DOUBLE("LAT", atm->lat, 1);
13830 NC_PUT_DOUBLE("LON", atm->lon, 1);
13831 NC_PUT_DOUBLE("PRESS", atm->p, 1);
13832 if (ctl->advect_vert_coord == 1) {
13833 NC_PUT_DOUBLE("ZETA", atm->q[ctl->qnt_zeta], 1);
13834 } else if (ctl->qnt_zeta >= 0) {
13835 NC_PUT_DOUBLE("ZETA", atm->q[ctl->qnt_zeta_d], 1);
13836 }
13837 for (int iq = 0; iq < ctl->nq; iq++)
13838 NC_PUT_DOUBLE(ctl->qnt_name[iq], atm->q[iq], 1);
13839
13840 /* Close file... */
13841 NC(nc_close(ncid));
13842
13843 /* At the last time step create the init_fix_YYYYMMDDHH file... */
13844 if ((year == year_stop) && (mon == mon_stop)
13845 && (day == day_stop) && (hour == hour_stop)) {
13846
13847 /* Set filename... */
13848 char filename_init[2 * LEN] = "./init_fix_YYYYMMDDHH.nc";
13849 sprintf(filename_init, "%s/init_fix_%02d%02d%02d%02d.nc",
13850 dirname, year_stop % 100, mon_stop, day_stop, hour_stop);
13851 LOG(1, "Write init file: %s", filename_init);
13852
13853 /* Create file... */
13854 NC(nc_create(filename_init, NC_NETCDF4, &ncid));
13855
13856 /* Define dimensions... */
13857 NC(nc_def_dim(ncid, "time", 1, &tid));
13858 NC(nc_def_dim(ncid, "NPARTS", (size_t) atm->np, &pid));
13859 dim_ids[0] = tid;
13860 dim_ids[1] = pid;
13861
13862 /* Define variables and their attributes... */
13863 NC_DEF_VAR("time", NC_DOUBLE, 1, &tid, "Time",
13864 "seconds since 2000-01-01 00:00:00 UTC", ctl->atm_nc_level, 0);
13865 NC_DEF_VAR("LAT", NC_DOUBLE, 1, &pid, "Latitude", "deg",
13866 ctl->atm_nc_level, 0);
13867 NC_DEF_VAR("LON", NC_DOUBLE, 1, &pid, "Longitude", "deg",
13868 ctl->atm_nc_level, 0);
13869 NC_DEF_VAR("PRESS", NC_DOUBLE, 1, &pid, "Pressure", "hPa",
13870 ctl->atm_nc_level, 0);
13871 NC_DEF_VAR("ZETA", NC_DOUBLE, 1, &pid, "Zeta", "K", ctl->atm_nc_level, 0);
13872 for (int iq = 0; iq < ctl->nq; iq++)
13873 NC_DEF_VAR(ctl->qnt_name[iq], NC_DOUBLE, 2, dim_ids,
13874 ctl->qnt_name[iq], ctl->qnt_unit[iq],
13875 ctl->atm_nc_level, ctl->atm_nc_quant[iq]);
13876
13877 /* Define global attributes... */
13878 NC_PUT_ATT_GLOBAL("exp_VERTCOOR_name", "zeta");
13879 NC_PUT_ATT_GLOBAL("model", "MPTRAC");
13880
13881 /* End definitions... */
13882 NC(nc_enddef(ncid));
13883
13884 /* Write data... */
13885 NC_PUT_DOUBLE("time", atm->time, 0);
13886 NC_PUT_DOUBLE("LAT", atm->lat, 0);
13887 NC_PUT_DOUBLE("LON", atm->lon, 0);
13888 NC_PUT_DOUBLE("PRESS", atm->p, 0);
13889 NC_PUT_DOUBLE("ZETA", atm->q[ctl->qnt_zeta_d], 0);
13890 for (int iq = 0; iq < ctl->nq; iq++)
13891 NC_PUT_DOUBLE(ctl->qnt_name[iq], atm->q[iq], 0);
13892
13893 /* Close file... */
13894 NC(nc_close(ncid));
13895 }
13896}
13897
13898/*****************************************************************************/
13899
13901 const char *filename,
13902 const ctl_t *ctl,
13903 const atm_t *atm) {
13904
13905 int ncid, obsid, varid;
13906
13907 size_t start[2], count[2];
13908
13909 /* Create file... */
13910 NC(nc_create(filename, NC_NETCDF4, &ncid));
13911
13912 /* Define dimensions... */
13913 NC(nc_def_dim(ncid, "obs", (size_t) atm->np, &obsid));
13914
13915 /* Define variables and their attributes... */
13916 NC_DEF_VAR("time", NC_DOUBLE, 1, &obsid, "time",
13917 "seconds since 2000-01-01 00:00:00 UTC", ctl->atm_nc_level, 0);
13918 NC_DEF_VAR("press", NC_DOUBLE, 1, &obsid, "pressure", "hPa",
13919 ctl->atm_nc_level, 0);
13920 NC_DEF_VAR("lon", NC_DOUBLE, 1, &obsid, "longitude", "degrees_east",
13921 ctl->atm_nc_level, 0);
13922 NC_DEF_VAR("lat", NC_DOUBLE, 1, &obsid, "latitude", "degrees_north",
13923 ctl->atm_nc_level, 0);
13924 for (int iq = 0; iq < ctl->nq; iq++)
13925 NC_DEF_VAR(ctl->qnt_name[iq], NC_DOUBLE, 1, &obsid,
13926 ctl->qnt_longname[iq], ctl->qnt_unit[iq],
13927 ctl->atm_nc_level, ctl->atm_nc_quant[iq]);
13928
13929 /* Define global attributes... */
13930 NC_PUT_ATT_GLOBAL("featureType", "point");
13931
13932 /* End definitions... */
13933 NC(nc_enddef(ncid));
13934
13935 /* Write data... */
13936 NC_PUT_DOUBLE("time", atm->time, 0);
13937 NC_PUT_DOUBLE("press", atm->p, 0);
13938 NC_PUT_DOUBLE("lon", atm->lon, 0);
13939 NC_PUT_DOUBLE("lat", atm->lat, 0);
13940 for (int iq = 0; iq < ctl->nq; iq++)
13941 NC_PUT_DOUBLE(ctl->qnt_name[iq], atm->q[iq], 0);
13942
13943 /* Close file... */
13944 NC(nc_close(ncid));
13945}
13946
13947/*****************************************************************************/
13948
13950 const char *filename,
13951 const ctl_t *ctl,
13952 const atm_t *atm,
13953 const double t) {
13954
13955 static FILE *out;
13956
13957 /* Set timer... */
13958 SELECT_TIMER("WRITE_BUDGET", "OUTPUT");
13959
13960 /* Check quantities... */
13961 if (ctl->qnt_m < 0)
13962 ERRMSG("Need quantity mass!");
13963 const int ensemble = (ctl->nens > 0);
13964 if (ensemble && ctl->qnt_ens < 0)
13965 ERRMSG("Missing ensemble IDs!");
13966 if (ctl->nens > NENS)
13967 ERRMSG("Too many ensembles!");
13968
13969 /* Initialize output file... */
13970 if (out == NULL) {
13971 LOG(1, "Write mass budget data: %s", filename);
13972 if (!(out = fopen(filename, "w")))
13973 ERRMSG("Cannot create file!");
13974 fprintf(out,
13975 "# $1 = time [s]\n"
13976 "# $2 = ensemble ID (-999=total)\n"
13977 "# $3 = number of active air parcels [1]\n"
13978 "# $4 = total mass [kg]\n"
13979 "# $5 = mass loss due to OH chemistry [kg]\n"
13980 "# $6 = mass loss due to H2O2 chemistry [kg]\n"
13981 "# $7 = mass loss due to KPP chemistry [kg]\n"
13982 "# $8 = mass loss due to wet deposition [kg]\n"
13983 "# $9 = mass loss due to dry deposition [kg]\n"
13984 "# $10 = mass loss due to exponential decay [kg]\n"
13985 "# $11 = total tracked mass loss [kg]\n"
13986 "# $12 = accounted mass [kg]\n\n");
13987 }
13988
13989 /* Calculate mass budget... */
13990 const int nq = 7;
13991 const int qnt[7] = {
13992 ctl->qnt_m, ctl->qnt_mloss_oh, ctl->qnt_mloss_h2o2,
13993 ctl->qnt_mloss_kpp, ctl->qnt_mloss_wet, ctl->qnt_mloss_dry,
13994 ctl->qnt_mloss_decay
13995 };
13996 double sum[NENS + 1][7] = { {0} };
13997 int np[NENS + 1] = { 0 };
13998 const double t0 = t - 0.5 * ctl->dt_mod;
13999 const double t1 = t + 0.5 * ctl->dt_mod;
14000
14001 /* Sum active air parcels... */
14002 for (int ip = 0; ip < atm->np; ip++) {
14003 if (atm->time[ip] < t0 || atm->time[ip] > t1)
14004 continue;
14005 int e = 0;
14006 if (ensemble) {
14007 e = (int) atm->q[ctl->qnt_ens][ip];
14008 if (e < 0 || e >= ctl->nens)
14009 ERRMSG("Ensemble ID out of range!");
14010 }
14011 np[e]++;
14012 np[NENS]++;
14013 for (int iq = 0; iq < nq; iq++)
14014 if (qnt[iq] >= 0) {
14015 sum[e][iq] += atm->q[qnt[iq]][ip];
14016 sum[NENS][iq] += atm->q[qnt[iq]][ip];
14017 }
14018 }
14019
14020 /* Write total and ensemble budgets... */
14021 for (int ie = -1; ie < (ensemble ? ctl->nens : 0); ie++) {
14022 const int e = (ie < 0 ? NENS : ie);
14023 double mloss = 0;
14024 for (int iq = 1; iq < nq; iq++)
14025 if (qnt[iq] >= 0)
14026 mloss += sum[e][iq];
14027 fprintf(out, "%.2f %d %d %g", t, ie < 0 ? -999 : ie, np[e], sum[e][0]);
14028 for (int iq = 1; iq < nq; iq++)
14029 fprintf(out, " %g", qnt[iq] >= 0 ? sum[e][iq] : NAN);
14030 fprintf(out, " %g %g\n", mloss, sum[e][0] + mloss);
14031 }
14032 fflush(out);
14033
14034 /* Finalize... */
14035 if (t == ctl->t_stop) {
14036 fclose(out);
14037 out = NULL;
14038 }
14039}
14040
14041/*****************************************************************************/
14042
14044 const char *filename,
14045 const ctl_t *ctl,
14046 const atm_t *atm,
14047 const double t) {
14048
14049 if (ctl->met_coord_type != 0)
14050 ERRMSG("Only lat/lon grid supported");
14051
14052 static FILE *out;
14053
14054 static double *modmean, *obsmean, *obsstd, *rt, *rz, *rlon, *rlat, *robs,
14055 *area, dlon, dlat, dz, x[NCSI], y[NCSI], obsstdn[NCSI], kz[EP], kw[EP];
14056
14057 static int *obscount, nobs, nk;
14058
14059 static int ct[NENS], cx[NENS], cy[NENS], cz[NENS], n[NENS];
14060
14061 const int ensemble = (ctl->nens > 0);
14062
14063 /* Set timer */
14064 SELECT_TIMER("WRITE_CSI", "OUTPUT");
14065
14066 /* Check quantities... */
14067 if (ctl->qnt_m < 0)
14068 ERRMSG("Need quantity mass!");
14069 if (ensemble) {
14070 if (ctl->qnt_ens < 0)
14071 ERRMSG("Missing ensemble IDs!");
14072 if (ctl->nens > NENS)
14073 ERRMSG("Too many ensembles!");
14074 }
14075
14076 /* Init... */
14077 if (t == ctl->t_start) {
14078
14079 /* Allocate.. */
14080 ALLOC(area, double,
14081 ctl->csi_ny);
14082 ALLOC(rt, double,
14083 NOBS);
14084 ALLOC(rz, double,
14085 NOBS);
14086 ALLOC(rlon, double,
14087 NOBS);
14088 ALLOC(rlat, double,
14089 NOBS);
14090 ALLOC(robs, double,
14091 NOBS);
14092
14093 /* Read observation data... */
14094 read_obs(ctl->csi_obsfile, ctl, rt, rz, rlon, rlat, robs, &nobs);
14095
14096 /* Read kernel data... */
14097 if (ctl->csi_kernel[0] != '-')
14098 read_kernel(ctl->csi_kernel, kz, kw, &nk);
14099
14100 /* Create new file... */
14101 LOG(1, "Write CSI%s data: %s", ensemble ? " ensemble" : "", filename);
14102 if (!(out = fopen(filename, "w")))
14103 ERRMSG("Cannot create file!");
14104
14105 /* Write header... */
14106 fprintf(out,
14107 "# $1 = time [s]\n"
14108 "# $2 = ensemble ID\n"
14109 "# $3 = number of hits (cx)\n"
14110 "# $4 = number of misses (cy)\n"
14111 "# $5 = number of false alarms (cz)\n"
14112 "# $6 = number of observations (cx + cy)\n"
14113 "# $7 = number of forecasts (cx + cz)\n"
14114 "# $8 = bias (%%)\n"
14115 "# $9 = POD (%%)\n"
14116 "# $10 = FAR (%%)\n"
14117 "# $11 = CSI (%%)\n"
14118 "# $12 = hits by random chance\n"
14119 "# $13 = ETS (%%)\n"
14120 "# $14 = Pearson R\n"
14121 "# $15 = Spearman R\n"
14122 "# $16 = mean error [kg/m²]\n"
14123 "# $17 = RMSE [kg/m²]\n"
14124 "# $18 = MAE [kg/m²]\n"
14125 "# $19 = log-likelihood\n" "# $20 = number of points\n\n");
14126
14127 /* Set grid box size... */
14128 dz = (ctl->csi_z1 - ctl->csi_z0) / ctl->csi_nz;
14129 dlon = (ctl->csi_lon1 - ctl->csi_lon0) / ctl->csi_nx;
14130 dlat = (ctl->csi_lat1 - ctl->csi_lat0) / ctl->csi_ny;
14131
14132 /* Set horizontal coordinates... */
14133 for (int iy = 0; iy < ctl->csi_ny; iy++) {
14134 const double lat = ctl->csi_lat0 + dlat * (iy + 0.5);
14135 area[iy] = dlat * dlon * SQR(RE * M_PI / 180.0) * cos(DEG2RAD(lat));
14136 }
14137 }
14138
14139 /* Set time interval... */
14140 const double t0 = t - 0.5 * ctl->dt_mod;
14141 const double t1 = t + 0.5 * ctl->dt_mod;
14142
14143 /* Allocate... */
14144 int grid_size = ctl->csi_nx * ctl->csi_ny * ctl->csi_nz;
14145 ALLOC(modmean, double,
14146 (ensemble ? ctl->nens : 1) * grid_size);
14147 ALLOC(obsmean, double,
14148 grid_size);
14149 ALLOC(obscount, int,
14150 grid_size);
14151 ALLOC(obsstd, double,
14152 grid_size);
14153
14154 /* Init... */
14155 for (int i = 0; i < (ensemble ? ctl->nens : 1); i++)
14156 ct[i] = cx[i] = cy[i] = cz[i] = n[i] = 0;
14157
14158 /* Loop over observations... */
14159 for (int i = 0; i < nobs; i++) {
14160 if (rt[i] < t0 || rt[i] >= t1 || !isfinite(robs[i]))
14161 continue;
14162
14163 /* Check grid boundaries and calculate indices... */
14164 if (rlon[i] < ctl->csi_lon0 || rlon[i] >= ctl->csi_lon1
14165 || rlat[i] < ctl->csi_lat0 || rlat[i] >= ctl->csi_lat1
14166 || rz[i] < ctl->csi_z0 || rz[i] >= ctl->csi_z1)
14167 continue;
14168 const int ix = (int) ((rlon[i] - ctl->csi_lon0) / dlon);
14169 const int iy = (int) ((rlat[i] - ctl->csi_lat0) / dlat);
14170 const int iz = (int) ((rz[i] - ctl->csi_z0) / dz);
14171 if (ix >= ctl->csi_nx || iy >= ctl->csi_ny || iz >= ctl->csi_nz)
14172 continue;
14173
14174 /* Get mean observation index... */
14175 const int idx = ARRAY_3D(ix, iy, ctl->csi_ny, iz, ctl->csi_nz);
14176 obsmean[idx] += robs[i];
14177 obsstd[idx] += SQR(robs[i]);
14178 obscount[idx]++;
14179 }
14180
14181 /* Analyze model data... */
14182 for (int ip = 0; ip < atm->np; ip++) {
14183
14184 /* Check time... */
14185 if (atm->time[ip] < t0 || atm->time[ip] > t1)
14186 continue;
14187
14188 /* Get ensemble ID... */
14189 int ens_id = ensemble ? (int) atm->q[ctl->qnt_ens][ip] : 0;
14190 if (ens_id < 0 || ens_id >= (ensemble ? ctl->nens : 1))
14191 ERRMSG("Ensemble ID out of range!");
14192
14193 /* Check grid boundaries and get indices... */
14194 const double zpart = Z(atm->p[ip]);
14195 if (atm->lon[ip] < ctl->csi_lon0
14196 || atm->lon[ip] >= ctl->csi_lon1
14197 || atm->lat[ip] < ctl->csi_lat0
14198 || atm->lat[ip] >= ctl->csi_lat1
14199 || zpart < ctl->csi_z0 || zpart >= ctl->csi_z1)
14200 continue;
14201 const int ix = (int) ((atm->lon[ip] - ctl->csi_lon0) / dlon);
14202 const int iy = (int) ((atm->lat[ip] - ctl->csi_lat0) / dlat);
14203 const int iz = (int) ((zpart - ctl->csi_z0) / dz);
14204 if (ix >= ctl->csi_nx || iy >= ctl->csi_ny || iz >= ctl->csi_nz)
14205 continue;
14206
14207 /* Get total mass in grid cell... */
14208 const int idx =
14209 ens_id * grid_size + ARRAY_3D(ix, iy, ctl->csi_ny, iz, ctl->csi_nz);
14210 modmean[idx] +=
14211 kernel_weight(kz, kw, nk, atm->p[ip]) * atm->q[ctl->qnt_m][ip];
14212 }
14213 for (int e = 0; e < (ensemble ? ctl->nens : 1); e++) {
14214 /* Analyze all grid cells... */
14215 for (int ix = 0; ix < ctl->csi_nx; ix++)
14216 for (int iy = 0; iy < ctl->csi_ny; iy++)
14217 for (int iz = 0; iz < ctl->csi_nz; iz++) {
14218
14219 /* Calculate mean observation index... */
14220 const int idx = ARRAY_3D(ix, iy, ctl->csi_ny, iz, ctl->csi_nz);
14221 if (e == 0)
14222 if (obscount[idx]) {
14223 obsmean[idx] /= obscount[idx];
14224 obsstd[idx] =
14225 sqrt(obsstd[idx] / obscount[idx] - SQR(obsmean[idx]));
14226 }
14227
14228 /* Calculate model mean per ensemble... */
14229 const int midx = e * grid_size + idx;
14230 if (modmean[midx] > 0)
14231 modmean[midx] /= (1e6 * area[iy]);
14232
14233 /* Check number of observations... */
14234 if (obscount[idx]) {
14235
14236 /* Calculate CSI... */
14237 ct[e]++;
14238 if (obsmean[idx] >= ctl->csi_obsmin
14239 && modmean[midx] >= ctl->csi_modmin)
14240 cx[e]++;
14241 else if (obsmean[idx] >= ctl->csi_obsmin)
14242 cy[e]++;
14243 else if (modmean[midx] >= ctl->csi_modmin)
14244 cz[e]++;
14245
14246 /* Save data for other verification statistics... */
14247 if (obsmean[idx] >= ctl->csi_obsmin
14248 || modmean[midx] >= ctl->csi_modmin) {
14249 x[n[e]] = modmean[midx];
14250 y[n[e]] = obsmean[idx];
14251 if (modmean[midx] >= ctl->csi_modmin)
14252 obsstdn[n[e]] = obsstd[idx];
14253 if ((++n[e]) >= NCSI)
14254 ERRMSG("Too many points for statistics!");
14255 }
14256 }
14257 }
14258 /* Write output... */
14259 if (fmod(t, ctl->csi_dt_out) == 0) {
14260
14261 if (n[e] == 0)
14262 continue;
14263
14264 /* Calculate verification statistics
14265 (https://www.cawcr.gov.au/projects/verification/) ... */
14266 static double work[2 * NCSI], work2[2 * NCSI];
14267 const int n_obs = cx[e] + cy[e];
14268 const int n_for = cx[e] + cz[e];
14269 const double cx_rd = (ct[e] > 0) ? (1. * n_obs * n_for) / ct[e] : NAN;
14270 const double bias = (n_obs > 0) ? 100. * n_for / n_obs : NAN;
14271 const double pod = (n_obs > 0) ? 100. * cx[e] / n_obs : NAN;
14272 const double far = (n_for > 0) ? 100. * cz[e] / n_for : NAN;
14273 const double csi =
14274 (cx[e] + cy[e] + cz[e] >
14275 0) ? 100. * cx[e] / (cx[e] + cy[e] + cz[e]) : NAN;
14276 const double ets =
14277 (cx[e] + cy[e] + cz[e] - cx_rd >
14278 0) ? 100. * (cx[e] - cx_rd) / (cx[e] + cy[e] + cz[e] - cx_rd) : NAN;
14279 const double rho_p = gsl_stats_correlation(x, 1, y, 1, (size_t) n[e]);
14280 const double rho_s =
14281 gsl_stats_spearman(x, 1, y, 1, (size_t) n[e], work);
14282 for (int i = 0; i < n[e]; i++) {
14283 work[i] = x[i] - y[i];
14284 work2[i] = (obsstdn[i] != 0) ? work[i] / obsstdn[i] : 0;
14285 }
14286 const double mean = gsl_stats_mean(work, 1, (size_t) n[e]);
14287 const double rmse =
14288 gsl_stats_sd_with_fixed_mean(work, 1, (size_t) n[e], 0.0);
14289 const double absdev = gsl_stats_absdev_m(work, 1, (size_t) n[e], 0.0);
14290 const double loglikelihood =
14291 gsl_stats_tss_m(work2, 1, (size_t) n[e], 0.0) * -0.5;
14292
14293 /* Write... */
14294 fprintf(out,
14295 "%.2f %d %d %d %d %d %d %g %g %g %g %g %g %g %g %g %g %g %g %d\n",
14296 t, ensemble ? e : -999, cx[e], cy[e], cz[e], n_obs, n_for, bias,
14297 pod, far, csi, cx_rd, ets, rho_p, rho_s, mean, rmse, absdev,
14298 loglikelihood, n[e]);
14299
14300 /* Set counters to zero... */
14301 for (int i = 0; i < n[e]; i++)
14302 work[i] = work2[i] = x[i] = y[i] = obsstdn[i] = 0;
14303 ct[e] = cx[e] = cy[e] = cz[e] = n[e] = 0;
14304 }
14305 }
14306 /* Free... */
14307 free(modmean);
14308 free(obsmean);
14309 free(obscount);
14310 free(obsstd);
14311
14312 /* Finalize... */
14313 if (t == ctl->t_stop) {
14314
14315 /* Close output file... */
14316 fclose(out);
14317
14318 /* Free... */
14319 free(area);
14320 free(rt);
14321 free(rz);
14322 free(rlon);
14323 free(rlat);
14324 free(robs);
14325 }
14326}
14327
14328/*****************************************************************************/
14329
14331 const char *filename,
14332 const ctl_t *ctl,
14333 const atm_t *atm,
14334 const double t) {
14335
14336 if (ctl->met_coord_type != 0)
14337 ERRMSG("Only lat/lon grid supported");
14338
14339 static FILE *out;
14340
14341 static double dummy, lat, lon, qm[NQ][NENS], qs[NQ][NENS], xm[NENS][3],
14342 x[3], zm[NENS];
14343
14344 static int n[NENS];
14345
14346 /* Set timer... */
14347 SELECT_TIMER("WRITE_ENS", "OUTPUT");
14348
14349 /* Check quantities... */
14350 if (ctl->qnt_ens < 0)
14351 ERRMSG("Missing ensemble IDs!");
14352
14353 /* Set time interval... */
14354 const double t0 = t - 0.5 * ctl->dt_mod;
14355 const double t1 = t + 0.5 * ctl->dt_mod;
14356
14357 /* Init... */
14358 for (int i = 0; i < NENS; i++) {
14359 for (int iq = 0; iq < ctl->nq; iq++)
14360 qm[iq][i] = qs[iq][i] = 0;
14361 xm[i][0] = xm[i][1] = xm[i][2] = zm[i] = 0;
14362 n[i] = 0;
14363 }
14364
14365 /* Loop over air parcels... */
14366 for (int ip = 0; ip < atm->np; ip++) {
14367
14368 /* Check time... */
14369 if (atm->time[ip] < t0 || atm->time[ip] > t1)
14370 continue;
14371
14372 /* Check ensemble ID... */
14373 if (atm->q[ctl->qnt_ens][ip] < 0 || atm->q[ctl->qnt_ens][ip] >= NENS)
14374 ERRMSG("Ensemble ID is out of range!");
14375
14376 /* Get means... */
14377 geo2cart(0, atm->lon[ip], atm->lat[ip], x);
14378 for (int iq = 0; iq < ctl->nq; iq++) {
14379 qm[iq][ctl->qnt_ens] += atm->q[iq][ip];
14380 qs[iq][ctl->qnt_ens] += SQR(atm->q[iq][ip]);
14381 }
14382 xm[ctl->qnt_ens][0] += x[0];
14383 xm[ctl->qnt_ens][1] += x[1];
14384 xm[ctl->qnt_ens][2] += x[2];
14385 zm[ctl->qnt_ens] += Z(atm->p[ip]);
14386 n[ctl->qnt_ens]++;
14387 }
14388
14389 /* Create file... */
14390 LOG(1, "Write ensemble data: %s", filename);
14391 if (!(out = fopen(filename, "w")))
14392 ERRMSG("Cannot create file!");
14393
14394 /* Write header... */
14395 fprintf(out,
14396 "# $1 = time [s]\n"
14397 "# $2 = altitude [km]\n"
14398 "# $3 = longitude [deg]\n" "# $4 = latitude [deg]\n");
14399 for (int iq = 0; iq < ctl->nq; iq++)
14400 fprintf(out, "# $%d = %s (mean) [%s]\n", 5 + iq,
14401 ctl->qnt_name[iq], ctl->qnt_unit[iq]);
14402 for (int iq = 0; iq < ctl->nq; iq++)
14403 fprintf(out, "# $%d = %s (sigma) [%s]\n", 5 + ctl->nq + iq,
14404 ctl->qnt_name[iq], ctl->qnt_unit[iq]);
14405 fprintf(out, "# $%d = number of members\n\n", 5 + 2 * ctl->nq);
14406
14407 /* Write data... */
14408 for (int i = 0; i < NENS; i++)
14409 if (n[i] > 0) {
14410 cart2geo(xm[i], &dummy, &lon, &lat);
14411 fprintf(out, "%.2f %g %g %g", t, zm[i] / n[i], lon, lat);
14412 for (int iq = 0; iq < ctl->nq; iq++) {
14413 fprintf(out, " ");
14414 fprintf(out, ctl->qnt_format[iq], qm[iq][i] / n[i]);
14415 }
14416 for (int iq = 0; iq < ctl->nq; iq++) {
14417 fprintf(out, " ");
14418 double var = qs[iq][i] / n[i] - SQR(qm[iq][i] / n[i]);
14419 fprintf(out, ctl->qnt_format[iq], (var > 0 ? sqrt(var) : 0));
14420 }
14421 fprintf(out, " %d\n", n[i]);
14422 }
14423
14424 /* Close file... */
14425 fclose(out);
14426}
14427
14428/*****************************************************************************/
14429
14431 const char *filename,
14432 const ctl_t *ctl,
14433 const depo_t *depo,
14434 const double t) {
14435
14436 double *area, *data, *lat, *lon;
14437
14438 /* Set timer... */
14439 SELECT_TIMER("WRITE_DEPO", "OUTPUT");
14440 LOG(1, "Write radioactive deposition data: %s", filename);
14441
14442 /* Allocate output arrays... */
14443 const int nxy = ctl->grid_nx * ctl->grid_ny;
14444 ALLOC(area, double,
14445 ctl->grid_ny);
14446 ALLOC(data, double,
14447 4 * nxy);
14448 ALLOC(lat, double,
14449 ctl->grid_ny);
14450 ALLOC(lon, double,
14451 ctl->grid_nx);
14452
14453 /* Set horizontal coordinates and grid-cell areas... */
14454 const double dlon = (ctl->grid_lon1 - ctl->grid_lon0) / ctl->grid_nx;
14455 const double dlat = (ctl->grid_lat1 - ctl->grid_lat0) / ctl->grid_ny;
14456 for (int ix = 0; ix < ctl->grid_nx; ix++)
14457 lon[ix] = ctl->grid_lon0 + dlon * (ix + 0.5);
14458 for (int iy = 0; iy < ctl->grid_ny; iy++) {
14459 lat[iy] = ctl->grid_lat0 + dlat * (iy + 0.5);
14460 area[iy] = 1e6 * dlat * dlon * SQR(RE * M_PI / 180.)
14461 * cos(DEG2RAD(lat[iy]));
14462 }
14463
14464 /* Convert inventories to activity densities at output time... */
14465 const double lambda[4] = {
14466 log(2.0) / RADIO_HALF_LIFE_PB210,
14467 log(2.0) / RADIO_HALF_LIFE_BE7,
14468 log(2.0) / RADIO_HALF_LIFE_CS137,
14469 log(2.0) / RADIO_HALF_LIFE_I131
14470 };
14471 const double *inventory[4] = {
14472 depo->Apb210, depo->Abe7, depo->Acs137, depo->Ai131
14473 };
14474 for (int iq = 0; iq < 4; iq++) {
14475 const double decay = ctl->radio_decay
14476 ? exp(-lambda[iq] * (t - ctl->t_start)) : 1.0;
14477 for (int ix = 0; ix < ctl->grid_nx; ix++)
14478 for (int iy = 0; iy < ctl->grid_ny; iy++) {
14479 const int idx = ARRAY_2D(ix, iy, ctl->grid_ny);
14480 data[iq * nxy + idx] = inventory[iq][idx] * decay / area[iy];
14481 }
14482 }
14483
14484 /* Write output... */
14485 if (ctl->depo_type == 0)
14486 write_depo_asc(filename, ctl, data, t, lon, lat, area);
14487 else if (ctl->depo_type == 1)
14488 write_depo_nc(filename, ctl, data, t, lon, lat, area);
14489 else
14490 ERRMSG("Radioactive deposition output type unknown!");
14491
14492 /* Free... */
14493 free(area);
14494 free(data);
14495 free(lat);
14496 free(lon);
14497}
14498
14499/*****************************************************************************/
14500
14502 const char *filename,
14503 const ctl_t *ctl,
14504 const double *data,
14505 const double t,
14506 const double *lon,
14507 const double *lat,
14508 const double *area) {
14509
14510 FILE *out;
14511 if (!(out = fopen(filename, "w")))
14512 ERRMSG("Cannot create file!");
14513
14514 fprintf(out,
14515 "# $1 = time [s]\n"
14516 "# $2 = longitude [deg]\n"
14517 "# $3 = latitude [deg]\n"
14518 "# $4 = area [m^2]\n"
14519 "# $5 = deposited Pb-210 activity [Bq/m^2]\n"
14520 "# $6 = deposited Be-7 activity [Bq/m^2]\n"
14521 "# $7 = deposited Cs-137 activity [Bq/m^2]\n"
14522 "# $8 = deposited I-131 activity [Bq/m^2]\n\n");
14523
14524 const int nxy = ctl->grid_nx * ctl->grid_ny;
14525 for (int ix = 0; ix < ctl->grid_nx; ix++) {
14526 for (int iy = 0; iy < ctl->grid_ny; iy++) {
14527 const int idx = ARRAY_2D(ix, iy, ctl->grid_ny);
14528 fprintf(out, "%.2f %g %g %g %g %g %g %g\n",
14529 t, lon[ix], lat[iy], area[iy],
14530 data[idx], data[nxy + idx],
14531 data[2 * nxy + idx], data[3 * nxy + idx]);
14532 }
14533 fprintf(out, "\n");
14534 }
14535
14536 fclose(out);
14537}
14538
14539/*****************************************************************************/
14540
14542 const char *filename,
14543 const ctl_t *ctl,
14544 const double *data,
14545 const double t,
14546 const double *lon,
14547 const double *lat,
14548 const double *area) {
14549
14550 double *help;
14551 int ncid, dimid[3], varid;
14552 size_t start[2], count[2];
14553
14554 const int nxy = ctl->grid_nx * ctl->grid_ny;
14555 ALLOC(help, double,
14556 nxy);
14557
14558 /* Create file and dimensions... */
14559 NC(nc_create(filename, NC_NETCDF4, &ncid));
14560 NC(nc_def_dim(ncid, "time", 1, &dimid[0]));
14561 NC(nc_def_dim(ncid, "lat", (size_t) ctl->grid_ny, &dimid[1]));
14562 NC(nc_def_dim(ncid, "lon", (size_t) ctl->grid_nx, &dimid[2]));
14563
14564 /* Define variables... */
14565 NC_DEF_VAR("time", NC_DOUBLE, 1, &dimid[0], "time",
14566 "seconds since 2000-01-01 00:00:00 UTC", 0, 0);
14567 NC_DEF_VAR("lat", NC_DOUBLE, 1, &dimid[1], "latitude",
14568 "degrees_north", 0, 0);
14569 NC_DEF_VAR("lon", NC_DOUBLE, 1, &dimid[2], "longitude",
14570 "degrees_east", 0, 0);
14571 NC_DEF_VAR("area", NC_DOUBLE, 1, &dimid[1], "surface area", "m**2", 0, 0);
14572 NC_DEF_VAR("depo_pb210", NC_DOUBLE, 3, dimid,
14573 "ground inventory of Pb-210", "Bq m**-2", ctl->grid_nc_level, 0);
14574 NC_DEF_VAR("depo_be7", NC_DOUBLE, 3, dimid,
14575 "ground inventory of Be-7", "Bq m**-2", ctl->grid_nc_level, 0);
14576 NC_DEF_VAR("depo_cs137", NC_DOUBLE, 3, dimid,
14577 "ground inventory of Cs-137", "Bq m**-2", ctl->grid_nc_level, 0);
14578 NC_DEF_VAR("depo_i131", NC_DOUBLE, 3, dimid,
14579 "ground inventory of aerosol-bound I-131", "Bq m**-2",
14580 ctl->grid_nc_level, 0);
14581 NC(nc_enddef(ncid));
14582
14583 /* Write coordinates... */
14584 NC_PUT_DOUBLE("time", &t, 0);
14585 NC_PUT_DOUBLE("lon", lon, 0);
14586 NC_PUT_DOUBLE("lat", lat, 0);
14587 NC_PUT_DOUBLE("area", area, 0);
14588
14589 /* Write fields in netCDF dimension order... */
14590 const char *varname[4] = {
14591 "depo_pb210", "depo_be7", "depo_cs137", "depo_i131"
14592 };
14593 for (int iq = 0; iq < 4; iq++) {
14594 for (int ix = 0; ix < ctl->grid_nx; ix++)
14595 for (int iy = 0; iy < ctl->grid_ny; iy++)
14596 help[ARRAY_2D(iy, ix, ctl->grid_nx)] =
14597 data[iq * nxy + ARRAY_2D(ix, iy, ctl->grid_ny)];
14598 NC_PUT_DOUBLE(varname[iq], help, 0);
14599 }
14600
14601 NC(nc_close(ncid));
14602 free(help);
14603}
14604
14605/*****************************************************************************/
14606
14608 const char *filename,
14609 const ctl_t *ctl,
14610 met_t *met0,
14611 met_t *met1,
14612 const atm_t *atm,
14613 const double t) {
14614
14615 if (ctl->met_coord_type != 0)
14616 ERRMSG("Only lat/lon grid supported");
14617
14618 static double kz[EP], kw[EP];
14619
14620 static int nk;
14621
14622 double *cd, *mean[NQ], *sigma[NQ], *vmr_impl, *z, *lon, *lat, *area, *press;
14623
14624 int *ixs, *iys, *izs, *np;
14625
14626 /* Set timer... */
14627 SELECT_TIMER("WRITE_GRID", "OUTPUT");
14628
14629 /* Write info... */
14630 LOG(1, "Write grid data: %s", filename);
14631
14632 /* Init... */
14633 if (t == ctl->t_start) {
14634
14635 /* Read kernel data... */
14636 if (ctl->grid_kernel[0] != '-')
14637 read_kernel(ctl->grid_kernel, kz, kw, &nk);
14638 }
14639
14640 /* Allocate... */
14641 ALLOC(cd, double,
14642 ctl->grid_nx * ctl->grid_ny * ctl->grid_nz);
14643 for (int iq = 0; iq < ctl->nq; iq++) {
14644 ALLOC(mean[iq], double,
14645 ctl->grid_nx * ctl->grid_ny * ctl->grid_nz);
14646 ALLOC(sigma[iq], double,
14647 ctl->grid_nx * ctl->grid_ny * ctl->grid_nz);
14648 }
14649 ALLOC(vmr_impl, double,
14650 ctl->grid_nx * ctl->grid_ny * ctl->grid_nz);
14651 ALLOC(z, double,
14652 ctl->grid_nz);
14653 ALLOC(lon, double,
14654 ctl->grid_nx);
14655 ALLOC(lat, double,
14656 ctl->grid_ny);
14657 ALLOC(area, double,
14658 ctl->grid_ny);
14659 ALLOC(press, double,
14660 ctl->grid_nz);
14661 ALLOC(np, int,
14662 ctl->grid_nx * ctl->grid_ny * ctl->grid_nz);
14663 ALLOC(ixs, int,
14664 atm->np);
14665 ALLOC(iys, int,
14666 atm->np);
14667 ALLOC(izs, int,
14668 atm->np);
14669
14670 /* Set grid box size... */
14671 const double dz = (ctl->grid_z1 - ctl->grid_z0) / ctl->grid_nz;
14672 const double dlon = (ctl->grid_lon1 - ctl->grid_lon0) / ctl->grid_nx;
14673 const double dlat = (ctl->grid_lat1 - ctl->grid_lat0) / ctl->grid_ny;
14674
14675 /* Set vertical coordinates... */
14676#pragma omp parallel for default(shared)
14677 for (int iz = 0; iz < ctl->grid_nz; iz++) {
14678 z[iz] = ctl->grid_z0 + dz * (iz + 0.5);
14679 press[iz] = P(z[iz]);
14680 }
14681
14682 /* Set horizontal coordinates... */
14683 for (int ix = 0; ix < ctl->grid_nx; ix++)
14684 lon[ix] = ctl->grid_lon0 + dlon * (ix + 0.5);
14685#pragma omp parallel for default(shared)
14686 for (int iy = 0; iy < ctl->grid_ny; iy++) {
14687 lat[iy] = ctl->grid_lat0 + dlat * (iy + 0.5);
14688 area[iy] = dlat * dlon * SQR(RE * M_PI / 180.) * cos(DEG2RAD(lat[iy]));
14689 }
14690
14691 /* Set time interval for output... */
14692 const double t0 = t - 0.5 * ctl->dt_mod;
14693 const double t1 = t + 0.5 * ctl->dt_mod;
14694
14695 /* Get grid box indices... */
14696#pragma omp parallel for default(shared)
14697 for (int ip = 0; ip < atm->np; ip++) {
14698 const double zpart = Z(atm->p[ip]);
14699 if (atm->time[ip] < t0 || atm->time[ip] > t1
14700 || atm->lon[ip] < ctl->grid_lon0
14701 || atm->lon[ip] >= ctl->grid_lon1
14702 || atm->lat[ip] < ctl->grid_lat0
14703 || atm->lat[ip] >= ctl->grid_lat1
14704 || zpart < ctl->grid_z0 || zpart >= ctl->grid_z1) {
14705 izs[ip] = -1;
14706 continue;
14707 }
14708 ixs[ip] = (int) ((atm->lon[ip] - ctl->grid_lon0) / dlon);
14709 iys[ip] = (int) ((atm->lat[ip] - ctl->grid_lat0) / dlat);
14710 izs[ip] = (int) ((zpart - ctl->grid_z0) / dz);
14711 if (ixs[ip] >= ctl->grid_nx || iys[ip] >= ctl->grid_ny
14712 || izs[ip] >= ctl->grid_nz)
14713 izs[ip] = -1;
14714 }
14715
14716 /* Average data... */
14717 for (int ip = 0; ip < atm->np; ip++)
14718 if (izs[ip] >= 0) {
14719 const int idx =
14720 ARRAY_3D(ixs[ip], iys[ip], ctl->grid_ny, izs[ip], ctl->grid_nz);
14721 const double kernel = kernel_weight(kz, kw, nk, atm->p[ip]);
14722 np[idx]++;
14723 for (int iq = 0; iq < ctl->nq; iq++) {
14724 mean[iq][idx] += kernel * atm->q[iq][ip];
14725 sigma[iq][idx] += SQR(kernel * atm->q[iq][ip]);
14726 }
14727 }
14728
14729 /* Calculate column density and volume mixing ratio... */
14730#pragma omp parallel for default(shared)
14731 for (int ix = 0; ix < ctl->grid_nx; ix++)
14732 for (int iy = 0; iy < ctl->grid_ny; iy++)
14733 for (int iz = 0; iz < ctl->grid_nz; iz++) {
14734
14735 /* Get grid index... */
14736 const int idx = ARRAY_3D(ix, iy, ctl->grid_ny, iz, ctl->grid_nz);
14737
14738 /* Calculate column density... */
14739 cd[idx] = NAN;
14740 if (ctl->qnt_m >= 0)
14741 cd[idx] = mean[ctl->qnt_m][idx] / (1e6 * area[iy]);
14742
14743 /* Calculate volume mixing ratio (implicit)... */
14744 vmr_impl[idx] = NAN;
14745 if (ctl->qnt_m >= 0 && ctl->molmass > 0 && met0 != NULL
14746 && met1 != NULL) {
14747 vmr_impl[idx] = 0;
14748 if (mean[ctl->qnt_m][idx] > 0) {
14749
14750 /* Get temperature... */
14751 double temp;
14753 intpol_met_time_3d(met0, met0->t, met1, met1->t, t, press[iz],
14754 lon[ix], lat[iy], &temp, ci, cw, 1);
14755
14756 /* Calculate volume mixing ratio... */
14757 vmr_impl[idx] =
14758 MA / ctl->molmass * cd[idx] / (RHO(press[iz], temp) * dz * 1e3);
14759 }
14760 }
14761
14762 /* Calculate mean... */
14763 if (np[idx] > 0)
14764 for (int iq = 0; iq < ctl->nq; iq++) {
14765 mean[iq][idx] /= np[idx];
14766 const double var = sigma[iq][idx] / np[idx] - SQR(mean[iq][idx]);
14767 sigma[iq][idx] = (var > 0 ? sqrt(var) : 0);
14768 } else
14769 for (int iq = 0; iq < ctl->nq; iq++) {
14770 mean[iq][idx] = NAN;
14771 sigma[iq][idx] = NAN;
14772 }
14773 }
14774
14775 /* Write ASCII data... */
14776 if (ctl->grid_type == 0)
14777 write_grid_asc(filename, ctl, cd, mean, sigma, vmr_impl,
14778 t, z, lon, lat, area, dz, np);
14779
14780 /* Write netCDF data... */
14781 else if (ctl->grid_type == 1)
14782 write_grid_nc(filename, ctl, cd, mean, sigma, vmr_impl,
14783 t, z, lon, lat, area, dz, np);
14784
14785 /* Error message... */
14786 else
14787 ERRMSG("Grid data format GRID_TYPE unknown!");
14788
14789 /* Free... */
14790 free(cd);
14791 for (int iq = 0; iq < ctl->nq; iq++) {
14792 free(mean[iq]);
14793 free(sigma[iq]);
14794 }
14795 free(vmr_impl);
14796 free(z);
14797 free(lon);
14798 free(lat);
14799 free(area);
14800 free(press);
14801 free(np);
14802 free(ixs);
14803 free(iys);
14804 free(izs);
14805}
14806
14807/*****************************************************************************/
14808
14810 const char *filename,
14811 const ctl_t *ctl,
14812 const double *cd,
14813 double *mean[NQ],
14814 double *sigma[NQ],
14815 const double *vmr_impl,
14816 const double t,
14817 const double *z,
14818 const double *lon,
14819 const double *lat,
14820 const double *area,
14821 const double dz,
14822 const int *np) {
14823
14824 FILE *out;
14825
14826 /* Check if gnuplot output is requested... */
14827 if (ctl->grid_gpfile[0] != '-') {
14828
14829 /* Create gnuplot pipe... */
14830 if (!(out = popen("gnuplot", "w")))
14831 ERRMSG("Cannot create pipe to gnuplot!");
14832
14833 /* Set plot filename... */
14834 fprintf(out, "set out \"%s.png\"\n", filename);
14835
14836 /* Set time string... */
14837 double r;
14838 int year, mon, day, hour, min, sec;
14839 jsec2time(t, &year, &mon, &day, &hour, &min, &sec, &r);
14840 fprintf(out, "timestr=\"%d-%02d-%02d, %02d:%02d UTC\"\n",
14841 year, mon, day, hour, min);
14842
14843 /* Dump gnuplot file to pipe... */
14844 FILE *in;
14845 char line[LEN];
14846 if (!(in = fopen(ctl->grid_gpfile, "r")))
14847 ERRMSG("Cannot open file!");
14848 while (fgets(line, LEN, in))
14849 fprintf(out, "%s", line);
14850 fclose(in);
14851 }
14852
14853 else {
14854
14855 /* Create file... */
14856 if (!(out = fopen(filename, "w")))
14857 ERRMSG("Cannot create file!");
14858 }
14859
14860 /* Write header... */
14861 fprintf(out,
14862 "# $1 = time [s]\n"
14863 "# $2 = altitude [km]\n"
14864 "# $3 = longitude [deg]\n"
14865 "# $4 = latitude [deg]\n"
14866 "# $5 = surface area [km^2]\n"
14867 "# $6 = layer depth [km]\n"
14868 "# $7 = column density (implicit) [kg/m^2]\n"
14869 "# $8 = volume mixing ratio (implicit) [ppv]\n"
14870 "# $9 = number of particles [1]\n");
14871 for (int iq = 0; iq < ctl->nq; iq++)
14872 fprintf(out, "# $%i = %s (mean) [%s]\n", 10 + iq, ctl->qnt_name[iq],
14873 ctl->qnt_unit[iq]);
14874 if (ctl->grid_stddev)
14875 for (int iq = 0; iq < ctl->nq; iq++)
14876 fprintf(out, "# $%i = %s (stddev) [%s]\n", 10 + ctl->nq + iq,
14877 ctl->qnt_name[iq], ctl->qnt_unit[iq]);
14878 fprintf(out, "\n");
14879
14880 /* Write data... */
14881 for (int ix = 0; ix < ctl->grid_nx; ix++) {
14882 if (ix > 0 && ctl->grid_ny > 1 && !ctl->grid_sparse)
14883 fprintf(out, "\n");
14884 for (int iy = 0; iy < ctl->grid_ny; iy++) {
14885 if (iy > 0 && ctl->grid_nz > 1 && !ctl->grid_sparse)
14886 fprintf(out, "\n");
14887 for (int iz = 0; iz < ctl->grid_nz; iz++) {
14888 int idx = ARRAY_3D(ix, iy, ctl->grid_ny, iz, ctl->grid_nz);
14889 if (!ctl->grid_sparse || vmr_impl[idx] > 0) {
14890 fprintf(out, "%.2f %g %g %g %g %g %g %g %d", t, z[iz], lon[ix],
14891 lat[iy], area[iy], dz, cd[idx], vmr_impl[idx], np[idx]);
14892 for (int iq = 0; iq < ctl->nq; iq++) {
14893 fprintf(out, " ");
14894 fprintf(out, ctl->qnt_format[iq], mean[iq][idx]);
14895 }
14896 if (ctl->grid_stddev)
14897 for (int iq = 0; iq < ctl->nq; iq++) {
14898 fprintf(out, " ");
14899 fprintf(out, ctl->qnt_format[iq], sigma[iq][idx]);
14900 }
14901 fprintf(out, "\n");
14902 }
14903 }
14904 }
14905 }
14906
14907 /* Close file... */
14908 fclose(out);
14909}
14910
14911/*****************************************************************************/
14912
14914 const char *filename,
14915 const ctl_t *ctl,
14916 const double *cd,
14917 double *mean[NQ],
14918 double *sigma[NQ],
14919 const double *vmr_impl,
14920 const double t,
14921 const double *z,
14922 const double *lon,
14923 const double *lat,
14924 const double *area,
14925 const double dz,
14926 const int *np) {
14927
14928 char longname[2 * LEN], varname[2 * LEN];
14929
14930 double *help;
14931
14932 int *help2, ncid, dimid[10], varid;
14933
14934 size_t start[2], count[2];
14935
14936 /* Allocate... */
14937 ALLOC(help, double,
14938 ctl->grid_nx * ctl->grid_ny * ctl->grid_nz);
14939 ALLOC(help2, int,
14940 ctl->grid_nx * ctl->grid_ny * ctl->grid_nz);
14941
14942 /* Create file... */
14943 NC(nc_create(filename, NC_NETCDF4, &ncid));
14944
14945 /* Define dimensions... */
14946 NC(nc_def_dim(ncid, "time", 1, &dimid[0]));
14947 NC(nc_def_dim(ncid, "z", (size_t) ctl->grid_nz, &dimid[1]));
14948 NC(nc_def_dim(ncid, "lat", (size_t) ctl->grid_ny, &dimid[2]));
14949 NC(nc_def_dim(ncid, "lon", (size_t) ctl->grid_nx, &dimid[3]));
14950 NC(nc_def_dim(ncid, "dz", 1, &dimid[4]));
14951
14952 /* Define variables and their attributes... */
14953 NC_DEF_VAR("time", NC_DOUBLE, 1, &dimid[0], "time",
14954 "seconds since 2000-01-01 00:00:00 UTC", 0, 0);
14955 NC_DEF_VAR("z", NC_DOUBLE, 1, &dimid[1], "altitude", "km", 0, 0);
14956 NC_DEF_VAR("lat", NC_DOUBLE, 1, &dimid[2], "latitude", "degrees_north", 0,
14957 0);
14958 NC_DEF_VAR("lon", NC_DOUBLE, 1, &dimid[3], "longitude", "degrees_east", 0,
14959 0);
14960 NC_DEF_VAR("dz", NC_DOUBLE, 1, &dimid[1], "layer depth", "km", 0, 0);
14961 NC_DEF_VAR("area", NC_DOUBLE, 1, &dimid[2], "surface area", "km**2", 0, 0);
14962
14963 NC_DEF_VAR("cd", NC_FLOAT, 4, dimid, "column density", "kg m**-2",
14964 ctl->grid_nc_level, 0);
14965 NC_DEF_VAR("vmr_impl", NC_FLOAT, 4, dimid,
14966 "volume mixing ratio (implicit)", "ppv", ctl->grid_nc_level, 0);
14967 NC_DEF_VAR("np", NC_INT, 4, dimid, "number of particles", "1", 0, 0);
14968 for (int iq = 0; iq < ctl->nq; iq++) {
14969 sprintf(varname, "%s_mean", ctl->qnt_name[iq]);
14970 sprintf(longname, "%s (mean)", ctl->qnt_longname[iq]);
14971 NC_DEF_VAR(varname, NC_DOUBLE, 4, dimid, longname, ctl->qnt_unit[iq],
14972 ctl->grid_nc_level, ctl->grid_nc_quant[iq]);
14973 if (ctl->grid_stddev) {
14974 sprintf(varname, "%s_stddev", ctl->qnt_name[iq]);
14975 sprintf(longname, "%s (stddev)", ctl->qnt_longname[iq]);
14976 NC_DEF_VAR(varname, NC_DOUBLE, 4, dimid, longname, ctl->qnt_unit[iq],
14977 ctl->grid_nc_level, ctl->grid_nc_quant[iq]);
14978 }
14979 }
14980 /* End definitions... */
14981 NC(nc_enddef(ncid));
14982
14983 /* Write data... */
14984 NC_PUT_DOUBLE("time", &t, 0);
14985 NC_PUT_DOUBLE("lon", lon, 0);
14986 NC_PUT_DOUBLE("lat", lat, 0);
14987 NC_PUT_DOUBLE("z", z, 0);
14988 NC_PUT_DOUBLE("area", area, 0);
14989 NC_PUT_DOUBLE("dz", &dz, 0);
14990
14991 for (int ix = 0; ix < ctl->grid_nx; ix++)
14992 for (int iy = 0; iy < ctl->grid_ny; iy++)
14993 for (int iz = 0; iz < ctl->grid_nz; iz++)
14994 help[ARRAY_3D(iz, iy, ctl->grid_ny, ix, ctl->grid_nx)] =
14995 cd[ARRAY_3D(ix, iy, ctl->grid_ny, iz, ctl->grid_nz)];
14996 NC_PUT_DOUBLE("cd", help, 0);
14997
14998 for (int ix = 0; ix < ctl->grid_nx; ix++)
14999 for (int iy = 0; iy < ctl->grid_ny; iy++)
15000 for (int iz = 0; iz < ctl->grid_nz; iz++)
15001 help[ARRAY_3D(iz, iy, ctl->grid_ny, ix, ctl->grid_nx)] =
15002 vmr_impl[ARRAY_3D(ix, iy, ctl->grid_ny, iz, ctl->grid_nz)];
15003 NC_PUT_DOUBLE("vmr_impl", help, 0);
15004
15005 for (int ix = 0; ix < ctl->grid_nx; ix++)
15006 for (int iy = 0; iy < ctl->grid_ny; iy++)
15007 for (int iz = 0; iz < ctl->grid_nz; iz++)
15008 help2[ARRAY_3D(iz, iy, ctl->grid_ny, ix, ctl->grid_nx)] =
15009 np[ARRAY_3D(ix, iy, ctl->grid_ny, iz, ctl->grid_nz)];
15010 NC_PUT_INT("np", help2, 0);
15011
15012 for (int iq = 0; iq < ctl->nq; iq++) {
15013 sprintf(varname, "%s_mean", ctl->qnt_name[iq]);
15014 for (int ix = 0; ix < ctl->grid_nx; ix++)
15015 for (int iy = 0; iy < ctl->grid_ny; iy++)
15016 for (int iz = 0; iz < ctl->grid_nz; iz++)
15017 help[ARRAY_3D(iz, iy, ctl->grid_ny, ix, ctl->grid_nx)] =
15018 mean[iq][ARRAY_3D(ix, iy, ctl->grid_ny, iz, ctl->grid_nz)];
15019 NC_PUT_DOUBLE(varname, help, 0);
15020 }
15021
15022 if (ctl->grid_stddev)
15023 for (int iq = 0; iq < ctl->nq; iq++) {
15024 sprintf(varname, "%s_stddev", ctl->qnt_name[iq]);
15025 for (int ix = 0; ix < ctl->grid_nx; ix++)
15026 for (int iy = 0; iy < ctl->grid_ny; iy++)
15027 for (int iz = 0; iz < ctl->grid_nz; iz++)
15028 help[ARRAY_3D(iz, iy, ctl->grid_ny, ix, ctl->grid_nx)] =
15029 sigma[iq][ARRAY_3D(ix, iy, ctl->grid_ny, iz, ctl->grid_nz)];
15030 NC_PUT_DOUBLE(varname, help, 0);
15031 }
15032
15033 /* Close file... */
15034 NC(nc_close(ncid));
15035
15036 /* Free... */
15037 free(help);
15038 free(help2);
15039}
15040
15041/*****************************************************************************/
15042
15044 const char *filename,
15045 const ctl_t *ctl,
15046 met_t *met) {
15047
15048 /* Create file... */
15049 FILE *out, *level_log = NULL;
15050 if (!(out = fopen(filename, "w")))
15051 ERRMSG("Cannot create file!");
15052
15053 /* Create diagnostics file... */
15054 if (strcmp(ctl->met_comp_logfile, "-") != 0) {
15055
15056 if (!(level_log = fopen(ctl->met_comp_logfile, "w")))
15057 ERRMSG("Cannot create compression log file!");
15058 LOG(1, "Write compression diagnostics: %s", ctl->met_comp_logfile);
15059
15060 /* Write header... */
15061 fprintf(level_log,
15062 "# $1 = compression codec name [-]\n"
15063 "# $2 = variable name [-]\n"
15064 "# $3 = level index [-]\n"
15065 "# $4 = pressure level [hPa]\n"
15066 "# $5 = compression ratio [-]\n"
15067 "# $6 = bits per value [bit/value]\n"
15068 "# $7 = correlation coefficient [-]\n"
15069 "# $8 = mean compression error [-]\n"
15070 "# $9 = standard deviation of compression error [-]\n"
15071 "# $10 = minimum compression error [-]\n"
15072 "# $11 = maximum compression error [-]\n"
15073 "# $12 = mean value of original field [-]\n"
15074 "# $13 = value range of original field [-]\n"
15075 "# $14 = normalized root mean square error [-]\n"
15076 "# $15 = compression time [s]\n"
15077 "# $16 = compression speed [MiB/s]\n"
15078 "# $17 = decompression time [s]\n"
15079 "# $18 = decompression speed [MiB/s]\n\n");
15080 }
15081
15082 /* Write type of binary data... */
15083 FWRITE(&ctl->met_type, int,
15084 1,
15085 out);
15086
15087 /* Write version of binary data... */
15088 int version = 104;
15089 FWRITE(&version, int,
15090 1,
15091 out);
15092
15093 /* Write grid data... */
15094 FWRITE(&met->time, double,
15095 1,
15096 out);
15097 FWRITE(&met->nx, int,
15098 1,
15099 out);
15100 FWRITE(&met->ny, int,
15101 1,
15102 out);
15103 FWRITE(&met->np, int,
15104 1,
15105 out);
15106 FWRITE(met->lon, double,
15107 (size_t) met->nx,
15108 out);
15109 FWRITE(met->lat, double,
15110 (size_t) met->ny,
15111 out);
15112 FWRITE(met->p, double,
15113 (size_t) met->np,
15114 out);
15115
15116 /* Write surface data... */
15117 write_met_bin_2d(out, met, met->ps, "PS");
15118 write_met_bin_2d(out, met, met->ts, "TS");
15119 write_met_bin_2d(out, met, met->zs, "ZS");
15120 write_met_bin_2d(out, met, met->us, "US");
15121 write_met_bin_2d(out, met, met->vs, "VS");
15122 write_met_bin_2d(out, met, met->ess, "ESS");
15123 write_met_bin_2d(out, met, met->nss, "NSS");
15124 write_met_bin_2d(out, met, met->shf, "SHF");
15125 write_met_bin_2d(out, met, met->lsm, "LSM");
15126 write_met_bin_2d(out, met, met->sst, "SST");
15127 write_met_bin_2d(out, met, met->pbl, "PBL");
15128 write_met_bin_2d(out, met, met->pt, "PT");
15129 write_met_bin_2d(out, met, met->tt, "TT");
15130 write_met_bin_2d(out, met, met->zt, "ZT");
15131 write_met_bin_2d(out, met, met->h2ot, "H2OT");
15132 write_met_bin_2d(out, met, met->pct, "PCT");
15133 write_met_bin_2d(out, met, met->pcb, "PCB");
15134 write_met_bin_2d(out, met, met->cl, "CL");
15135 write_met_bin_2d(out, met, met->plcl, "PLCL");
15136 write_met_bin_2d(out, met, met->plfc, "PLFC");
15137 write_met_bin_2d(out, met, met->pel, "PEL");
15138 write_met_bin_2d(out, met, met->cape, "CAPE");
15139 write_met_bin_2d(out, met, met->cin, "CIN");
15140 write_met_bin_2d(out, met, met->o3c, "O3C");
15141
15142 /* Write level data... */
15143 write_met_bin_3d(out, ctl, met, met->z, "Z", 0, level_log);
15144 write_met_bin_3d(out, ctl, met, met->t, "T", 1, level_log);
15145 write_met_bin_3d(out, ctl, met, met->u, "U", 2, level_log);
15146 write_met_bin_3d(out, ctl, met, met->v, "V", 3, level_log);
15147 write_met_bin_3d(out, ctl, met, met->w, "W", 4, level_log);
15148 write_met_bin_3d(out, ctl, met, met->pv, "PV", 5, level_log);
15149 write_met_bin_3d(out, ctl, met, met->h2o, "H2O", 6, level_log);
15150 write_met_bin_3d(out, ctl, met, met->o3, "O3", 7, level_log);
15151 write_met_bin_3d(out, ctl, met, met->lwc, "LWC", 8, level_log);
15152 write_met_bin_3d(out, ctl, met, met->rwc, "RWC", 9, level_log);
15153 write_met_bin_3d(out, ctl, met, met->iwc, "IWC", 10, level_log);
15154 write_met_bin_3d(out, ctl, met, met->swc, "SWC", 11, level_log);
15155 write_met_bin_3d(out, ctl, met, met->cc, "CC", 12, level_log);
15156 if (METVAR != 13)
15157 ERRMSG("Number of meteo variables doesn't match!");
15158
15159 /* Write final flag... */
15160 int final = 999;
15161 FWRITE(&final, int,
15162 1,
15163 out);
15164
15165 /* Close file... */
15166 if (level_log)
15167 fclose(level_log);
15168 fclose(out);
15169}
15170
15171/*****************************************************************************/
15172
15174 FILE *out,
15175 met_t *met,
15176 float var[EX][EY],
15177 const char *varname) {
15178
15179 float *help;
15180
15181 /* Allocate... */
15182 ALLOC(help, float,
15183 EX * EY);
15184
15185 /* Copy data... */
15186 for (int ix = 0; ix < met->nx; ix++)
15187 for (int iy = 0; iy < met->ny; iy++)
15188 help[ARRAY_2D(ix, iy, met->ny)] = var[ix][iy];
15189
15190 /* Write uncompressed data... */
15191 LOG(2, "Write 2-D variable: %s (uncompressed)", varname);
15192 FWRITE(help, float,
15193 (size_t) (met->nx * met->ny),
15194 out);
15195
15196 /* Free... */
15197 free(help);
15198}
15199
15200/*****************************************************************************/
15201
15203 FILE *out,
15204 const ctl_t *ctl,
15205 met_t *met,
15206 float var[EX][EY][EP],
15207 const char *varname,
15208 const int metvar,
15209 FILE *level_log) {
15210
15211 float *help;
15212
15213 /* Allocate... */
15214 ALLOC(help, float,
15215 EX * EY * EP);
15216
15217 /* Copy data... */
15218#pragma omp parallel for default(shared) collapse(2)
15219 for (int ix = 0; ix < met->nx; ix++)
15220 for (int iy = 0; iy < met->ny; iy++)
15221 for (int ip = 0; ip < met->np; ip++)
15222 help[ARRAY_3D(ix, iy, met->ny, ip, met->np)] = var[ix][iy][ip];
15223
15224 /* Write uncompressed data... */
15225 if (ctl->met_type == 1) {
15226 LOG(2, "Write 3-D variable: %s (uncompressed)", varname);
15227 FWRITE(help, float,
15228 (size_t) (met->nx * met->ny * met->np),
15229 out);
15230 }
15231
15232 /* Write packed data... */
15233 else if (ctl->met_type == 2)
15234 compress_pck(ctl, met, varname, help, 0, level_log, out);
15235
15236 /* Write ZFP data... */
15237#ifdef ZFP
15238 else if (ctl->met_type == 3) {
15239 FWRITE(&ctl->met_zfp_prec[metvar], int,
15240 1,
15241 out);
15242 FWRITE(&ctl->met_zfp_tol[metvar], double,
15243 1,
15244 out);
15245 compress_zfp(ctl, met, varname, help, 0, level_log, out);
15246 }
15247#endif
15248
15249 /* Write zstd data... */
15250#ifdef ZSTD
15251 else if (ctl->met_type == 4)
15252 compress_zstd(ctl, met, varname, help, 0, level_log, out);
15253#endif
15254
15255 /* Write LZ4 data... */
15256#ifdef LZ4
15257 else if (ctl->met_type == 8)
15258 compress_lz4(ctl, met, varname, help, 0, level_log, out);
15259#endif
15260
15261 /* Write cmultiscale data... */
15262#ifdef CMS
15263 else if (ctl->met_type == 5) {
15264 compress_cms(ctl, met, varname, help, 0, level_log, out);
15265 }
15266#endif
15267
15268 /* Write SZ3 data... */
15269#ifdef SZ3
15270 else if (ctl->met_type == 7) {
15271 FWRITE(&ctl->met_sz3_prec[metvar], int,
15272 1,
15273 out);
15274 FWRITE(&ctl->met_sz3_tol[metvar], double,
15275 1,
15276 out);
15277 compress_sz3(ctl, met, varname, help, 0, level_log, out);
15278 }
15279#endif
15280
15281 /* Unknown method... */
15282 else {
15283 ERRMSG("MET_TYPE not supported!");
15284
15285 /* This will never execute, hack to avoid compilation error... */
15286 LOG(3, "%d", metvar);
15287 }
15288
15289 /* Free... */
15290 free(help);
15291}
15292
15293/*****************************************************************************/
15294
15296 const char *filename,
15297 const ctl_t *ctl,
15298 met_t *met) {
15299
15300 /* Create file... */
15301 int ncid, varid;
15302 size_t start[4], count[4];
15303 NC(nc_create(filename, NC_NETCDF4, &ncid));
15304
15305 /* Define dimensions... */
15306 int tid, lonid, latid, levid;
15307 NC(nc_def_dim(ncid, "time", 1, &tid));
15308
15309 if (met->coord_type == 0) {
15310 NC(nc_def_dim(ncid, "lon", (size_t) met->nx, &lonid));
15311 NC(nc_def_dim(ncid, "lat", (size_t) met->ny, &latid));
15312 NC_DEF_VAR("lon", NC_DOUBLE, 1, &lonid, "longitude", "degrees_east", 0,
15313 0);
15314 NC_DEF_VAR("lat", NC_DOUBLE, 1, &latid, "latitude", "degrees_north", 0,
15315 0);
15316 } else {
15317 NC(nc_def_dim(ncid, "x", (size_t) met->nx, &lonid));
15318 NC(nc_def_dim(ncid, "y", (size_t) met->ny, &latid));
15319 NC_DEF_VAR("x", NC_DOUBLE, 1, &lonid, "x", "easting", 0, 0);
15320 NC_DEF_VAR("y", NC_DOUBLE, 1, &latid, "y", "northing", 0, 0);
15321 }
15322
15323 NC(nc_def_dim(ncid, "lev", (size_t) met->np, &levid));
15324
15325 /* Define grid... */
15326 NC_DEF_VAR("time", NC_DOUBLE, 1, &tid, "time",
15327 "seconds since 2000-01-01 00:00:00 UTC", 0, 0);
15328 NC_DEF_VAR("lev", NC_DOUBLE, 1, &levid, "pressure", "Pa", 0, 0);
15329
15330 /* Define surface variables... */
15331 int dimid2[3] = { tid, latid, lonid };
15332 NC_DEF_VAR("sp", NC_FLOAT, 3, dimid2, "Surface pressure", "Pa",
15333 ctl->met_nc_level, 0);
15334 NC_DEF_VAR("z", NC_FLOAT, 3, dimid2, "Geopotential", "m**2 s**-2",
15335 ctl->met_nc_level, 0);
15336 NC_DEF_VAR("t2m", NC_FLOAT, 3, dimid2, "2 metre temperature", "K",
15337 ctl->met_nc_level, 0);
15338 NC_DEF_VAR("u10m", NC_FLOAT, 3, dimid2, "10 metre U wind component",
15339 "m s**-1", ctl->met_nc_level, 0);
15340 NC_DEF_VAR("v10m", NC_FLOAT, 3, dimid2, "10 metre V wind component",
15341 "m s**-1", ctl->met_nc_level, 0);
15342 NC_DEF_VAR("iews", NC_FLOAT, 3, dimid2,
15343 "Instantaneous eastward turbulent surface stress", "N m**-2",
15344 ctl->met_nc_level, 0);
15345 NC_DEF_VAR("inss", NC_FLOAT, 3, dimid2,
15346 "Instantaneous northward turbulent surface stress", "N m**-2",
15347 ctl->met_nc_level, 0);
15348 NC_DEF_VAR("ishf", NC_FLOAT, 3, dimid2,
15349 "Instantaneous surface sensible heat flux", "W m**-2",
15350 ctl->met_nc_level, 0);
15351 NC_DEF_VAR("lsm", NC_FLOAT, 3, dimid2, "Land/sea mask", "-",
15352 ctl->met_nc_level, 0);
15353 NC_DEF_VAR("sstk", NC_FLOAT, 3, dimid2, "Sea surface temperature", "K",
15354 ctl->met_nc_level, 0);
15355 NC_DEF_VAR("blp", NC_FLOAT, 3, dimid2, "Boundary layer pressure", "Pa",
15356 ctl->met_nc_level, 0);
15357 NC_DEF_VAR("pt", NC_FLOAT, 3, dimid2, "Tropopause pressure", "Pa",
15358 ctl->met_nc_level, 0);
15359 NC_DEF_VAR("tt", NC_FLOAT, 3, dimid2, "Tropopause temperature", "K",
15360 ctl->met_nc_level, 0);
15361 NC_DEF_VAR("zt", NC_FLOAT, 3, dimid2, "Tropopause height", "m",
15362 ctl->met_nc_level, 0);
15363 NC_DEF_VAR("h2ot", NC_FLOAT, 3, dimid2, "Tropopause water vapor", "ppv",
15364 ctl->met_nc_level, 0);
15365 NC_DEF_VAR("pct", NC_FLOAT, 3, dimid2, "Cloud top pressure", "Pa",
15366 ctl->met_nc_level, 0);
15367 NC_DEF_VAR("pcb", NC_FLOAT, 3, dimid2, "Cloud bottom pressure", "Pa",
15368 ctl->met_nc_level, 0);
15369 NC_DEF_VAR("cl", NC_FLOAT, 3, dimid2, "Total column cloud water",
15370 "kg m**2", ctl->met_nc_level, 0);
15371 NC_DEF_VAR("plcl", NC_FLOAT, 3, dimid2,
15372 "Pressure at lifted condensation level (LCL)", "Pa",
15373 ctl->met_nc_level, 0);
15374 NC_DEF_VAR("plfc", NC_FLOAT, 3, dimid2,
15375 "Pressure at level of free convection (LFC)", "Pa",
15376 ctl->met_nc_level, 0);
15377 NC_DEF_VAR("pel", NC_FLOAT, 3, dimid2,
15378 "Pressure at equilibrium level (EL)", "Pa", ctl->met_nc_level,
15379 0);
15380 NC_DEF_VAR("cape", NC_FLOAT, 3, dimid2,
15381 "Convective available potential energy", "J kg**-1",
15382 ctl->met_nc_level, 0);
15383 NC_DEF_VAR("cin", NC_FLOAT, 3, dimid2, "Convective inhibition",
15384 "J kg**-1", ctl->met_nc_level, 0);
15385 NC_DEF_VAR("o3c", NC_FLOAT, 3, dimid2, "Total column ozone", "DU",
15386 ctl->met_nc_level, 0);
15387
15388 /* Define level data... */
15389 int dimid3[4] = { tid, levid, latid, lonid };
15390 NC_DEF_VAR("t", NC_FLOAT, 4, dimid3, "Temperature", "K",
15391 ctl->met_nc_level, ctl->met_nc_quant);
15392 NC_DEF_VAR("u", NC_FLOAT, 4, dimid3, "U velocity", "m s**-1",
15393 ctl->met_nc_level, ctl->met_nc_quant);
15394 NC_DEF_VAR("v", NC_FLOAT, 4, dimid3, "V velocity", "m s**-1",
15395 ctl->met_nc_level, ctl->met_nc_quant);
15396 NC_DEF_VAR("w", NC_FLOAT, 4, dimid3, "Vertical velocity", "Pa s**-1",
15397 ctl->met_nc_level, ctl->met_nc_quant);
15398 NC_DEF_VAR("q", NC_FLOAT, 4, dimid3, "Specific humidity", "kg kg**-1",
15399 ctl->met_nc_level, ctl->met_nc_quant);
15400 NC_DEF_VAR("o3", NC_FLOAT, 4, dimid3, "Ozone mass mixing ratio",
15401 "kg kg**-1", ctl->met_nc_level, ctl->met_nc_quant);
15402 NC_DEF_VAR("clwc", NC_FLOAT, 4, dimid3, "Cloud liquid water content",
15403 "kg kg**-1", ctl->met_nc_level, ctl->met_nc_quant);
15404 NC_DEF_VAR("crwc", NC_FLOAT, 4, dimid3, "Cloud rain water content",
15405 "kg kg**-1", ctl->met_nc_level, ctl->met_nc_quant);
15406 NC_DEF_VAR("ciwc", NC_FLOAT, 4, dimid3, "Cloud ice water content",
15407 "kg kg**-1", ctl->met_nc_level, ctl->met_nc_quant);
15408 NC_DEF_VAR("cswc", NC_FLOAT, 4, dimid3, "Cloud snow water content",
15409 "kg kg**-1", ctl->met_nc_level, ctl->met_nc_quant);
15410 NC_DEF_VAR("cc", NC_FLOAT, 4, dimid3, "Cloud cover", "-",
15411 ctl->met_nc_level, ctl->met_nc_quant);
15412
15413 /* End definitions... */
15414 NC(nc_enddef(ncid));
15415
15416 /* Write grid data... */
15417 NC_PUT_DOUBLE("time", &met->time, 0);
15418
15419 if (met->coord_type == 0) {
15420 NC_PUT_DOUBLE("lon", met->lon, 0);
15421 NC_PUT_DOUBLE("lat", met->lat, 0);
15422 } else {
15423 NC_PUT_DOUBLE("x", met->lon, 0);
15424 NC_PUT_DOUBLE("y", met->lat, 0);
15425 }
15426
15427 double phelp[EP];
15428 for (int ip = 0; ip < met->np; ip++)
15429 phelp[ip] = 100. * met->p[ip];
15430 NC_PUT_DOUBLE("lev", phelp, 0);
15431
15432 /* Write surface data... */
15433 write_met_nc_2d(ncid, "sp", met, met->ps, 100.0f);
15434 write_met_nc_2d(ncid, "z", met, met->zs, (float) (1000. * G0));
15435 write_met_nc_2d(ncid, "t2m", met, met->ts, 1.0f);
15436 write_met_nc_2d(ncid, "u10m", met, met->us, 1.0f);
15437 write_met_nc_2d(ncid, "v10m", met, met->vs, 1.0f);
15438 write_met_nc_2d(ncid, "iews", met, met->ess, 1.0f);
15439 write_met_nc_2d(ncid, "inss", met, met->nss, 1.0f);
15440 write_met_nc_2d(ncid, "ishf", met, met->shf, 1.0f);
15441 write_met_nc_2d(ncid, "lsm", met, met->lsm, 1.0f);
15442 write_met_nc_2d(ncid, "sstk", met, met->sst, 1.0f);
15443 write_met_nc_2d(ncid, "blp", met, met->pbl, 100.0f);
15444 write_met_nc_2d(ncid, "pt", met, met->pt, 100.0f);
15445 write_met_nc_2d(ncid, "tt", met, met->tt, 1.0f);
15446 write_met_nc_2d(ncid, "zt", met, met->zt, 1000.0f);
15447 write_met_nc_2d(ncid, "h2ot", met, met->h2ot, 1.0f);
15448 write_met_nc_2d(ncid, "pct", met, met->pct, 100.0f);
15449 write_met_nc_2d(ncid, "pcb", met, met->pcb, 100.0f);
15450 write_met_nc_2d(ncid, "cl", met, met->cl, 1.0f);
15451 write_met_nc_2d(ncid, "plcl", met, met->plcl, 100.0f);
15452 write_met_nc_2d(ncid, "plfc", met, met->plfc, 100.0f);
15453 write_met_nc_2d(ncid, "pel", met, met->pel, 100.0f);
15454 write_met_nc_2d(ncid, "cape", met, met->cape, 1.0f);
15455 write_met_nc_2d(ncid, "cin", met, met->cin, 1.0f);
15456 write_met_nc_2d(ncid, "o3c", met, met->o3c, 1.0f);
15457
15458 /* Write level data... */
15459 write_met_nc_3d(ncid, "t", met, met->t, 1.0f);
15460 write_met_nc_3d(ncid, "u", met, met->u, 1.0f);
15461 write_met_nc_3d(ncid, "v", met, met->v, 1.0f);
15462 write_met_nc_3d(ncid, "w", met, met->w, 100.0f);
15463 write_met_nc_3d(ncid, "q", met, met->h2o, (float) (MH2O / MA));
15464 write_met_nc_3d(ncid, "o3", met, met->o3, (float) (MO3 / MA));
15465 write_met_nc_3d(ncid, "clwc", met, met->lwc, 1.0f);
15466 write_met_nc_3d(ncid, "crwc", met, met->rwc, 1.0f);
15467 write_met_nc_3d(ncid, "ciwc", met, met->iwc, 1.0f);
15468 write_met_nc_3d(ncid, "cswc", met, met->swc, 1.0f);
15469 write_met_nc_3d(ncid, "cc", met, met->cc, 1.0f);
15470
15471 /* Close file... */
15472 NC(nc_close(ncid));
15473}
15474
15475/*****************************************************************************/
15476
15478 const int ncid,
15479 const char *varname,
15480 met_t *met,
15481 float var[EX][EY],
15482 const float scl) {
15483
15484 int varid;
15485 size_t start[4], count[4];
15486
15487 /* Allocate... */
15488 float *help;
15489 ALLOC(help, float,
15490 EX * EY);
15491
15492 /* Copy data... */
15493 for (int ix = 0; ix < met->nx; ix++)
15494 for (int iy = 0; iy < met->ny; iy++)
15495 help[ARRAY_2D(iy, ix, met->nx)] = scl * var[ix][iy];
15496
15497 /* Write data... */
15498 LOG(2, "Write 2-D variable: %s (netCDF)", varname);
15499 NC_PUT_FLOAT(varname, help, 0);
15500
15501 /* Free... */
15502 free(help);
15503}
15504
15505/*****************************************************************************/
15506
15508 const int ncid,
15509 const char *varname,
15510 met_t *met,
15511 float var[EX][EY][EP],
15512 const float scl) {
15513
15514 int varid;
15515 size_t start[4], count[4];
15516
15517 /* Allocate... */
15518 float *help;
15519 ALLOC(help, float,
15520 EX * EY * EP);
15521
15522 /* Copy data... */
15523 for (int ix = 0; ix < met->nx; ix++)
15524 for (int iy = 0; iy < met->ny; iy++)
15525 for (int ip = 0; ip < met->np; ip++)
15526 help[ARRAY_3D(ip, iy, met->ny, ix, met->nx)] = scl * var[ix][iy][ip];
15527
15528 /* Write data... */
15529 LOG(2, "Write 3-D variable: %s (netCDF)", varname);
15530 NC_PUT_FLOAT(varname, help, 0);
15531
15532 /* Free... */
15533 free(help);
15534}
15535
15536/*****************************************************************************/
15537
15539 const char *filename,
15540 const ctl_t *ctl,
15541 met_t *met0,
15542 met_t *met1,
15543 const atm_t *atm,
15544 const double t) {
15545
15546 if (ctl->met_coord_type != 0)
15547 ERRMSG("Only lat/lon grid supported");
15548
15549 static FILE *out;
15550
15551 static double *mass, *obsmean, *rt, *rz, *rlon, *rlat, *robs, *area,
15552 dz, dlon, dlat, *lon, *lat, *z, *press, temp, vmr, h2o, o3;
15553
15554 static int nobs, *obscount, ip, okay;
15555
15556 /* Set timer... */
15557 SELECT_TIMER("WRITE_PROF", "OUTPUT");
15558
15559 /* Init... */
15560 if (t == ctl->t_start) {
15561
15562 /* Check quantity index for mass... */
15563 if (ctl->qnt_m < 0)
15564 ERRMSG("Need quantity mass!");
15565
15566 /* Check molar mass... */
15567 if (ctl->molmass <= 0)
15568 ERRMSG("Specify molar mass!");
15569
15570 /* Allocate... */
15571 ALLOC(lon, double,
15572 ctl->prof_nx);
15573 ALLOC(lat, double,
15574 ctl->prof_ny);
15575 ALLOC(area, double,
15576 ctl->prof_ny);
15577 ALLOC(z, double,
15578 ctl->prof_nz);
15579 ALLOC(press, double,
15580 ctl->prof_nz);
15581 ALLOC(rt, double,
15582 NOBS);
15583 ALLOC(rz, double,
15584 NOBS);
15585 ALLOC(rlon, double,
15586 NOBS);
15587 ALLOC(rlat, double,
15588 NOBS);
15589 ALLOC(robs, double,
15590 NOBS);
15591
15592 /* Read observation data... */
15593 read_obs(ctl->prof_obsfile, ctl, rt, rz, rlon, rlat, robs, &nobs);
15594
15595 /* Create new output file... */
15596 LOG(1, "Write profile data: %s", filename);
15597 if (!(out = fopen(filename, "w")))
15598 ERRMSG("Cannot create file!");
15599
15600 /* Write header... */
15601 fprintf(out,
15602 "# $1 = time [s]\n"
15603 "# $2 = altitude [km]\n"
15604 "# $3 = longitude [deg]\n"
15605 "# $4 = latitude [deg]\n"
15606 "# $5 = pressure [hPa]\n"
15607 "# $6 = temperature [K]\n"
15608 "# $7 = volume mixing ratio [ppv]\n"
15609 "# $8 = H2O volume mixing ratio [ppv]\n"
15610 "# $9 = O3 volume mixing ratio [ppv]\n"
15611 "# $10 = observed BT index [K]\n"
15612 "# $11 = number of observations\n");
15613
15614 /* Set grid box size... */
15615 dz = (ctl->prof_z1 - ctl->prof_z0) / ctl->prof_nz;
15616 dlon = (ctl->prof_lon1 - ctl->prof_lon0) / ctl->prof_nx;
15617 dlat = (ctl->prof_lat1 - ctl->prof_lat0) / ctl->prof_ny;
15618
15619 /* Set vertical coordinates... */
15620 for (int iz = 0; iz < ctl->prof_nz; iz++) {
15621 z[iz] = ctl->prof_z0 + dz * (iz + 0.5);
15622 press[iz] = P(z[iz]);
15623 }
15624
15625 /* Set horizontal coordinates... */
15626 for (int ix = 0; ix < ctl->prof_nx; ix++)
15627 lon[ix] = ctl->prof_lon0 + dlon * (ix + 0.5);
15628 for (int iy = 0; iy < ctl->prof_ny; iy++) {
15629 lat[iy] = ctl->prof_lat0 + dlat * (iy + 0.5);
15630 area[iy] = dlat * dlon * SQR(RE * M_PI / 180.) * cos(DEG2RAD(lat[iy]));
15631 }
15632 }
15633
15634 /* Set time interval... */
15635 const double t0 = t - 0.5 * ctl->dt_mod;
15636 const double t1 = t + 0.5 * ctl->dt_mod;
15637
15638 /* Allocate... */
15639 ALLOC(mass, double,
15640 ctl->prof_nx * ctl->prof_ny * ctl->prof_nz);
15641 ALLOC(obsmean, double,
15642 ctl->prof_nx * ctl->prof_ny);
15643 ALLOC(obscount, int,
15644 ctl->prof_nx * ctl->prof_ny);
15645
15646 /* Loop over observations... */
15647 for (int i = 0; i < nobs; i++) {
15648
15649 /* Check time... */
15650 if (rt[i] < t0)
15651 continue;
15652 else if (rt[i] >= t1)
15653 break;
15654
15655 /* Check observation data... */
15656 if (!isfinite(robs[i]))
15657 continue;
15658
15659 /* Check grid boundaries and calculate indices... */
15660 if (rlon[i] < ctl->prof_lon0 || rlon[i] >= ctl->prof_lon1
15661 || rlat[i] < ctl->prof_lat0 || rlat[i] >= ctl->prof_lat1)
15662 continue;
15663 const int ix = (int) ((rlon[i] - ctl->prof_lon0) / dlon);
15664 const int iy = (int) ((rlat[i] - ctl->prof_lat0) / dlat);
15665 if (ix >= ctl->prof_nx || iy >= ctl->prof_ny)
15666 continue;
15667
15668 /* Get mean observation index... */
15669 const int idx = ARRAY_2D(ix, iy, ctl->prof_ny);
15670 obsmean[idx] += robs[i];
15671 obscount[idx]++;
15672 }
15673
15674 /* Analyze model data... */
15675 for (ip = 0; ip < atm->np; ip++) {
15676
15677 /* Check time... */
15678 if (atm->time[ip] < t0 || atm->time[ip] > t1)
15679 continue;
15680
15681 /* Check grid boundaries and get indices... */
15682 const double zpart = Z(atm->p[ip]);
15683 if (atm->lon[ip] < ctl->prof_lon0
15684 || atm->lon[ip] >= ctl->prof_lon1
15685 || atm->lat[ip] < ctl->prof_lat0
15686 || atm->lat[ip] >= ctl->prof_lat1
15687 || zpart < ctl->prof_z0 || zpart >= ctl->prof_z1)
15688 continue;
15689 const int ix = (int) ((atm->lon[ip] - ctl->prof_lon0) / dlon);
15690 const int iy = (int) ((atm->lat[ip] - ctl->prof_lat0) / dlat);
15691 const int iz = (int) ((zpart - ctl->prof_z0) / dz);
15692 if (ix >= ctl->prof_nx || iy >= ctl->prof_ny || iz >= ctl->prof_nz)
15693 continue;
15694
15695 /* Get total mass in grid cell... */
15696 const int idx = ARRAY_3D(ix, iy, ctl->prof_ny, iz, ctl->prof_nz);
15697 mass[idx] += atm->q[ctl->qnt_m][ip];
15698 }
15699
15700 /* Extract profiles... */
15701 for (int ix = 0; ix < ctl->prof_nx; ix++)
15702 for (int iy = 0; iy < ctl->prof_ny; iy++) {
15703 int idx2 = ARRAY_2D(ix, iy, ctl->prof_ny);
15704 if (obscount[idx2] > 0) {
15705
15706 /* Check profile... */
15707 okay = 0;
15708 for (int iz = 0; iz < ctl->prof_nz; iz++) {
15709 int idx3 = ARRAY_3D(ix, iy, ctl->prof_ny, iz, ctl->prof_nz);
15710 if (mass[idx3] > 0) {
15711 okay = 1;
15712 break;
15713 }
15714 }
15715 if (!okay)
15716 continue;
15717
15718 /* Write output... */
15719 fprintf(out, "\n");
15720
15721 /* Loop over altitudes... */
15722 for (int iz = 0; iz < ctl->prof_nz; iz++) {
15723
15724 /* Get temperature, water vapor, and ozone... */
15726 intpol_met_time_3d(met0, met0->t, met1, met1->t, t, press[iz],
15727 lon[ix], lat[iy], &temp, ci, cw, 1);
15728 intpol_met_time_3d(met0, met0->h2o, met1, met1->h2o, t, press[iz],
15729 lon[ix], lat[iy], &h2o, ci, cw, 0);
15730 intpol_met_time_3d(met0, met0->o3, met1, met1->o3, t, press[iz],
15731 lon[ix], lat[iy], &o3, ci, cw, 0);
15732
15733 /* Calculate volume mixing ratio... */
15734 const int idx3 = ARRAY_3D(ix, iy, ctl->prof_ny, iz, ctl->prof_nz);
15735 vmr = MA / ctl->molmass * mass[idx3]
15736 / (RHO(press[iz], temp) * area[iy] * dz * 1e9);
15737
15738 /* Write output... */
15739 fprintf(out, "%.2f %g %g %g %g %g %g %g %g %g %d\n",
15740 t, z[iz], lon[ix], lat[iy], press[iz], temp, vmr, h2o, o3,
15741 obsmean[idx2] / obscount[idx2], obscount[idx2]);
15742 }
15743 }
15744 }
15745
15746 /* Free... */
15747 free(mass);
15748 free(obsmean);
15749 free(obscount);
15750
15751 /* Finalize... */
15752 if (t == ctl->t_stop) {
15753
15754 /* Close output file... */
15755 fclose(out);
15756
15757 /* Free... */
15758 free(lon);
15759 free(lat);
15760 free(area);
15761 free(z);
15762 free(press);
15763 free(rt);
15764 free(rz);
15765 free(rlon);
15766 free(rlat);
15767 free(robs);
15768 }
15769}
15770
15771/*****************************************************************************/
15772
15774 const char *filename,
15775 const ctl_t *ctl,
15776 met_t *met0,
15777 met_t *met1,
15778 const atm_t *atm,
15779 const double t) {
15780
15781 if (ctl->met_coord_type != 0)
15782 ERRMSG("Only lat/lon grid supported");
15783
15784 static FILE *out;
15785
15786 static double area, dlat, rmax2, *rt, *rz, *rlon, *rlat, *robs, kz[EP],
15787 kw[EP];
15788
15789 static int nobs, nk;
15790
15791 /* Set timer... */
15792 SELECT_TIMER("WRITE_SAMPLE", "OUTPUT");
15793
15794 /* Init... */
15795 if (t == ctl->t_start) {
15796
15797 /* Allocate... */
15798 ALLOC(rt, double,
15799 NOBS);
15800 ALLOC(rz, double,
15801 NOBS);
15802 ALLOC(rlon, double,
15803 NOBS);
15804 ALLOC(rlat, double,
15805 NOBS);
15806 ALLOC(robs, double,
15807 NOBS);
15808
15809 /* Read observation data... */
15810 read_obs(ctl->sample_obsfile, ctl, rt, rz, rlon, rlat, robs, &nobs);
15811
15812 /* Read kernel data... */
15813 if (ctl->sample_kernel[0] != '-')
15814 read_kernel(ctl->sample_kernel, kz, kw, &nk);
15815
15816 /* Create output file... */
15817 LOG(1, "Write sample data: %s", filename);
15818 if (!(out = fopen(filename, "w")))
15819 ERRMSG("Cannot create file!");
15820
15821 /* Write header... */
15822 fprintf(out,
15823 "# $1 = time [s]\n"
15824 "# $2 = altitude [km]\n"
15825 "# $3 = longitude [deg]\n"
15826 "# $4 = latitude [deg]\n"
15827 "# $5 = surface area [km^2]\n"
15828 "# $6 = layer depth [km]\n"
15829 "# $7 = number of particles [1]\n"
15830 "# $8 = column density [kg/m^2]\n"
15831 "# $9 = volume mixing ratio [ppv]\n"
15832 "# $10 = observed BT index [K]\n\n");
15833
15834 /* Set latitude range, squared radius, and area... */
15835 dlat = DY2DEG(ctl->sample_dx);
15836 rmax2 = SQR(ctl->sample_dx);
15837 area = M_PI * rmax2;
15838 }
15839
15840 /* Set time interval for output... */
15841 const double t0 = t - 0.5 * ctl->dt_mod;
15842 const double t1 = t + 0.5 * ctl->dt_mod;
15843
15844 /* Loop over observations... */
15845 for (int i = 0; i < nobs; i++) {
15846
15847 /* Check time... */
15848 if (rt[i] < t0)
15849 continue;
15850 else if (rt[i] >= t1)
15851 break;
15852
15853 /* Calculate Cartesian coordinates... */
15854 double x0[3];
15855 geo2cart(0, rlon[i], rlat[i], x0);
15856
15857 /* Set pressure range... */
15858 const double rp = P(rz[i]);
15859 const double ptop = P(rz[i] + ctl->sample_dz);
15860 const double pbot = P(rz[i] - ctl->sample_dz);
15861
15862 /* Init... */
15863 double mass = 0;
15864 int np = 0;
15865
15866 /* Loop over air parcels... */
15867 //#pragma omp parallel for default(shared) reduction(+:mass,np)
15868 for (int ip = 0; ip < atm->np; ip++) {
15869
15870 /* Check time... */
15871 if (atm->time[ip] < t0 || atm->time[ip] > t1)
15872 continue;
15873
15874 /* Check latitude... */
15875 if (fabs(rlat[i] - atm->lat[ip]) > dlat)
15876 continue;
15877
15878 /* Check horizontal distance... */
15879 double x1[3];
15880 geo2cart(0, atm->lon[ip], atm->lat[ip], x1);
15881 if (DIST2(x0, x1) > rmax2)
15882 continue;
15883
15884 /* Check pressure... */
15885 if (ctl->sample_dz > 0)
15886 if (atm->p[ip] > pbot || atm->p[ip] < ptop)
15887 continue;
15888
15889 /* Add mass... */
15890 if (ctl->qnt_m >= 0)
15891 mass +=
15892 kernel_weight(kz, kw, nk, atm->p[ip]) * atm->q[ctl->qnt_m][ip];
15893 np++;
15894 }
15895
15896 /* Calculate column density... */
15897 const double cd = mass / (1e6 * area);
15898
15899 /* Calculate volume mixing ratio... */
15900 double vmr = 0;
15901 if (ctl->molmass > 0 && ctl->sample_dz > 0) {
15902 if (mass > 0) {
15903
15904 /* Get temperature... */
15905 double temp;
15907 intpol_met_time_3d(met0, met0->t, met1, met1->t, rt[i], rp,
15908 rlon[i], rlat[i], &temp, ci, cw, 1);
15909
15910 /* Calculate volume mixing ratio... */
15911 vmr = MA / ctl->molmass * cd / (RHO(rp, temp) * ctl->sample_dz * 1e3);
15912 }
15913 } else
15914 vmr = NAN;
15915
15916 /* Write output... */
15917 fprintf(out, "%.2f %g %g %g %g %g %d %g %g %g\n", rt[i], rz[i],
15918 rlon[i], rlat[i], area, ctl->sample_dz, np, cd, vmr, robs[i]);
15919 }
15920
15921 /* Finalize...... */
15922 if (t == ctl->t_stop) {
15923
15924 /* Close output file... */
15925 fclose(out);
15926
15927 /* Free... */
15928 free(rt);
15929 free(rz);
15930 free(rlon);
15931 free(rlat);
15932 free(robs);
15933 }
15934}
15935
15936/*****************************************************************************/
15937
15939 const char *filename,
15940 const ctl_t *ctl,
15941 atm_t *atm,
15942 const double t) {
15943
15944 if (ctl->met_coord_type != 0)
15945 ERRMSG("Only lat/lon grid supported");
15946
15947 static FILE *out;
15948
15949 static double rmax2, x0[3], x1[3];
15950
15951 /* Set timer... */
15952 SELECT_TIMER("WRITE_STATION", "OUTPUT");
15953
15954 /* Init... */
15955 if (t == ctl->t_start) {
15956
15957 /* Write info... */
15958 LOG(1, "Write station data: %s", filename);
15959
15960 /* Create new file... */
15961 if (!(out = fopen(filename, "w")))
15962 ERRMSG("Cannot create file!");
15963
15964 /* Write header... */
15965 fprintf(out,
15966 "# $1 = time [s]\n"
15967 "# $2 = altitude [km]\n"
15968 "# $3 = longitude [deg]\n" "# $4 = latitude [deg]\n");
15969 for (int iq = 0; iq < ctl->nq; iq++)
15970 fprintf(out, "# $%i = %s [%s]\n", (iq + 5),
15971 ctl->qnt_name[iq], ctl->qnt_unit[iq]);
15972 fprintf(out, "\n");
15973
15974 /* Set geolocation and search radius... */
15975 geo2cart(0, ctl->stat_lon, ctl->stat_lat, x0);
15976 rmax2 = SQR(ctl->stat_r);
15977 }
15978
15979 /* Set time interval for output... */
15980 const double t0 = t - 0.5 * ctl->dt_mod;
15981 const double t1 = t + 0.5 * ctl->dt_mod;
15982
15983 /* Count number of newly set station flags... */
15984 int nflag = 0;
15985
15986 /* Loop over air parcels... */
15987 for (int ip = 0; ip < atm->np; ip++) {
15988
15989 /* Check time... */
15990 if (atm->time[ip] < t0 || atm->time[ip] > t1)
15991 continue;
15992
15993 /* Check time range for station output... */
15994 if (atm->time[ip] < ctl->stat_t0 || atm->time[ip] > ctl->stat_t1)
15995 continue;
15996
15997 /* Check station flag... */
15998 if (ctl->qnt_stat >= 0)
15999 if ((int) atm->q[ctl->qnt_stat][ip])
16000 continue;
16001
16002 /* Get Cartesian coordinates... */
16003 geo2cart(0, atm->lon[ip], atm->lat[ip], x1);
16004
16005 /* Check horizontal distance... */
16006 if (DIST2(x0, x1) > rmax2)
16007 continue;
16008
16009 /* Set station flag... */
16010 if (ctl->qnt_stat >= 0) {
16011 atm->q[ctl->qnt_stat][ip] = 1;
16012 nflag += 1;
16013 }
16014
16015 /* Write data... */
16016 fprintf(out, "%.2f %g %g %g",
16017 atm->time[ip], Z(atm->p[ip]), atm->lon[ip], atm->lat[ip]);
16018 for (int iq = 0; iq < ctl->nq; iq++) {
16019 fprintf(out, " ");
16020 fprintf(out, ctl->qnt_format[iq], atm->q[iq][ip]);
16021 }
16022 fprintf(out, "\n");
16023 }
16024
16025 /* During the run, the parcel state lives on the GPU, see mptrac_init().
16026 Therefore, push the station flags written above back to the device;
16027 otherwise, they are invisible to the next timesteps and get discarded
16028 by the next mptrac_update_host() within mptrac_write_output(). */
16029#ifdef _OPENACC
16030 if (nflag > 0) {
16031#pragma acc update device(atm->q[ctl->qnt_stat][0:atm->np])
16032 }
16033#else
16034 (void) nflag;
16035#endif
16036
16037 /* Close file... */
16038 if (t == ctl->t_stop)
16039 fclose(out);
16040}
16041
16042/*****************************************************************************/
16043
16045 const char *filename,
16046 const ctl_t *ctl,
16047 const atm_t *atm,
16048 const double t) {
16049
16050 if (ctl->met_coord_type != 0)
16051 ERRMSG("Only lat/lon grid supported");
16052
16053 FILE *out;
16054
16055 /* Set timer... */
16056 SELECT_TIMER("WRITE_VTK", "OUTPUT");
16057
16058 /* Write info... */
16059 LOG(1, "Write VTK data: %s", filename);
16060
16061 /* Set time interval for output... */
16062 const double t0 = t - 0.5 * ctl->dt_mod;
16063 const double t1 = t + 0.5 * ctl->dt_mod;
16064
16065 /* Create file... */
16066 if (!(out = fopen(filename, "w")))
16067 ERRMSG("Cannot create file!");
16068
16069 /* Count data points... */
16070 int np = 0;
16071 for (int ip = 0; ip < atm->np; ip += ctl->vtk_stride) {
16072 if (atm->time[ip] < t0 || atm->time[ip] > t1)
16073 continue;
16074 np++;
16075 }
16076
16077 /* Write header... */
16078 fprintf(out,
16079 "# vtk DataFile Version 3.0\n"
16080 "vtk output\n" "ASCII\n" "DATASET POLYDATA\n");
16081
16082 /* Write point coordinates... */
16083 fprintf(out, "POINTS %d float\n", np);
16084 if (ctl->vtk_sphere) {
16085 for (int ip = 0; ip < atm->np; ip += ctl->vtk_stride) {
16086 if (atm->time[ip] < t0 || atm->time[ip] > t1)
16087 continue;
16088 const double radius = (RE + Z(atm->p[ip]) * ctl->vtk_scale
16089 + ctl->vtk_offset) / RE;
16090 const double coslat = cos(DEG2RAD(atm->lat[ip]));
16091 const double x = radius * coslat * cos(DEG2RAD(atm->lon[ip]));
16092 const double y = radius * coslat * sin(DEG2RAD(atm->lon[ip]));
16093 const double z = radius * sin(DEG2RAD(atm->lat[ip]));
16094 fprintf(out, "%g %g %g\n", x, y, z);
16095 }
16096 } else
16097 for (int ip = 0; ip < atm->np; ip += ctl->vtk_stride) {
16098 if (atm->time[ip] < t0 || atm->time[ip] > t1)
16099 continue;
16100 fprintf(out, "%g %g %g\n", atm->lon[ip], atm->lat[ip],
16101 Z(atm->p[ip]) * ctl->vtk_scale + ctl->vtk_offset);
16102 }
16103
16104 /* Write point data... */
16105 fprintf(out, "POINT_DATA %d\n", np);
16106 for (int iq = 0; iq < ctl->nq; iq++) {
16107 fprintf(out, "SCALARS %s float 1\n" "LOOKUP_TABLE default\n",
16108 ctl->qnt_name[iq]);
16109 for (int ip = 0; ip < atm->np; ip += ctl->vtk_stride) {
16110 if (atm->time[ip] < t0 || atm->time[ip] > t1)
16111 continue;
16112 fprintf(out, "%g\n", atm->q[iq][ip]);
16113 }
16114 }
16115
16116 /* Close file... */
16117 fclose(out);
16118}
void read_met_geopot(const ctl_t *ctl, met_t *met)
Calculates geopotential heights from meteorological data.
Definition: mptrac.c:9968
void dd_init(const ctl_t *ctl, dd_t *dd, atm_t *atm)
Initialize the domain decomposition infrastructure.
Definition: mptrac.c:2334
void write_depo(const char *filename, const ctl_t *ctl, const depo_t *depo, const double t)
Convert cumulative ground inventories to Bq m^-2 and write them.
Definition: mptrac.c:14430
void mptrac_write_atm(const char *filename, const ctl_t *ctl, const atm_t *atm, const double t)
Writes air parcel data to a file in various formats.
Definition: mptrac.c:8559
void day2doy(const int year, const int mon, const int day, int *doy)
Get day of year from date.
Definition: mptrac.c:1900
void read_met_extrapolate(met_t *met)
Extrapolates meteorological data.
Definition: mptrac.c:9928
void write_atm_clams_traj(const char *dirname, const ctl_t *ctl, const atm_t *atm, const double t)
Writes CLaMS trajectory data to a NetCDF file.
Definition: mptrac.c:13739
void write_met_nc_2d(const int ncid, const char *varname, met_t *met, float var[EX][EY], const float scl)
Writes a 2D meteorological variable to a NetCDF file.
Definition: mptrac.c:15477
void dd_read_met_nc_grid(dd_t *dd, const ctl_t *ctl, met_t *met, const int ncid)
Read meteorological grid information and construct the domain-decomposed grid with halo regions.
Definition: mptrac.c:12148
void read_met_sample(const ctl_t *ctl, met_t *met)
Downsamples meteorological data based on specified parameters.
Definition: mptrac.c:12747
int read_met_nc_2d(const int ncid, const char *const varnames[], const size_t nvarnames, const ctl_t *ctl, const met_t *met, dd_t *dd, float dest[EX][EY], const float scl, const int init)
Reads a 2-dimensional meteorological variable from a NetCDF file.
Definition: mptrac.c:10847
void read_obs(const char *filename, const ctl_t *ctl, double *rt, double *rz, double *rlon, double *rlat, double *robs, int *nobs)
Reads observation data from a file and stores it in arrays.
Definition: mptrac.c:13094
void module_advect(const ctl_t *ctl, const cache_t *cache, met_t *met0, met_t *met1, atm_t *atm)
Advances particle positions using different advection schemes.
Definition: mptrac.c:3900
void module_meteo(const ctl_t *ctl, const cache_t *cache, const clim_t *clim, met_t *met0, met_t *met1, atm_t *atm)
Update atmospheric properties using meteorological data.
Definition: mptrac.c:5379
void read_clim_photo(const char *filename, clim_photo_t *photo)
Reads photolysis rates from a NetCDF file and populates the given photolysis structure.
Definition: mptrac.c:9032
void read_met_cloud(met_t *met)
Calculates cloud-related variables for each grid point.
Definition: mptrac.c:9764
void module_decay(const ctl_t *ctl, const cache_t *cache, const clim_t *clim, atm_t *atm)
Simulate exponential decay processes for atmospheric particles.
Definition: mptrac.c:4544
double sedi(const double p, const double T, const double rp, const double rhop)
Calculates the sedimentation velocity of a particle in air.
Definition: mptrac.c:13267
double cos_sza(const double sec, const double lon, const double lat)
Calculates the cosine of the solar zenith angle.
Definition: mptrac.c:1859
void intpol_met_space_2d(const met_t *met, float array[EX][EY], const double lon, const double lat, double *var, int *ci, double *cw, const int init)
Interpolates meteorological variables in 2D space.
Definition: mptrac.c:3350
float * global
Definition: mptrac.c:10663
size_t local_elements
Definition: mptrac.c:10664
void compress_log_levels_3d(FILE *out, const char *codec, const char *varname, const met_t *met, const float *org_all, const float *cmp_all, const size_t nxy, const size_t nz, const double ratio, const double bpv, const double t_comp, const double t_decomp, const size_t nbytes)
Write per-level compression diagnostics for a full 3-D field.
Definition: mptrac.c:767
int read_atm_nc(const char *filename, const ctl_t *ctl, atm_t *atm)
Reads air parcel data from a generic netCDF file and populates the given atmospheric structure.
Definition: mptrac.c:8999
void read_met_pbl(const ctl_t *ctl, met_t *met)
Computes the planetary boundary layer (PBL) pressure based on meteorological data.
Definition: mptrac.c:12338
void read_met_detrend(const ctl_t *ctl, met_t *met)
Detrends meteorological data.
Definition: mptrac.c:9821
void read_met_tropo(const ctl_t *ctl, const clim_t *clim, met_t *met)
Calculates the tropopause and related meteorological variables based on various methods and stores th...
Definition: mptrac.c:12920
void read_obs_asc(const char *filename, double *rt, double *rz, double *rlon, double *rlat, double *robs, int *nobs)
Reads observation data from an ASCII file.
Definition: mptrac.c:13138
void write_depo_asc(const char *filename, const ctl_t *ctl, const double *data, const double t, const double *lon, const double *lat, const double *area)
Write radioactive deposition densities as a gnuplot-compatible table.
Definition: mptrac.c:14501
void module_chem_init(const ctl_t *ctl, const cache_t *cache, const clim_t *clim, met_t *met0, met_t *met1, atm_t *atm)
Initializes the chemistry modules by setting atmospheric composition.
Definition: mptrac.c:4367
int locate_reg(const double *xx, const int n, const double x)
Locate the index of the interval containing a given value in a regular grid.
Definition: mptrac.c:3861
void read_met_nc_levels(const int ncid, const ctl_t *ctl, met_t *met, dd_t *dd)
Reads and processes meteorological level data from NetCDF files with domain decomposition.
Definition: mptrac.c:10440
void compress_pck(const ctl_t *ctl, const met_t *met, const char *varname, float *array, const int decompress, FILE *level_log, FILE *inout)
Compresses or decompresses a 3‑D float array using the PCK format.
Definition: mptrac.c:988
void read_met_monotonize(const ctl_t *ctl, met_t *met)
Makes zeta and pressure profiles monotone.
Definition: mptrac.c:12001
int read_clim_ts(const char *filename, clim_ts_t *ts)
Reads a climatological time series from a file and populates the given time series structure.
Definition: mptrac.c:9151
void intpol_check_cartesian(const double *lons, const int nlon, const double *lats, const int nlat, const double lon, const double lat, double *lon2, double *lat2)
Clamps UTM coordinates to the valid bounds.
Definition: mptrac.c:3084
void read_met_periodic(met_t *met)
Applies periodic boundary conditions to meteorological data along longitudinal axis.
Definition: mptrac.c:12475
int compress_metvar_index(const char *varname)
Maps a meteorological variable name to its internal MPTRAC variable index.
Definition: mptrac.c:971
void module_timesteps_init(ctl_t *ctl, const atm_t *atm)
Initialize start time and time interval for time-stepping.
Definition: mptrac.c:6363
void write_ens(const char *filename, const ctl_t *ctl, const atm_t *atm, const double t)
Writes ensemble data to a file.
Definition: mptrac.c:14330
void module_mixing(const ctl_t *ctl, const clim_t *clim, atm_t *atm, const double t)
Update atmospheric properties through interparcel mixing.
Definition: mptrac.c:5505
double clim_zm(const clim_zm_t *zm, const double t, const double lat, const double p)
Interpolates monthly mean zonal mean climatological variables.
Definition: mptrac.c:416
int size
Definition: mptrac.c:10665
void read_clim_photo_help(const int ncid, const char *varname, const clim_photo_t *photo, double var[CP][CSZA][CO3])
Reads a 3D climatological photochemistry variable from a NetCDF file.
Definition: mptrac.c:9123
void read_met_ml2pl(const ctl_t *ctl, const met_t *met, float var[EX][EY][EP], const char *varname)
Interpolates meteorological data to specified pressure levels.
Definition: mptrac.c:11959
double clim_tropo(const clim_t *clim, const double t, const double lat)
Calculates the tropopause pressure based on climatological data.
Definition: mptrac.c:215
void read_obs_nc(const char *filename, double *rt, double *rz, double *rlon, double *rlat, double *robs, int *nobs)
Reads observation data from a NetCDF file.
Definition: mptrac.c:13166
void read_met_bin_2d(FILE *in, const met_t *met, float var[EX][EY], const char *varname)
Reads a 2-dimensional meteorological variable from a binary file and stores it in the provided array.
Definition: mptrac.c:9505
int locate_irr(const double *xx, const int n, const double x)
Locate the index of the interval containing a given value in a sorted array.
Definition: mptrac.c:3797
void module_isosurf_init(const ctl_t *ctl, cache_t *cache, met_t *met0, met_t *met1, atm_t *atm)
Initialize the isosurface module based on atmospheric data.
Definition: mptrac.c:5203
void mptrac_free(ctl_t *ctl, cache_t *cache, clim_t *clim, met_t *met0, met_t *met1, atm_t *atm, depo_t *depo, dd_t *dd)
Frees memory resources allocated for MPTRAC.
Definition: mptrac.c:6701
void dd_sort(const ctl_t *ctl, const met_t *met0, cache_t *cache, atm_t *atm, dd_t *dd, int *npart)
Sort local atmospheric particles and determine export counts for domain decomposition.
Definition: mptrac.c:2653
void level_definitions(ctl_t *ctl)
Defines pressure levels for meteorological data.
Definition: mptrac.c:3644
void write_grid_asc(const char *filename, const ctl_t *ctl, const double *cd, double *mean[NQ], double *sigma[NQ], const double *vmr_impl, const double t, const double *z, const double *lon, const double *lat, const double *area, const double dz, const int *np)
Writes grid data to an ASCII file.
Definition: mptrac.c:14809
void mptrac_update_device(const ctl_t *ctl, const cache_t *cache, const clim_t *clim, met_t **met0, met_t **met1, const atm_t *atm)
Updates device memory for specified data structures.
Definition: mptrac.c:8447
void mptrac_write_output(const char *dirname, const ctl_t *ctl, met_t *met0, met_t *met1, atm_t *atm, depo_t *depo, const double t)
Writes various types of output data to files in a specified directory.
Definition: mptrac.c:8672
void time2jsec(const int year, const int mon, const int day, const int hour, const int min, const int sec, const double remain, double *jsec)
Converts time components to seconds since January 1, 2000, 12:00:00 UTC.
Definition: mptrac.c:13368
void intpol_met_time_3d(const met_t *met0, float array0[EX][EY][EP], const met_t *met1, float array1[EX][EY][EP], const double ts, const double p, const double lon, const double lat, double *var, int *ci, double *cw, const int init)
Interpolates meteorological data in 3D space and time.
Definition: mptrac.c:3414
void get_met_filename(const ctl_t *ctl, const double t, const int direct, const char *metbase, const double dt_met, char *filename)
Generates a formatted filename for meteorological data files based on the input parameters.
Definition: mptrac.c:2921
void fft_help(double *fcReal, double *fcImag, const int n)
Computes the Fast Fourier Transform (FFT) of a complex sequence.
Definition: mptrac.c:2864
void module_wet_depo(const ctl_t *ctl, const cache_t *cache, met_t *met0, met_t *met1, atm_t *atm)
Perform wet deposition calculations for air parcels.
Definition: mptrac.c:6472
float * packed
Definition: mptrac.c:10663
int compress_read_lossy_scale(FILE *in, const size_t nz, double **off, double **scl)
Read optional lossyscaling metadata for a 3-D field.
Definition: mptrac.c:876
double nat_temperature(const double p, const double h2o, const double hno3)
Calculates the nitric acid trihydrate (NAT) temperature.
Definition: mptrac.c:8792
void spline(const double *x, const double *y, const int n, const double *x2, double *y2, const int n2, const int method)
Performs spline interpolation or linear interpolation.
Definition: mptrac.c:13300
void dd_pack_compact(const ctl_t *ctl, cache_t *cache, atm_t *atm, dd_t *dd, particle_t *particles, const int nkeep, const int npart)
Pack outgoing particles and compact local arrays without sorting.
Definition: mptrac.c:2050
float * local
Definition: mptrac.c:10663
void module_chem_grid(const ctl_t *ctl, met_t *met0, met_t *met1, atm_t *atm, const double tt)
Computes gridded chemical tracer concentrations (volume mixing ratio) from individual air parcel mass...
Definition: mptrac.c:4187
void dd_assign_subdomains(const ctl_t *ctl, const dd_t *dd, atm_t *atm, const int init)
Assign or update particle subdomain ownership.
Definition: mptrac.c:1927
double clim_photo(const double rate[CP][CSZA][CO3], const clim_photo_t *photo, const double p, const double sza, const double o3c)
Calculates the photolysis rate for a given set of atmospheric conditions.
Definition: mptrac.c:158
void read_clim_zm(const char *filename, const char *varname, clim_zm_t *zm)
Reads zonally averaged climatological data from a netCDF file and populates the given structure.
Definition: mptrac.c:9205
void module_sedi(const ctl_t *ctl, const cache_t *cache, met_t *met0, met_t *met1, atm_t *atm)
Simulate sedimentation of particles in the atmosphere.
Definition: mptrac.c:6209
int rank
Definition: mptrac.c:10665
int read_met_nc(const char *filename, const ctl_t *ctl, met_t *met, dd_t *dd)
Reads meteorological data from a NetCDF file and processes it.
Definition: mptrac.c:12086
void timer(const char *name, const char *group, const int output)
Measures and reports elapsed time for named and grouped timers.
Definition: mptrac.c:13399
void write_atm_asc(const char *filename, const ctl_t *ctl, const atm_t *atm, const double t)
Writes air parcel data to an ASCII file or gnuplot.
Definition: mptrac.c:13535
void intpol_met_space_3d(const met_t *met, float array[EX][EY][EP], const double p, const double lon, const double lat, double *var, int *ci, double *cw, const int init)
Interpolates meteorological variables in 3D space.
Definition: mptrac.c:3287
void module_mixing_help(const ctl_t *ctl, atm_t *atm, const int *ixs, const int *iys, const int *izs, const double *mixparam, const int qnt_idx, const int use_ensemble)
Perform subgrid-scale interparcel mixing of a given quantity.
Definition: mptrac.c:5597
int dd_calc_subdomain_from_coords(const ctl_t *ctl, const dd_t *dd, double lon, double lat)
Determine MPI subdomain from particle coordinates.
Definition: mptrac.c:2164
void module_convection(const ctl_t *ctl, cache_t *cache, met_t *met0, met_t *met1, atm_t *atm)
Performs convective mixing of atmospheric particles.
Definition: mptrac.c:4411
void compress_scale_to_unit(float *array, const size_t nxy, const size_t nz, double *off, double *scl)
Scales each vertical level of a 3-D field independently to the interval [0,1].
Definition: mptrac.c:813
void read_kernel(const char *filename, double kz[EP], double kw[EP], int *nk)
Reads kernel function data from a file and populates the provided arrays.
Definition: mptrac.c:9304
void module_bound_cond(const ctl_t *ctl, const cache_t *cache, const clim_t *clim, met_t *met0, met_t *met1, atm_t *atm)
Apply boundary conditions to particles based on meteorological and climatological data.
Definition: mptrac.c:4091
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.
Definition: mptrac.c:13195
void dd_normalize_lon_lat(const dd_t *dd, double *lon, double *lat)
Normalize geographic coordinates to the global grid convention.
Definition: mptrac.c:2413
void module_advect_init(const ctl_t *ctl, const cache_t *cache, met_t *met0, met_t *met1, atm_t *atm)
Initializes the advection module by setting up pressure fields.
Definition: mptrac.c:4064
void module_position(const cache_t *cache, met_t *met0, met_t *met1, atm_t *atm, const int reflect)
Update the positions and pressure levels of atmospheric particles.
Definition: mptrac.c:5784
void module_radio_decay(const ctl_t *ctl, const cache_t *cache, atm_t *atm)
Apply radioactive decay to atmospheric tracer species.
Definition: mptrac.c:5843
void mptrac_get_met(ctl_t *ctl, clim_t *clim, const double t, met_t **met0, met_t **met1, dd_t *dd)
Retrieves meteorological data for the specified time.
Definition: mptrac.c:6779
void dd_particles2atm(const ctl_t *ctl, cache_t *cache, const particle_t *particles, const int npart, atm_t *atm)
Copy received particles from the communication buffer into the atmospheric state.
Definition: mptrac.c:2446
float stddev(const float *data, const int n)
Calculates the standard deviation of a set of data.
Definition: mptrac.c:13347
void intpol_tropo_3d(const double time0, float array0[EX][EY], const double time1, float array1[EX][EY], const double lons[EX], const double lats[EY], const int nlon, const int nlat, const double time, const double lon, const double lat, const int method, double *var, double *sigma)
Interpolates tropopause data in 3D (latitude, longitude, and time).
Definition: mptrac.c:3476
void read_met_bin_3d(FILE *in, const ctl_t *ctl, const met_t *met, float var[EX][EY][EP], const char *varname, const float bound_min, const float bound_max)
Reads 3D meteorological data from a binary file, potentially using different compression methods.
Definition: mptrac.c:9534
int locate_irr_float(const float *xx, const int n, const double x, const int ig)
Locate the index of the interval containing a given value in an irregularly spaced array.
Definition: mptrac.c:3827
void write_prof(const char *filename, const ctl_t *ctl, met_t *met0, met_t *met1, const atm_t *atm, const double t)
Writes profile data to a specified file.
Definition: mptrac.c:15538
void mptrac_read_clim(const ctl_t *ctl, clim_t *clim)
Reads various climatological data and populates the given climatology structure.
Definition: mptrac.c:7004
void module_diff_meso(const ctl_t *ctl, cache_t *cache, const met_t *met0, const met_t *met1, atm_t *atm)
Simulate mesoscale diffusion for atmospheric particles.
Definition: mptrac.c:4583
void write_met_nc(const char *filename, const ctl_t *ctl, met_t *met)
Writes meteorological data to a NetCDF file.
Definition: mptrac.c:15295
void module_rng_init(const int ntask)
Initialize random number generators for parallel tasks.
Definition: mptrac.c:6073
void mptrac_init(ctl_t *ctl, cache_t *cache, clim_t *clim, atm_t *atm, depo_t *depo, const int ntask)
Initializes the MPTRAC model and its associated components.
Definition: mptrac.c:6904
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.
Definition: mptrac.c:6929
void mptrac_update_host(const ctl_t *ctl, const cache_t *cache, const clim_t *clim, met_t **met0, met_t **met1, const atm_t *atm)
Updates host memory for specified data structures.
Definition: mptrac.c:8503
double clim_oh(const ctl_t *ctl, const clim_t *clim, const double t, const double lon, const double lat, const double p)
Calculates the hydroxyl radical (OH) concentration from climatology data, with an optional diurnal co...
Definition: mptrac.c:91
void write_met_bin_3d(FILE *out, const ctl_t *ctl, met_t *met, float var[EX][EY][EP], const char *varname, const int metvar, FILE *level_log)
Writes a 3-dimensional meteorological variable to a binary file.
Definition: mptrac.c:15202
void read_met_ozone(met_t *met)
Calculates the total column ozone from meteorological ozone data.
Definition: mptrac.c:12718
void dd_count_outgoing(const ctl_t *ctl, const atm_t *atm, dd_t *dd, int *nkeep, int *npart)
Classify particles before direct packing and compaction.
Definition: mptrac.c:2768
void mptrac_alloc(ctl_t **ctl, cache_t **cache, clim_t **clim, met_t **met0, met_t **met1, atm_t **atm, depo_t **depo, dd_t **dd)
Allocates and initializes memory resources for MPTRAC.
Definition: mptrac.c:6611
void sort_array(double *array, const int *perm, double *tmp, const int np)
Reorder a double array according to particle source indices.
Definition: mptrac.c:2495
void broadcast_large_data(void *data, size_t N)
State variables of cuRAND random number generator.
Definition: mptrac.c:47
void compress_write_lossy_scale(FILE *out, const int enabled, float *array, const size_t nxy, const size_t nz, double **off, double **scl)
Write optional lossyscaling metadata for a 3-D field.
Definition: mptrac.c:911
size_t packed_elements
Definition: mptrac.c:10664
void read_met_nc_surface(const int ncid, const ctl_t *ctl, met_t *met, dd_t *dd)
Reads and processes surface meteorological data from NetCDF files with domain decomposition.
Definition: mptrac.c:10283
void clim_tropo_init(clim_t *clim)
Initializes the tropopause data in the climatology structure.
Definition: mptrac.c:243
void module_rng(const ctl_t *ctl, double *rs, const size_t n, const int method)
Generate random numbers using various methods and distributions.
Definition: mptrac.c:6104
void write_station(const char *filename, const ctl_t *ctl, atm_t *atm, const double t)
Writes station data to a specified file.
Definition: mptrac.c:15938
void cart2geo(const double *x, double *z, double *lon, double *lat)
Converts Cartesian coordinates to geographic coordinates.
Definition: mptrac.c:76
double time_from_filename(const char *filename, const int offset, const int with_seconds)
Extracts and converts a timestamp from a filename to Julian seconds.
Definition: mptrac.c:13467
void dd_atm2particles(const ctl_t *ctl, cache_t *cache, atm_t *atm, particle_t *particles, const int npart)
Copy migratable atmospheric particles from the ATM state into a particle buffer.
Definition: mptrac.c:2000
void module_sort(const ctl_t *ctl, const met_t *met0, cache_t *cache, atm_t *atm)
Sort particles according to box index.
Definition: mptrac.c:6237
void dd_communicate_particles(const ctl_t *ctl, const dd_t *dd, particle_t **particles, int *npart, int *capacity)
Exchange particles between MPI ranks according to their destination rank.
Definition: mptrac.c:2197
void doy2day(const int year, const int doy, int *mon, int *day)
Converts a given day of the year (DOY) to a date (month and day).
Definition: mptrac.c:2834
void intpol_met_4d_zeta(const met_t *met0, float heights0[EX][EY][EP], float array0[EX][EY][EP], const met_t *met1, float heights1[EX][EY][EP], float array1[EX][EY][EP], const double ts, const double height, const double lon, const double lat, double *var, int *ci, double *cw, const int init)
Interpolates meteorological variables to a given position and time.
Definition: mptrac.c:3110
int read_met_nc_3d(const int ncid, const char *const varnames[], const size_t nvarnames, const ctl_t *ctl, const met_t *met, dd_t *dd, float dest[EX][EY][EP], const float scl)
Reads a 3-dimensional meteorological variable from a NetCDF file.
Definition: mptrac.c:11185
void intpol_met_time_2d(const met_t *met0, float array0[EX][EY], const met_t *met1, float array1[EX][EY], const double ts, const double lon, const double lat, double *var, int *ci, double *cw, const int init)
Interpolates meteorological data in 2D space and time.
Definition: mptrac.c:3443
void clim_oh_diurnal_correction(const ctl_t *ctl, clim_t *clim)
Applies a diurnal correction to the hydroxyl radical (OH) concentration in climatology data.
Definition: mptrac.c:126
void locate_vert(float profiles[EX][EY][EP], const int np, const int lon_ap_ind, const int lat_ap_ind, const double height_ap, int *ind)
Locate the four vertical indizes of a box for a given height value.
Definition: mptrac.c:3880
void write_met_bin_2d(FILE *out, met_t *met, float var[EX][EY], const char *varname)
Writes a 2-dimensional meteorological variable to a binary file.
Definition: mptrac.c:15173
void read_met_pv(met_t *met)
Calculates potential vorticity (PV) from meteorological data.
Definition: mptrac.c:12598
int read_atm_bin(const char *filename, const ctl_t *ctl, atm_t *atm)
Reads air parcel data from a binary file and populates the given atmospheric structure.
Definition: mptrac.c:8880
size_t global_elements
Definition: mptrac.c:10664
void write_budget(const char *filename, const ctl_t *ctl, const atm_t *atm, const double t)
Writes a time series of the atmospheric tracer mass budget.
Definition: mptrac.c:13949
double clim_ts(const clim_ts_t *ts, const double t)
Interpolates a time series of climatological variables.
Definition: mptrac.c:398
void jsec2time(const double jsec, int *year, int *mon, int *day, int *hour, int *min, int *sec, double *remain)
Converts Julian seconds to calendar date and time components.
Definition: mptrac.c:3567
int read_met_bin(const char *filename, const ctl_t *ctl, met_t *met)
Reads meteorological data from a binary file.
Definition: mptrac.c:9345
void write_atm_clams(const char *filename, const ctl_t *ctl, const atm_t *atm)
Writes air parcel data to a NetCDF file in the CLaMS format.
Definition: mptrac.c:13683
void sort_uvwp(cache_t *cache, const int *perm, double *tmp, const int np)
Reorder particle-specific wind perturbations.
Definition: mptrac.c:2533
void get_met_replace(char *orig, const char *search, const char *repl)
Replaces occurrences of a substring in a string with another substring.
Definition: mptrac.c:2990
void module_diff_turb(const ctl_t *ctl, cache_t *cache, const clim_t *clim, met_t *met0, met_t *met1, atm_t *atm)
Applies turbulent diffusion processes to atmospheric particles.
Definition: mptrac.c:4905
int read_atm_clams(const char *filename, const ctl_t *ctl, atm_t *atm)
Reads atmospheric data from a CLAMS NetCDF file.
Definition: mptrac.c:8936
void write_depo_nc(const char *filename, const ctl_t *ctl, const double *data, const double t, const double *lon, const double *lat, const double *area)
Write radioactive deposition densities as a CF-style netCDF file.
Definition: mptrac.c:14541
void module_timesteps(const ctl_t *ctl, cache_t *cache, const met_t *met0, atm_t *atm, const double t)
Calculate time steps for air parcels based on specified conditions.
Definition: mptrac.c:6314
int mptrac_read_met(const char *filename, const ctl_t *ctl, const clim_t *clim, met_t *met, dd_t *dd)
Reads meteorological data from a file, supporting multiple formats and MPI broadcasting.
Definition: mptrac.c:8156
void mptrac_run_timestep(ctl_t *ctl, cache_t *cache, clim_t *clim, met_t **met0, met_t **met1, atm_t *atm, depo_t *depo, double t, dd_t *dd)
Executes a single timestep of the MPTRAC model simulation.
Definition: mptrac.c:8291
void write_vtk(const char *filename, const ctl_t *ctl, const atm_t *atm, const double t)
Writes VTK (Visualization Toolkit) data to a specified file.
Definition: mptrac.c:16044
void module_tracer_chem(const ctl_t *ctl, const cache_t *cache, const clim_t *clim, met_t *met0, met_t *met1, atm_t *atm)
Simulate chemical reactions involving long-lived atmospheric tracers.
Definition: mptrac.c:6394
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.
Definition: mptrac.c:7064
unsigned long long * geometry
Definition: mptrac.c:10662
void read_met_polar_winds(met_t *met)
Applies a fix for polar winds in meteorological data.
Definition: mptrac.c:12536
void module_h2o2_chem(const ctl_t *ctl, const cache_t *cache, const clim_t *clim, met_t *met0, met_t *met1, atm_t *atm)
Perform chemical reactions involving H2O2 within cloud particles.
Definition: mptrac.c:5118
void write_grid_nc(const char *filename, const ctl_t *ctl, const double *cd, double *mean[NQ], double *sigma[NQ], const double *vmr_impl, const double t, const double *z, const double *lon, const double *lat, const double *area, const double dz, const int *np)
Writes grid data to a NetCDF file.
Definition: mptrac.c:14913
double pbl_weight(const ctl_t *ctl, const atm_t *atm, const int ip, const double pbl, const double ps)
Computes a weighting factor based on planetary boundary layer pressure.
Definition: mptrac.c:8816
void module_diff_pbl(const ctl_t *ctl, cache_t *cache, met_t *met0, met_t *met1, atm_t *atm)
Computes particle diffusion within the planetary boundary layer (PBL).
Definition: mptrac.c:4660
void write_met_nc_3d(const int ncid, const char *varname, met_t *met, float var[EX][EY][EP], const float scl)
Writes a 3D meteorological variable to a NetCDF file.
Definition: mptrac.c:15507
void module_isosurf(const ctl_t *ctl, const cache_t *cache, met_t *met0, met_t *met1, atm_t *atm)
Apply the isosurface module to adjust atmospheric properties.
Definition: mptrac.c:5273
void module_oh_chem(const ctl_t *ctl, const cache_t *cache, const clim_t *clim, met_t *met0, met_t *met1, atm_t *atm)
Perform hydroxyl chemistry calculations for atmospheric particles.
Definition: mptrac.c:5700
void geo2cart(const double z, const double lon, const double lat, double *x)
Converts geographic coordinates (longitude, latitude, altitude) to Cartesian coordinates.
Definition: mptrac.c:2903
void read_met_nc_grid(const char *filename, const int ncid, const ctl_t *ctl, met_t *met, dd_t *dd)
Reads meteorological grid data from NetCDF files with domain decomposition.
Definition: mptrac.c:10096
void get_tropo(const int met_tropo, ctl_t *ctl, const clim_t *clim, met_t *met, const double *lons, const int nx, const double *lats, const int ny, double *pt, double *zt, double *tt, double *qt, double *o3t, double *ps, double *zs)
Calculate tropopause data.
Definition: mptrac.c:3014
double kernel_weight(const double kz[EP], const double kw[EP], const int nk, const double p)
Calculates the kernel weight based on altitude and given kernel data.
Definition: mptrac.c:3600
void module_dd(double t, const ctl_t *ctl, cache_t *cache, dd_t *dd, atm_t *atm, met_t **met)
Perform domain decomposition and exchange particles between MPI ranks.
Definition: mptrac.c:4485
void compress_log_level(FILE *out, const char *codec, const char *varname, const size_t lev, const double plev, const double ratio, const double bpv, const double t_comp, const double t_decomp, const size_t n, const size_t nbytes, const float *org, const float *cmp)
Write one row of per-level compression diagnostics.
Definition: mptrac.c:668
#define CHUNK_SIZE
void compress_unscale_from_unit(float *array, const size_t nxy, const size_t nz, const double *off, const double *scl)
Restores a levelwise [0,1]-scaled 3-D field to physical units.
Definition: mptrac.c:856
int read_atm_asc(const char *filename, const ctl_t *ctl, atm_t *atm)
Reads air parcel data from an ASCII file and populates the given atmospheric structure.
Definition: mptrac.c:8838
void intpol_check_lon_lat(const double *lons, const int nlon, const double *lats, const int nlat, const double lon, const double lat, double *lon2, double *lat2)
Adjusts longitude and latitude to ensure they fall within valid bounds.
Definition: mptrac.c:3057
void write_sample(const char *filename, const ctl_t *ctl, met_t *met0, met_t *met1, const atm_t *atm, const double t)
Writes sample data to a specified file.
Definition: mptrac.c:15773
void write_grid(const char *filename, const ctl_t *ctl, met_t *met0, met_t *met1, const atm_t *atm, const double t)
Writes grid data to a file in ASCII or netCDF format.
Definition: mptrac.c:14607
void module_dry_depo(const ctl_t *ctl, const cache_t *cache, met_t *met0, met_t *met1, atm_t *atm)
Simulate dry deposition of atmospheric particles.
Definition: mptrac.c:5055
void write_met_bin(const char *filename, const ctl_t *ctl, met_t *met)
Writes meteorological data in binary format to a specified file.
Definition: mptrac.c:15043
void write_atm_bin(const char *filename, const ctl_t *ctl, const atm_t *atm)
Writes air parcel data to a binary file.
Definition: mptrac.c:13633
void read_met_cape(const ctl_t *ctl, const clim_t *clim, met_t *met)
Calculates Convective Available Potential Energy (CAPE) for each grid point.
Definition: mptrac.c:9644
void mptrac_write_met(const char *filename, const ctl_t *ctl, met_t *met)
Writes meteorological data to a file, supporting multiple formats and compression options.
Definition: mptrac.c:8623
double tropo_weight(const ctl_t *ctl, const clim_t *clim, const atm_t *atm, const int ip)
Computes a weighting factor based on tropopause pressure.
Definition: mptrac.c:13509
void dd_push(const ctl_t *ctl, atm_t *atm, cache_t *cache, int *npart)
Compact and stage non-local particles for domain decomposition transfer.
Definition: mptrac.c:2575
double lapse_rate(const double t, const double h2o)
Calculates the moist adiabatic lapse rate in Kelvin per kilometer.
Definition: mptrac.c:3626
void module_radio_depo(const ctl_t *ctl, const cache_t *cache, met_t *met0, met_t *met1, atm_t *atm, depo_t *depo)
Deposit supported radionuclides from air parcels onto the ground grid.
Definition: mptrac.c:5901
void write_csi(const char *filename, const ctl_t *ctl, const atm_t *atm, const double t)
Writes Critical Success Index (CSI) data to a file.
Definition: mptrac.c:14043
void write_atm_nc(const char *filename, const ctl_t *ctl, const atm_t *atm)
Writes air parcel data to a NetCDF file.
Definition: mptrac.c:13900
MPTRAC library declarations.
#define NN(x0, y0, x1, y1, x)
Perform nearest-neighbor interpolation.
Definition: mptrac.h:1739
#define LEN
Maximum length of ASCII data lines.
Definition: mptrac.h:558
#define RE
Mean radius of Earth [km].
Definition: mptrac.h:314
#define TVIRT(t, h2o)
Compute virtual temperature.
Definition: mptrac.h:2211
void read_met_grib_surface(codes_handle **handles, const int num_messages, const ctl_t *ctl, met_t *met)
Reads surface meteorological data from a grib file and stores it in the meteorological data structure...
#define ARRAY_3D(ix, iy, ny, iz, nz)
Compute the linear index of a 3D array element.
Definition: mptrac.h:710
#define SO2_DISS_K2_TEMP
Temperature dependence of the second SO2 dissociation constant [K].
Definition: mptrac.h:444
#define PARTICLE_LOOP(ip0, ip1, check_dt,...)
Loop over particle indices with OpenACC acceleration.
Definition: mptrac.h:1766
#define MA
Molar mass of dry air [g/mol].
Definition: mptrac.h:289
#define RADIO_HALF_LIFE_CS137
Half-life of Cs-137 [s].
Definition: mptrac.h:479
#define P0
Standard pressure [hPa].
Definition: mptrac.h:304
#define AVO
Avogadro constant [1/mol].
Definition: mptrac.h:249
#define KB
Boltzmann constant [kg m^2/(K s^2)].
Definition: mptrac.h:284
#define SO2_CORR_B
Exponent of the high-SO2 correction [1].
Definition: mptrac.h:384
#define COMPRESS_BPV(n, stored_size)
Calculate bits per value from stored size and element count.
Definition: mptrac.h:796
#define MH2O
Molar mass of water vapor [g/mol].
Definition: mptrac.h:294
#define METVAR
Number of 3-D meteorological variables.
Definition: mptrac.h:563
#define NENS
Maximum number of data points for ensemble analysis.
Definition: mptrac.h:583
#define FWRITE(ptr, type, size, out)
Write data from memory to a file stream.
Definition: mptrac.h:1160
#define PW(p, h2o)
Calculate partial water vapor pressure.
Definition: mptrac.h:1871
#define H0
Scale height [km].
Definition: mptrac.h:269
#define NC_PUT_ATT_GLOBAL(attname, text)
Add a global text attribute to a NetCDF file.
Definition: mptrac.h:1719
#define MOLEC_DENS(p, t)
Calculate the density of a gas molecule.
Definition: mptrac.h:1495
#define LAPSE(p1, t1, p2, t2)
Calculate lapse rate.
Definition: mptrac.h:1333
#define NC(cmd)
Execute a NetCDF command and check for errors.
Definition: mptrac.h:1509
#define RADIO_DRY_VDEP_I131
Dry deposition velocity of aerosol-bound I-131 [m/s].
Definition: mptrac.h:509
#define SELECT_TIMER(id, group)
Select and start a timer with specific attributes.
Definition: mptrac.h:2492
#define RADIO_DRY_VDEP_CS137
Dry deposition velocity of Cs-137 [m/s].
Definition: mptrac.h:504
#define SO2_DISS_K1_REF
First SO2 dissociation constant at CHEM_REF_TEMP [mol/L].
Definition: mptrac.h:429
#define ECC_READ_3D(variable, level, target, scaling_factor, found_flag)
Writes 3D data from a grib message into the meteo struct.
Definition: mptrac.h:1095
void compress_zfp(const ctl_t *ctl, const met_t *met, const char *varname, float *array, const int decompress, FILE *level_log, FILE *inout)
Compresses or decompresses a 3D array of floats using the ZFP library.
#define SO2_DISS_K1_TEMP
Temperature dependence of the first SO2 dissociation constant [K].
Definition: mptrac.h:434
#define DOTP(a, b)
Calculate the dot product of two vectors.
Definition: mptrac.h:1037
#define RA
Specific gas constant of dry air [J/(kg K)].
Definition: mptrac.h:309
#define DD_EY_GLOB
Maximum number of latitudes of global meteo data.
Definition: mptrac.h:633
#define KARMAN
Karman's constant.
Definition: mptrac.h:279
#define INTPOL_INIT
Initialize arrays for interpolation.
Definition: mptrac.h:1175
#define MIN(a, b)
Macro to determine the minimum of two values.
Definition: mptrac.h:1480
#define O1D_RATE_CFC12_B
O(1D) reaction temperature parameter for CFC-12 [K].
Definition: mptrac.h:414
#define ERRMSG(...)
Print an error message with contextual information and terminate the program.
Definition: mptrac.h:2418
#define NC_PUT_INT(varname, ptr, hyperslab)
Write integer data to a NetCDF variable.
Definition: mptrac.h:1680
#define O1D_RATE_N2O_A
O(1D) reaction pre-factor for N2O [cm^3/s].
Definition: mptrac.h:419
#define EY
Maximum number of latitudes for meteo data.
Definition: mptrac.h:553
#define RADIO_DRY_VDEP_PB210
Dry deposition velocity of Pb-210 [m/s].
Definition: mptrac.h:494
#define SH(h2o)
Compute specific humidity from water vapor volume mixing ratio.
Definition: mptrac.h:2036
void compress_sz3(const ctl_t *ctl, const met_t *met, const char *varname, float *array, const int decompress, FILE *level_log, FILE *inout)
Compresses or decompresses a 3-D float array using the SZ3 library.
#define H2O2_HENRY_TEMP
Temperature dependence of the H2O2 Henry constant [K].
Definition: mptrac.h:374
#define INTPOL_3D(var, init)
Perform 3D interpolation for a meteorological variable.
Definition: mptrac.h:1206
#define CLAMP(v, lo, hi)
Clamp a value to a specified range.
Definition: mptrac.h:757
#define M_AIR_MOLECULE
Mean mass of an air molecule [kg].
Definition: mptrac.h:339
#define NOBS
Maximum number of observation data points.
Definition: mptrac.h:588
#define NTHREADS
Maximum number of OpenMP threads.
Definition: mptrac.h:593
#define ARRAY_2D(ix, iy, ny)
Macro for computing the linear index of a 2D array element.
Definition: mptrac.h:691
#define Z(p)
Convert pressure to altitude.
Definition: mptrac.h:2255
#define codes_handle
Placeholder when ECCODES is not available.
Definition: mptrac.h:240
void compress_zstd(const ctl_t *ctl, const met_t *met, const char *varname, float *array, const int decompress, FILE *level_log, FILE *inout)
Compresses or decompresses a float array using ZSTD.
#define SO2_CORR_A
Scale factor of the high-SO2 correction [1].
Definition: mptrac.h:379
#define P(z)
Compute pressure at given altitude.
Definition: mptrac.h:1796
#define LV
Latent heat of vaporization of water [J/kg].
Definition: mptrac.h:274
#define RADIO_HALF_LIFE_I131
Half-life of I-131 [s].
Definition: mptrac.h:484
#define RADIO_HALF_LIFE_BE7
Half-life of Be-7 [s].
Definition: mptrac.h:474
#define G0
Standard gravity [m/s^2].
Definition: mptrac.h:264
void compress_cms(const ctl_t *ctl, const met_t *met, const char *varname, float *array, const int decompress, FILE *level_log, FILE *inout)
Compresses or decompresses a 3-D meteorological field using cmultiscale.
#define CP
Maximum number of pressure levels for climatological data.
Definition: mptrac.h:608
#define NQ
Maximum number of quantities per data point.
Definition: mptrac.h:573
#define FREAD(ptr, type, size, in)
Read data from a file stream and store it in memory.
Definition: mptrac.h:1140
#define H2O2_SO2_RATE_REF
Reference rate for aqueous H2O2-SO2 chemistry [L^2/(mol^2 s)].
Definition: mptrac.h:349
#define DX2DEG(dx, lat)
Convert a distance in kilometers to degrees longitude at a given latitude.
Definition: mptrac.h:905
#define DEG2DY(dlat)
Convert a latitude difference to a distance in the y-direction (north-south).
Definition: mptrac.h:841
#define O1D_RATE_CFC12_A
O(1D) reaction pre-factor for CFC-12 [cm^3/s].
Definition: mptrac.h:409
#define EX
Maximum number of longitudes for meteo data.
Definition: mptrac.h:548
#define EPS
Ratio of the specific gas constant of dry air and water vapor [1].
Definition: mptrac.h:259
#define PSICE(t)
Compute saturation pressure over ice (WMO, 2018).
Definition: mptrac.h:1844
#define O1D_RATE_N2O_B
O(1D) reaction temperature parameter for N2O [K].
Definition: mptrac.h:424
#define DX2COORD(met, dx, lat)
Convert a distance in meters to a coordinate value based on grid type.
Definition: mptrac.h:967
#define O1D_RATE_CCL4_B
O(1D) reaction temperature parameter for CCl4 [K].
Definition: mptrac.h:394
#define COMPRESS_RATIO(raw_size, stored_size)
Calculate the compression ratio from raw and stored byte counts.
Definition: mptrac.h:784
#define WET_DEPO_T_ICE
Lower temperature of the ice-cloud retention transition [K].
Definition: mptrac.h:454
#define THETA(p, t)
Compute potential temperature.
Definition: mptrac.h:2136
#define RI
Ideal gas constant [J/(mol K)].
Definition: mptrac.h:319
int read_met_grib(const char *filename, const ctl_t *ctl, met_t *met)
Reads meteorological data from a grib file and processes it.
#define RADIO_WET_COEFF_BE7
Wet deposition coefficient of Be-7 [s^-1].
Definition: mptrac.h:519
#define SET_QNT(qnt, name, longname, unit)
Set atmospheric quantity index.
Definition: mptrac.h:2015
#define TICE(p, h2o)
Calculate frost point temperature (WMO, 2018).
Definition: mptrac.h:2112
void compress_lz4(const ctl_t *ctl, const met_t *met, const char *varname, float *array, const int decompress, FILE *level_log, FILE *inout)
Compresses or decompresses a float array using LZ4.
#define O1D_RATE_CFC11_A
O(1D) reaction pre-factor for CFC-11 [cm^3/s].
Definition: mptrac.h:399
#define RADIO_WET_COEFF_CS137
Wet deposition coefficient of Cs-137 [s^-1].
Definition: mptrac.h:524
#define TOK(line, tok, format, var)
Get string tokens.
Definition: mptrac.h:2186
#define ZDIFF(lnp0, t0, h2o0, lnp1, t1, h2o1)
Calculate geopotential height difference.
Definition: mptrac.h:2286
#define THETAVIRT(p, t, h2o)
Compute virtual potential temperature.
Definition: mptrac.h:2165
#define DZ2DP(dz, p)
Convert a change in altitude to a change in pressure.
Definition: mptrac.h:942
#define WARN(...)
Print a warning message with contextual information.
Definition: mptrac.h:2385
#define H2O2_SO2_RATE_TEMP
Temperature parameter for the aqueous H2O2-SO2 rate [K].
Definition: mptrac.h:354
#define ZETA(ps, p, t)
Computes the value of the zeta vertical coordinate.
Definition: mptrac.h:2305
#define RHICE(p, t, h2o)
Compute relative humidity over ice.
Definition: mptrac.h:1948
#define INTPOL_TIME_ALL(time, p, lon, lat)
Interpolate multiple meteorological variables in time.
Definition: mptrac.h:1279
#define ALLOC(ptr, type, n)
Allocate memory for a pointer with error handling.
Definition: mptrac.h:667
void read_met_grib_levels(codes_handle **handles, const int num_messages, const ctl_t *ctl, met_t *met)
Reads meteorological variables at different vertical levels from a grib file.
#define SET_ATM(qnt, val)
Set atmospheric quantity value.
Definition: mptrac.h:1992
#define O1D_RATE_CCL4_A
O(1D) reaction pre-factor for CCl4 [cm^3/s].
Definition: mptrac.h:389
#define CTS
Maximum number of data points of climatological time series.
Definition: mptrac.h:623
#define ECC_READ_2D(variable, target, scaling_factor, found_flag)
Writes 2-D data from a grib message into the meteo struct.
Definition: mptrac.h:1070
#define OMEGA_EARTH
Angular velocity of Earth [s^-1].
Definition: mptrac.h:329
#define DEG2RAD(deg)
Converts degrees to radians.
Definition: mptrac.h:858
#define MO3
Molar mass of ozone [g/mol].
Definition: mptrac.h:299
#define SQR(x)
Compute the square of a value.
Definition: mptrac.h:2049
#define RADIO_WET_COEFF_I131
Wet deposition coefficient of aerosol-bound I-131 [s^-1].
Definition: mptrac.h:529
#define RAD2DEG(rad)
Converts radians to degrees.
Definition: mptrac.h:1888
#define RADIO_DRY_VDEP_BE7
Dry deposition velocity of Be-7 [m/s].
Definition: mptrac.h:499
#define NP
Maximum number of atmospheric data points.
Definition: mptrac.h:568
#define NTIMER
Maximum number of timers.
Definition: mptrac.h:2462
#define COMPRESS_SPEED(nbytes, dt)
Calculate compression throughput in MiB/s.
Definition: mptrac.h:772
#define INTPOL_2D(var, init)
Perform 2D interpolation for a meteorological variable.
Definition: mptrac.h:1189
#define RH(p, t, h2o)
Compute relative humidity over water.
Definition: mptrac.h:1918
#define H2O2_HENRY_REF
Henry constant of H2O2 at CHEM_REF_TEMP [mol/(L atm)].
Definition: mptrac.h:369
#define CHEM_REF_TEMP
Reference temperature for chemical equilibrium constants [K].
Definition: mptrac.h:344
#define NC_PUT_FLOAT(varname, ptr, hyperslab)
Write a float array to a NetCDF file.
Definition: mptrac.h:1657
#define DD_EX_GLOB
Maximum number of longitudes of global meteo data.
Definition: mptrac.h:628
#define CY
Maximum number of latitudes for climatological data.
Definition: mptrac.h:598
#define LOG(level,...)
Print a log message with a specified logging level.
Definition: mptrac.h:2348
#define RADIO_HALF_LIFE_RN222
Half-life of Rn-222 [s].
Definition: mptrac.h:464
#define NC_DEF_VAR(varname, type, ndims, dims, long_name, units, level, quant)
Define a NetCDF variable with attributes.
Definition: mptrac.h:1538
#define TDEW(p, h2o)
Calculate dew point temperature.
Definition: mptrac.h:2087
#define KAPPA
Exponent used for potential-temperature calculations [1].
Definition: mptrac.h:324
#define ARRHENIUS(a, b, t)
Calculate the Arrhenius rate constant.
Definition: mptrac.h:735
#define NCSI
Maximum number of data points for CSI calculation.
Definition: mptrac.h:578
#define NC_GET_DOUBLE(varname, ptr, force)
Retrieve a double-precision variable from a NetCDF file.
Definition: mptrac.h:1579
#define EP
Maximum number of pressure levels for meteo data.
Definition: mptrac.h:543
#define PSAT(t)
Compute saturation pressure over water.
Definition: mptrac.h:1820
#define SO2_DISS_K2_REF
Second SO2 dissociation constant at CHEM_REF_TEMP [mol/L].
Definition: mptrac.h:439
#define SO2_HENRY_REF
Henry constant of SO2 at CHEM_REF_TEMP [mol/(L atm)].
Definition: mptrac.h:359
#define SO2_HENRY_TEMP
Temperature dependence of the SO2 Henry constant [K].
Definition: mptrac.h:364
#define RHO(p, t)
Compute density of air.
Definition: mptrac.h:1973
#define RADIO_HALF_LIFE_PB210
Half-life of Pb-210 [s].
Definition: mptrac.h:469
void module_kpp_chem(ctl_t *ctl, cache_t *cache, clim_t *clim, met_t *met0, met_t *met1, atm_t *atm)
KPP chemistry module.
#define CO3
Maximum number of total column ozone data for climatological data.
Definition: mptrac.h:603
void read_met_grib_grid(codes_handle **handles, int count_handles, met_t *met)
Reads global meteorological information from a grib file.
#define NC_PUT_DOUBLE(varname, ptr, hyperslab)
Write double precision data to a NetCDF variable.
Definition: mptrac.h:1633
#define ECC(cmd)
Execute an ECCODES command and check for errors.
Definition: mptrac.h:1051
#define LIN(x0, y0, x1, y1, x)
Linear interpolation.
Definition: mptrac.h:1352
#define WET_DEPO_T_LIQUID
Upper temperature of the ice-cloud retention transition [K].
Definition: mptrac.h:449
#define DIST2(a, b)
Calculate the squared Euclidean distance between two points in Cartesian coordinates.
Definition: mptrac.h:1021
#define NC_INQ_DIM(dimname, ptr, min, max, check)
Inquire the length of a dimension in a NetCDF file.
Definition: mptrac.h:1609
#define DEG2DX(dlon, lat)
Convert a longitude difference to a distance in the x-direction (east-west) at a specific latitude.
Definition: mptrac.h:820
#define DY2COORD(met, dy)
Convert a distance to coordinate value based on grid type.
Definition: mptrac.h:990
#define CPD
Specific heat of dry air at constant pressure [J/(kg K)].
Definition: mptrac.h:254
#define CSZA
Maximum number of solar zenith angles for climatological data.
Definition: mptrac.h:613
#define DY2DEG(dy)
Convert a distance in kilometers to degrees latitude.
Definition: mptrac.h:923
#define O1D_RATE_CFC11_B
O(1D) reaction temperature parameter for CFC-11 [K].
Definition: mptrac.h:404
#define MAX(a, b)
Macro to determine the maximum of two values.
Definition: mptrac.h:1379
#define WET_DEPO_T_LIQUID_BC
Temperature threshold for below-cloud retention [K].
Definition: mptrac.h:459
#define RADIO_HALF_LIFE_XE133
Half-life of Xe-133 [s].
Definition: mptrac.h:489
#define RADIO_WET_COEFF_PB210
Wet deposition coefficient of Pb-210 [s^-1].
Definition: mptrac.h:514
#define FMOD(x, y)
Calculate the floating-point remainder of dividing x by y.
Definition: mptrac.h:1122
#define DOBSON_UNIT
Ozone column mass corresponding to one Dobson unit [kg/m^2].
Definition: mptrac.h:334
Air parcel data.
Definition: mptrac.h:3604
double time[NP]
Time [s].
Definition: mptrac.h:3610
double lat[NP]
Latitude [deg].
Definition: mptrac.h:3619
double lon[NP]
Longitude [deg].
Definition: mptrac.h:3616
int np
Number of air parcels.
Definition: mptrac.h:3607
double q[NQ][NP]
Quantity data (for various, user-defined attributes).
Definition: mptrac.h:3622
double p[NP]
Pressure [hPa].
Definition: mptrac.h:3613
Cache data structure.
Definition: mptrac.h:3661
double dt[NP]
Timesteps [s].
Definition: mptrac.h:3679
double iso_ts[NP]
Shared balloon times [s] for ISOSURF=4.
Definition: mptrac.h:3667
int iso_n
Isosurface balloon number of data points.
Definition: mptrac.h:3670
double iso_ps[NP]
Shared balloon pressure time series [hPa] for ISOSURF=4.
Definition: mptrac.h:3664
double rs[3 *NP+1]
Random numbers.
Definition: mptrac.h:3676
float uvwp[NP][3]
Wind perturbations [m/s].
Definition: mptrac.h:3673
Climatological data in the form of photolysis rates.
Definition: mptrac.h:3717
int nsza
Number of solar zenith angles.
Definition: mptrac.h:3723
double sza[CSZA]
Solar zenith angle [rad].
Definition: mptrac.h:3732
double o3_1[CP][CSZA][CO3]
O3 photolysis rate (O3 + hv = O1d + O2) [1/s].
Definition: mptrac.h:3753
double p[CP]
Pressure [hPa].
Definition: mptrac.h:3729
double ccl2f2[CP][CSZA][CO3]
CCl2F2 photolysis rate [1/s].
Definition: mptrac.h:3747
double o2[CP][CSZA][CO3]
O2 photolysis rate [1/s].
Definition: mptrac.h:3750
double ccl3f[CP][CSZA][CO3]
CCl3F photolysis rate [1/s].
Definition: mptrac.h:3744
double n2o[CP][CSZA][CO3]
N2O photolysis rate [1/s].
Definition: mptrac.h:3738
double h2o2[CP][CSZA][CO3]
H2O2 photolysis rate [1/s].
Definition: mptrac.h:3759
double h2o[CP][CSZA][CO3]
H2O photolysis rate [1/s].
Definition: mptrac.h:3762
double ccl4[CP][CSZA][CO3]
CCl4 photolysis rate [1/s].
Definition: mptrac.h:3741
double o3_2[CP][CSZA][CO3]
O3 photolysis rate (O3 + hv = O3p + O2) [1/s].
Definition: mptrac.h:3756
double o3c[CO3]
Total column ozone [DU].
Definition: mptrac.h:3735
int np
Number of pressure levels.
Definition: mptrac.h:3720
int no3c
Number of total ozone columns.
Definition: mptrac.h:3726
Climatological data.
Definition: mptrac.h:3825
clim_ts_t ccl2f2
CFC-12 time series.
Definition: mptrac.h:3867
clim_photo_t photo
Photolysis rates.
Definition: mptrac.h:3843
clim_zm_t ho2
HO2 zonal means.
Definition: mptrac.h:3855
clim_zm_t hno3
HNO3 zonal means.
Definition: mptrac.h:3846
int tropo_ntime
Number of tropopause timesteps.
Definition: mptrac.h:3828
clim_ts_t sf6
SF6 time series.
Definition: mptrac.h:3873
clim_ts_t ccl4
CFC-10 time series.
Definition: mptrac.h:3861
clim_ts_t ccl3f
CFC-11 time series.
Definition: mptrac.h:3864
clim_zm_t o1d
O(1D) zonal means.
Definition: mptrac.h:3858
double tropo_lat[73]
Tropopause latitudes [deg].
Definition: mptrac.h:3837
clim_zm_t h2o2
H2O2 zonal means.
Definition: mptrac.h:3852
int tropo_nlat
Number of tropopause latitudes.
Definition: mptrac.h:3831
clim_zm_t oh
OH zonal means.
Definition: mptrac.h:3849
double tropo[12][73]
Tropopause pressure values [hPa].
Definition: mptrac.h:3840
double tropo_time[12]
Tropopause time steps [s].
Definition: mptrac.h:3834
clim_ts_t n2o
N2O time series.
Definition: mptrac.h:3870
Climatological data in the form of time series.
Definition: mptrac.h:3773
double vmr[CTS]
Volume mixing ratio [ppv].
Definition: mptrac.h:3782
double time[CTS]
Time [s].
Definition: mptrac.h:3779
int ntime
Number of timesteps.
Definition: mptrac.h:3776
Climatological data in the form of zonal means.
Definition: mptrac.h:3793
double time[CT]
Time [s].
Definition: mptrac.h:3805
int np
Number of pressure levels.
Definition: mptrac.h:3802
double p[CP]
Pressure [hPa].
Definition: mptrac.h:3811
double vmr[CT][CP][CY]
Volume mixing ratio [ppv].
Definition: mptrac.h:3814
int ntime
Number of timesteps.
Definition: mptrac.h:3796
int nlat
Number of latitudes.
Definition: mptrac.h:3799
double lat[CY]
Latitude [deg].
Definition: mptrac.h:3808
Control parameters.
Definition: mptrac.h:2506
double met_utm_ref_lon
Reference longitude [deg] for UTM grid.
Definition: mptrac.h:2871
double grid_z0
Lower altitude of gridded data [km].
Definition: mptrac.h:3451
int qnt_o3
Quantity array index for ozone volume mixing ratio.
Definition: mptrac.h:2618
double csi_lat1
Upper latitude of gridded CSI data [deg].
Definition: mptrac.h:3412
char csi_obsfile[LEN]
Observation data file for CSI analysis.
Definition: mptrac.h:3379
int qnt_Coh
Quantity array index for OH volume mixing ratio (chemistry code).
Definition: mptrac.h:2780
double wet_depo_ic_a
Coefficient A for wet deposition in cloud (exponential form).
Definition: mptrac.h:3285
int qnt_eta_d
Quantity array index for diagnosed eta vertical coordinate.
Definition: mptrac.h:2735
int qnt_target_subdomain
Quantity array index for destination subdomain in domain decomposition.
Definition: mptrac.h:2837
int met_nc_scale
Check netCDF scaling factors (0=no, 1=yes).
Definition: mptrac.h:2888
int qnt_pel
Quantity array index for pressure at equilibrium level (EL).
Definition: mptrac.h:2651
int csi_nz
Number of altitudes of gridded CSI data.
Definition: mptrac.h:3388
double molmass
Molar mass [g/mol].
Definition: mptrac.h:3141
int qnt_p
Quantity array index for pressure.
Definition: mptrac.h:2597
int qnt_Cccl2f2
Quantity array index for CFC-12 volume mixing ratio (chemistry code).
Definition: mptrac.h:2804
int dd_halos_size
Domain decomposition halo size in grid points.
Definition: mptrac.h:3581
char atm_gpfile[LEN]
Gnuplot file for atmospheric data.
Definition: mptrac.h:3334
int mixing_nx
Number of longitudes of mixing grid.
Definition: mptrac.h:3204
int met_zstd_nworkers
ZSTD number of worker threads (0=single-threaded, default=4).
Definition: mptrac.h:2903
double chemgrid_z1
Upper altitude of chemistry grid [km].
Definition: mptrac.h:3228
char qnt_format[NQ][LEN]
Quantity output format.
Definition: mptrac.h:2525
int qnt_m
Quantity array index for mass.
Definition: mptrac.h:2537
int qnt_aoa
Quantity array index for age of air.
Definition: mptrac.h:2813
int qnt_rhop
Quantity array index for particle density.
Definition: mptrac.h:2546
int qnt_swc
Quantity array index for cloud snow water content.
Definition: mptrac.h:2630
double csi_obsmin
Minimum observation index to trigger detection.
Definition: mptrac.h:3382
int qnt_pcb
Quantity array index for cloud bottom pressure.
Definition: mptrac.h:2639
char clim_n2o_timeseries[LEN]
Filename of N2O time series.
Definition: mptrac.h:3180
double bound_dzs
Boundary conditions surface layer depth [km].
Definition: mptrac.h:3129
double csi_lon1
Upper longitude of gridded CSI data [deg].
Definition: mptrac.h:3403
int qnt_u
Quantity array index for zonal wind.
Definition: mptrac.h:2606
double stat_lon
Longitude of station [deg].
Definition: mptrac.h:3529
double sort_dt
Time step for sorting of particle data [s].
Definition: mptrac.h:3034
double mixing_z1
Upper altitude of mixing grid [km].
Definition: mptrac.h:3201
double stat_r
Search radius around station [km].
Definition: mptrac.h:3535
double wet_depo_bc_a
Coefficient A for wet deposition below cloud (exponential form).
Definition: mptrac.h:3279
int met_zstd_level
ZSTD compression level (from -5 to 22, default=-3).
Definition: mptrac.h:2900
double met_utm_ref_lat
Reference latitude [deg] for UTM grid.
Definition: mptrac.h:2868
int csi_ny
Number of latitudes of gridded CSI data.
Definition: mptrac.h:3406
int vtk_sphere
Spherical projection for VTK data (0=no, 1=yes).
Definition: mptrac.h:3559
double chemgrid_z0
Lower altitude of chemistry grid [km].
Definition: mptrac.h:3225
double met_pbl_min
Minimum depth of planetary boundary layer [km].
Definition: mptrac.h:3002
int qnt_iwc
Quantity array index for cloud ice water content.
Definition: mptrac.h:2627
double chemgrid_lat0
Lower latitude of chemistry grid [deg].
Definition: mptrac.h:3243
double conv_cape
CAPE threshold for convection module [J/kg].
Definition: mptrac.h:3093
int qnt_Co1d
Quantity array index for O(1D) volume mixing ratio (chemistry code).
Definition: mptrac.h:2792
int qnt_pw
Quantity array index for partial water vapor pressure.
Definition: mptrac.h:2705
char prof_basename[LEN]
Basename for profile output file.
Definition: mptrac.h:3478
double grid_z1
Upper altitude of gridded data [km].
Definition: mptrac.h:3454
int direction
Direction flag (1=forward calculation, -1=backward calculation).
Definition: mptrac.h:2840
char balloon[LEN]
Balloon position filename.
Definition: mptrac.h:3041
int qnt_Cccl4
Quantity array index for CFC-10 volume mixing ratio (chemistry code).
Definition: mptrac.h:2798
int met_dp
Stride for pressure levels.
Definition: mptrac.h:2954
double met_dt_out
Time step for sampling of meteo data along trajectories [s].
Definition: mptrac.h:3021
int qnt_h2o2
Quantity array index for H2O2 volume mixing ratio (climatology).
Definition: mptrac.h:2669
int qnt_vh
Quantity array index for horizontal wind.
Definition: mptrac.h:2747
char species[LEN]
Species.
Definition: mptrac.h:3138
int csi_nx
Number of longitudes of gridded CSI data.
Definition: mptrac.h:3397
double csi_lat0
Lower latitude of gridded CSI data [deg].
Definition: mptrac.h:3409
double turb_dz_trop
Vertical turbulent diffusion coefficient (troposphere) [m^2/s].
Definition: mptrac.h:3072
int met_pbl
Planetary boundary layer data (0=file, 1=z2p, 2=Richardson, 3=theta).
Definition: mptrac.h:2999
int qnt_lwc
Quantity array index for cloud liquid water content.
Definition: mptrac.h:2621
double turb_mesoz
Vertical scaling factor for mesoscale wind fluctuations.
Definition: mptrac.h:3081
int grid_nc_level
zlib compression level of netCDF grid data files (0=off).
Definition: mptrac.h:3439
int grid_nx
Number of longitudes of gridded data.
Definition: mptrac.h:3457
int atm_type
Type of atmospheric data files (0=ASCII, 1=binary, 2=netCDF, 3=CLaMS_traj, 4=CLaMS_pos).
Definition: mptrac.h:3347
double bound_mass
Boundary conditions mass per particle [kg].
Definition: mptrac.h:3102
double grid_lat0
Lower latitude of gridded data [deg].
Definition: mptrac.h:3469
int qnt_ts
Quantity array index for surface temperature.
Definition: mptrac.h:2552
int qnt_loss_rate
Quantity array index for total loss rate.
Definition: mptrac.h:2696
int qnt_plfc
Quantity array index for pressure at level of free convection (LCF).
Definition: mptrac.h:2648
int qnt_Acs137
Quantity array index for radioactive activity of Cs-137.
Definition: mptrac.h:2825
double grid_lon0
Lower longitude of gridded data [deg].
Definition: mptrac.h:3460
int qnt_o1d
Quantity array index for O(1D) volume mixing ratio (climatology).
Definition: mptrac.h:2675
int met_tropo_spline
Tropopause interpolation method (0=linear, 1=spline).
Definition: mptrac.h:3018
char sample_kernel[LEN]
Kernel data file for sample output.
Definition: mptrac.h:3514
int qnt_tvirt
Quantity array index for virtual temperature.
Definition: mptrac.h:2741
double dt_met
Time step of meteo data [s].
Definition: mptrac.h:2859
char clim_ho2_filename[LEN]
Filename of HO2 climatology.
Definition: mptrac.h:3162
double chemgrid_lat1
Upper latitude of chemistry grid [deg].
Definition: mptrac.h:3246
int met_geopot_sy
Latitudinal smoothing of geopotential heights.
Definition: mptrac.h:2990
char grid_gpfile[LEN]
Gnuplot file for gridded data.
Definition: mptrac.h:3430
double turb_dx_strat
Horizontal turbulent diffusion coefficient (stratosphere) [m^2/s].
Definition: mptrac.h:3066
int qnt_vmr
Quantity array index for volume mixing ratio.
Definition: mptrac.h:2540
int qnt_lsm
Quantity array index for land-sea mask.
Definition: mptrac.h:2573
int qnt_theta
Quantity array index for potential temperature.
Definition: mptrac.h:2717
double bound_lat1
Boundary conditions maximum longitude [deg].
Definition: mptrac.h:3117
double stat_t1
Stop time for station output [s].
Definition: mptrac.h:3541
char csi_kernel[LEN]
Kernel data file for CSI output.
Definition: mptrac.h:3373
double turb_dx_trop
Horizontal turbulent diffusion coefficient (troposphere) [m^2/s].
Definition: mptrac.h:3063
int grid_type
Type of grid data files (0=ASCII, 1=netCDF).
Definition: mptrac.h:3475
double csi_lon0
Lower longitude of gridded CSI data [deg].
Definition: mptrac.h:3400
int qnt_pbl
Quantity array index for boundary layer pressure.
Definition: mptrac.h:2579
double oh_chem[4]
Coefficients for OH reaction rate (A, E/R or k0, n, kinf, m).
Definition: mptrac.h:3252
int grid_stddev
Include standard deviations in grid output (0=no, 1=yes).
Definition: mptrac.h:3445
int met_nc_read_mode
NetCDF DD input (0=local hyperslabs, 1=global 2-D slices via MPI).
Definition: mptrac.h:2891
int qnt_psice
Quantity array index for saturation pressure over ice.
Definition: mptrac.h:2702
double chemgrid_lon0
Lower longitude of chemistry grid [deg].
Definition: mptrac.h:3234
int bound_pbl
Boundary conditions planetary boundary layer (0=no, 1=yes).
Definition: mptrac.h:3135
int qnt_mloss_wet
Quantity array index for total mass loss due to wet deposition.
Definition: mptrac.h:2687
int radio_decay
RADIO_DECAY switch for airborne and deposited activity (0=off, 1=on, default: 0).
Definition: mptrac.h:3270
int met_geopot_sx
Longitudinal smoothing of geopotential heights.
Definition: mptrac.h:2987
int met_sy
Smoothing for latitudes.
Definition: mptrac.h:2960
int qnt_ps
Quantity array index for surface pressure.
Definition: mptrac.h:2549
int rng_type
Random number generator (0=GSL, 1=Squares, 2=cuRAND).
Definition: mptrac.h:3051
char prof_obsfile[LEN]
Observation data file for profile output.
Definition: mptrac.h:3481
int met_pck_zstd
Apply an additional ZSTD compression step to PCK payloads (0=off, 1=on).
Definition: mptrac.h:2909
int isosurf
Isosurface parameter (0=none, 1=pressure, 2=density, 3=theta, 4=balloon).
Definition: mptrac.h:3038
double bound_p1
Boundary conditions top pressure [hPa].
Definition: mptrac.h:3123
int qnt_zs
Quantity array index for surface geopotential height.
Definition: mptrac.h:2555
int prof_nz
Number of altitudes of gridded profile data.
Definition: mptrac.h:3484
double csi_dt_out
Time step for CSI output [s].
Definition: mptrac.h:3376
int met_cape
Convective available potential energy data (0=file, 1=calculate).
Definition: mptrac.h:2996
double csi_modmin
Minimum column density to trigger detection [kg/m^2].
Definition: mptrac.h:3385
int met_sx
Smoothing for longitudes.
Definition: mptrac.h:2957
double chemgrid_lon1
Upper longitude of chemistry grid [deg].
Definition: mptrac.h:3237
double depo_dt_out
DEPO_DT_OUT time interval for radioactive deposition output [s] (default: 86400).
Definition: mptrac.h:3325
double turb_mesox
Horizontal scaling factor for mesoscale wind fluctuations.
Definition: mptrac.h:3078
char grid_kernel[LEN]
Kernel data file for grid output.
Definition: mptrac.h:3427
double prof_z0
Lower altitude of gridded profile data [km].
Definition: mptrac.h:3487
int qnt_w
Quantity array index for vertical velocity.
Definition: mptrac.h:2612
double bound_vmr
Boundary conditions volume mixing ratio [ppv].
Definition: mptrac.h:3108
double met_tropo_pv
Dynamical tropopause potential vorticity threshold [PVU].
Definition: mptrac.h:3012
int prof_nx
Number of longitudes of gridded profile data.
Definition: mptrac.h:3493
int qnt_stat
Quantity array index for station flag.
Definition: mptrac.h:2534
int dd_supported
Whether the executable implements the domain decomposition.
Definition: mptrac.h:3592
double dd_sort_dt
Sorting time interval for the compactification (DD_EXCHANGE_MODE=0).
Definition: mptrac.h:3584
int met_tropo
Tropopause definition (0=none, 1=clim, 2=cold point, 3=WMO_1st, 4=WMO_2nd, 5=dynamical).
Definition: mptrac.h:3009
int qnt_rp
Quantity array index for particle radius.
Definition: mptrac.h:2543
int met_mpi_share
Use MPI to share meteo (0=no, 1=yes).
Definition: mptrac.h:3027
int qnt_vz
Quantity array index for vertical velocity.
Definition: mptrac.h:2750
int qnt_ho2
Quantity array index for HO2 volume mixing ratio (climatology).
Definition: mptrac.h:2672
double csi_z1
Upper altitude of gridded CSI data [km].
Definition: mptrac.h:3394
double stat_t0
Start time for station output [s].
Definition: mptrac.h:3538
double oh_chem_beta
Beta parameter for diurnal variability of OH.
Definition: mptrac.h:3255
int dd
Domain decomposition (0=no, 1=yes).
Definition: mptrac.h:3569
char clim_o1d_filename[LEN]
Filename of O(1D) climatology.
Definition: mptrac.h:3165
int qnt_eta
Quantity array index for eta vertical coordinate.
Definition: mptrac.h:2732
char clim_photo[LEN]
Filename of photolysis rates climatology.
Definition: mptrac.h:3150
double wet_depo_so2_ph
pH value used to calculate effective Henry constant of SO2.
Definition: mptrac.h:3297
double mixing_z0
Lower altitude of mixing grid [km].
Definition: mptrac.h:3198
int qnt_mloss_decay
Quantity array index for total mass loss due to exponential decay.
Definition: mptrac.h:2693
int atm_type_out
Type of atmospheric data files for output (-1=same as ATM_TYPE, 0=ASCII, 1=binary,...
Definition: mptrac.h:3352
int met_cms_nd0x
cmultiscale number of cells of coarsest grid in x-direction.
Definition: mptrac.h:2936
int met_nlev
Number of meteo data model levels.
Definition: mptrac.h:2978
double dt_kpp
Time step for KPP chemistry [s].
Definition: mptrac.h:3264
char csi_basename[LEN]
Basename of CSI data files.
Definition: mptrac.h:3370
double dry_depo_dp
Dry deposition surface layer [hPa].
Definition: mptrac.h:3306
int qnt_shf
Quantity array index for surface sensible heat flux.
Definition: mptrac.h:2570
int qnt_vs
Quantity array index for surface meridional wind.
Definition: mptrac.h:2561
int qnt_Cco
Quantity array index for CO volume mixing ratio (chemistry code).
Definition: mptrac.h:2777
double vtk_dt_out
Time step for VTK data output [s].
Definition: mptrac.h:3547
double t_stop
Stop time of simulation [s].
Definition: mptrac.h:2846
double conv_dt
Time interval for convection module [s].
Definition: mptrac.h:3099
char sample_obsfile[LEN]
Observation data file for sample output.
Definition: mptrac.h:3517
int qnt_hno3
Quantity array index for HNO3 volume mixing ratio (climatology).
Definition: mptrac.h:2663
char grid_basename[LEN]
Basename of grid data files.
Definition: mptrac.h:3424
int met_clams
Read MPTRAC or CLaMS meteo data (0=MPTRAC, 1=CLaMS).
Definition: mptrac.h:2885
char met_comp_logfile[LEN]
Filename for per-level compression diagnostics ("-" disables output).
Definition: mptrac.h:2927
int qnt_h2ot
Quantity array index for tropopause water vapor volume mixing ratio.
Definition: mptrac.h:2591
int qnt_rh
Quantity array index for relative humidity over water.
Definition: mptrac.h:2711
int met_gp2z
Convert surface geopotential to geopotential height (0=no, 1=yes).
Definition: mptrac.h:2878
double bound_lat0
Boundary conditions minimum longitude [deg].
Definition: mptrac.h:3114
double met_pbl_max
Maximum depth of planetary boundary layer [km].
Definition: mptrac.h:3005
int met_dx
Stride for longitudes.
Definition: mptrac.h:2948
int mixing_ny
Number of latitudes of mixing grid.
Definition: mptrac.h:3213
int met_convention
Meteo data layout (0=[lev, lat, lon], 1=[lon, lat, lev]).
Definition: mptrac.h:2862
char depo_basename[LEN]
DEPO_BASENAME for radioactive deposition files (default: disabled with "-").
Definition: mptrac.h:3322
int qnt_zeta_d
Quantity array index for diagnosed zeta vertical coordinate.
Definition: mptrac.h:2726
char clim_h2o2_filename[LEN]
Filename of H2O2 climatology.
Definition: mptrac.h:3159
int tracer_chem
Switch for first order tracer chemistry module (0=off, 1=on).
Definition: mptrac.h:3267
double dt_mod
Time step of simulation [s].
Definition: mptrac.h:2849
int diffusion
Diffusion switch (0=off, 1=on).
Definition: mptrac.h:3054
int qnt_tnat
Quantity array index for T_NAT.
Definition: mptrac.h:2765
int qnt_eta_dot
Quantity array index for velocity of eta vertical coordinate.
Definition: mptrac.h:2738
int qnt_tice
Quantity array index for T_ice.
Definition: mptrac.h:2759
int turb_pbl_scheme
PBL turbulence scheme (0=none, 1=closure).
Definition: mptrac.h:3057
double budget_dt_out
Time step for mass budget output [s].
Definition: mptrac.h:3367
int qnt_zg
Quantity array index for geopotential height.
Definition: mptrac.h:2594
double vtk_offset
Vertical offset for VTK data [km].
Definition: mptrac.h:3556
int qnt_v
Quantity array index for meridional wind.
Definition: mptrac.h:2609
int qnt_mloss_dry
Quantity array index for total mass loss due to dry deposition.
Definition: mptrac.h:2690
double bound_vmr_trend
Boundary conditions volume mixing ratio trend [ppv/s].
Definition: mptrac.h:3111
double met_zfp_tol[METVAR]
ZFP compression tolerance.
Definition: mptrac.h:2915
int met_cache
Preload meteo data into disk cache (0=no, 1=yes).
Definition: mptrac.h:3024
int qnt_oh
Quantity array index for OH volume mixing ratio (climatology).
Definition: mptrac.h:2666
int met_sz3_prec[METVAR]
SZ3 compression precision.
Definition: mptrac.h:2918
char qnt_unit[NQ][LEN]
Quantity units.
Definition: mptrac.h:2522
int qnt_Ch
Quantity array index for H volume mixing ratio (chemistry code).
Definition: mptrac.h:2783
int met_press_level_def
Use predefined pressure levels or not.
Definition: mptrac.h:2975
int oh_chem_reaction
Reaction type for OH chemistry (0=none, 2=bimolecular, 3=termolecular).
Definition: mptrac.h:3249
int qnt_h2o
Quantity array index for water vapor volume mixing ratio.
Definition: mptrac.h:2615
int prof_ny
Number of latitudes of gridded profile data.
Definition: mptrac.h:3502
int qnt_rhice
Quantity array index for relative humidity over ice.
Definition: mptrac.h:2714
int qnt_rho
Quantity array index for density of air.
Definition: mptrac.h:2603
double sample_dz
Layer depth for sample output [km].
Definition: mptrac.h:3523
double tdec_strat
Life time of particles in the stratosphere [s].
Definition: mptrac.h:3147
int obs_type
Type of observation data files (0=ASCII, 1=netCDF).
Definition: mptrac.h:3361
int grid_nc_quant[NQ]
Number of digits for quantization of netCDF grid data files (0=off).
Definition: mptrac.h:3442
int qnt_us
Quantity array index for surface zonal wind.
Definition: mptrac.h:2558
double grid_lon1
Upper longitude of gridded data [deg].
Definition: mptrac.h:3463
int qnt_Cn2o
Quantity array index for N2O volume mixing ratio (chemistry code).
Definition: mptrac.h:2807
int qnt_Cccl3f
Quantity array index for CFC-11 volume mixing ratio (chemistry code).
Definition: mptrac.h:2801
char qnt_name[NQ][LEN]
Quantity names.
Definition: mptrac.h:2516
int depo_type
DEPO_TYPE of deposition files (0=ASCII, 1=netCDF, default: 0).
Definition: mptrac.h:3328
char budget_basename[LEN]
Basename of mass budget data file.
Definition: mptrac.h:3364
char atm_basename[LEN]
Basename of atmospheric data files.
Definition: mptrac.h:3331
double mixing_lat0
Lower latitude of mixing grid [deg].
Definition: mptrac.h:3216
int nens
Number of ensembles.
Definition: mptrac.h:3415
int qnt_pt
Quantity array index for tropopause pressure.
Definition: mptrac.h:2582
int qnt_cl
Quantity array index for total column cloud water.
Definition: mptrac.h:2642
int advect
Advection scheme (1=Euler, 2=midpoint, 4=Runge-Kutta).
Definition: mptrac.h:3044
double prof_z1
Upper altitude of gridded profile data [km].
Definition: mptrac.h:3490
double met_lev_hyam[EP]
Meteo data model level a coefficients.
Definition: mptrac.h:2981
int qnt_t
Quantity array index for temperature.
Definition: mptrac.h:2600
int atm_filter
Time filter for atmospheric data output (0=none, 1=missval, 2=remove).
Definition: mptrac.h:3340
int kpp_chem
Switch for KPP chemistry module (0=off, 1=on).
Definition: mptrac.h:3261
int qnt_zeta
Quantity array index for zeta vertical coordinate.
Definition: mptrac.h:2723
double conv_pbl_trans
Depth of PBL transition layer (fraction of PBL pressure thickness).
Definition: mptrac.h:3090
int met_lz4_accel
LZ4 acceleration factor (>=1, default=8).
Definition: mptrac.h:2906
char ens_basename[LEN]
Basename of ensemble data file.
Definition: mptrac.h:3418
int qnt_Ai131
Quantity array index for radioactive activity of I-131.
Definition: mptrac.h:2828
double wet_depo_pre[2]
Coefficients for precipitation calculation.
Definition: mptrac.h:3276
double mixing_tau_trop
E-folding time for interparcel mixing in the troposphere [s].
Definition: mptrac.h:3189
int met_vert_coord
Vertical coordinate of input meteo data (0=plev, 1=mlev_p_file, 2=mlev_ab_file, 3=mlev_ab_full,...
Definition: mptrac.h:2875
double csi_z0
Lower altitude of gridded CSI data [km].
Definition: mptrac.h:3391
int qnt_lapse
Quantity array index for lapse rate.
Definition: mptrac.h:2744
int qnt_Apb210
Quantity array index for radioactive activity of Pb-210.
Definition: mptrac.h:2819
double stat_lat
Latitude of station [deg].
Definition: mptrac.h:3532
int qnt_Cho2
Quantity array index for HO2 volume mixing ratio (chemistry code).
Definition: mptrac.h:2786
double wet_depo_bc_h[2]
Coefficients for wet deposition below cloud (Henry's law: Hb, Cb).
Definition: mptrac.h:3294
int grid_ny
Number of latitudes of gridded data.
Definition: mptrac.h:3466
int qnt_Csf6
Quantity array index for SF6 volume mixing ratio (chemistry code).
Definition: mptrac.h:2810
int qnt_Ch2o
Quantity array index for H2O volume mixing ratio (chemistry code).
Definition: mptrac.h:2771
double met_detrend
FWHM of horizontal Gaussian used for detrending [km].
Definition: mptrac.h:2966
int conv_mix_pbl
Vertical mixing in the PBL (0=off, 1=on).
Definition: mptrac.h:3087
char metbase[LEN]
Basename for meteo data.
Definition: mptrac.h:2856
double bound_dps
Boundary conditions surface layer depth [hPa].
Definition: mptrac.h:3126
double met_cms_eps[METVAR]
cmultiscale compression epsilon.
Definition: mptrac.h:2945
int chemgrid_nz
Number of altitudes of chemistry grid.
Definition: mptrac.h:3222
int qnt_cape
Quantity array index for convective available potential energy (CAPE).
Definition: mptrac.h:2654
int qnt_zeta_dot
Quantity array index for velocity of zeta vertical coordinate.
Definition: mptrac.h:2729
double bound_mass_trend
Boundary conditions mass per particle trend [kg/s].
Definition: mptrac.h:3105
int met_cms_nd0y
cmultiscale number of cells of coarsest grid in y-direction.
Definition: mptrac.h:2939
int mixing_nz
Number of altitudes of mixing grid.
Definition: mptrac.h:3195
int qnt_o3c
Quantity array index for total column ozone.
Definition: mptrac.h:2660
double bound_p0
Boundary conditions bottom pressure [hPa].
Definition: mptrac.h:3120
double mixing_lon0
Lower longitude of mixing grid [deg].
Definition: mptrac.h:3207
char clim_ccl4_timeseries[LEN]
Filename of CFC-10 time series.
Definition: mptrac.h:3171
int qnt_Co3
Quantity array index for O3 volume mixing ratio (chemistry code).
Definition: mptrac.h:2774
int qnt_tsts
Quantity array index for T_STS.
Definition: mptrac.h:2762
int grid_nz
Number of altitudes of gridded data.
Definition: mptrac.h:3448
char clim_oh_filename[LEN]
Filename of OH climatology.
Definition: mptrac.h:3156
int qnt_nss
Quantity array index for northward turbulent surface stress.
Definition: mptrac.h:2567
double ens_dt_out
Time step for ensemble output [s].
Definition: mptrac.h:3421
char sample_basename[LEN]
Basename of sample data file.
Definition: mptrac.h:3511
int atm_stride
Particle index stride for atmospheric data files.
Definition: mptrac.h:3343
int met_relhum
Try to read relative humidity (0=no, 1=yes).
Definition: mptrac.h:2993
double mixing_lat1
Upper latitude of mixing grid [deg].
Definition: mptrac.h:3219
double atm_dt_out
Time step for atmospheric data output [s].
Definition: mptrac.h:3337
char clim_sf6_timeseries[LEN]
Filename of SF6 time series.
Definition: mptrac.h:3183
int met_lossy_scale[METVAR]
Apply levelwise [0,1] scaling before lossy compression (0=off, 1=on).
Definition: mptrac.h:2924
double prof_lat1
Upper latitude of gridded profile data [deg].
Definition: mptrac.h:3508
int qnt_current_subdomain
Quantity array index for current subdomain in domain decomposition.
Definition: mptrac.h:2834
int met_cms_batch
cmultiscale batch size.
Definition: mptrac.h:2930
double psc_h2o
H2O volume mixing ratio for PSC analysis.
Definition: mptrac.h:3312
int met_sp
Smoothing for pressure levels.
Definition: mptrac.h:2963
double prof_lon0
Lower longitude of gridded profile data [deg].
Definition: mptrac.h:3496
int qnt_Axe133
Quantity array index for radioactive activity of Xe-133.
Definition: mptrac.h:2831
int chemgrid_nx
Number of longitudes of chemistry grid.
Definition: mptrac.h:3231
int qnt_pct
Quantity array index for cloud top pressure.
Definition: mptrac.h:2636
int qnt_mloss_kpp
Quantity array index for total mass loss due to KPP chemistry.
Definition: mptrac.h:2684
int qnt_psat
Quantity array index for saturation pressure over water.
Definition: mptrac.h:2699
double met_lev_hybm[EP]
Meteo data model level b coefficients.
Definition: mptrac.h:2984
double prof_lat0
Lower latitude of gridded profile data [deg].
Definition: mptrac.h:3505
int qnt_cin
Quantity array index for convective inhibition (CIN).
Definition: mptrac.h:2657
double turb_pbl_trans
Depth of turbulent PBL transition layer (fraction of PBL pressure thickness).
Definition: mptrac.h:3084
double psc_hno3
HNO3 volume mixing ratio for PSC analysis.
Definition: mptrac.h:3315
double prof_lon1
Upper longitude of gridded profile data [deg].
Definition: mptrac.h:3499
int met_nc_quant
Number of digits for quantization of netCDF meteo files (0=off).
Definition: mptrac.h:2897
int h2o2_chem_reaction
Reaction type for H2O2 chemistry (0=none, 1=SO2).
Definition: mptrac.h:3258
int qnt_Co3p
Quantity array index for O(3P) volume mixing ratio (chemistry code).
Definition: mptrac.h:2795
int atm_nc_quant[NQ]
Number of digits for quantization of netCDF atmospheric data files (0=off).
Definition: mptrac.h:3358
double wet_depo_bc_ret_ratio
Coefficients for wet deposition below cloud: retention ratio.
Definition: mptrac.h:3303
int chemgrid_ny
Number of latitudes of chemistry grid.
Definition: mptrac.h:3240
int qnt_Abe7
Quantity array index for radioactive activity of Be-7.
Definition: mptrac.h:2822
char clim_ccl3f_timeseries[LEN]
Filename of CFC-11 time series.
Definition: mptrac.h:3174
int met_cms_zstd
cmultiscale ZSTD compression (0=off, 1=on).
Definition: mptrac.h:2933
int met_cms_maxlev
cmultiscale maximum refinement level.
Definition: mptrac.h:2942
int grid_sparse
Sparse output in grid data files (0=no, 1=yes).
Definition: mptrac.h:3436
double met_sz3_tol[METVAR]
SZ3 compression tolerance.
Definition: mptrac.h:2921
char vtk_basename[LEN]
Basename of VTK data files.
Definition: mptrac.h:3544
double dry_depo_vdep
Dry deposition velocity [m/s].
Definition: mptrac.h:3309
int qnt_tt
Quantity array index for tropopause temperature.
Definition: mptrac.h:2585
int met_np
Number of target pressure levels.
Definition: mptrac.h:2969
int qnt_ens
Quantity array index for ensemble IDs.
Definition: mptrac.h:2531
int met_nc_level
zlib compression level of netCDF meteo files (0=off).
Definition: mptrac.h:2894
double mixing_dt
Time interval for mixing [s].
Definition: mptrac.h:3186
double mixing_tau_strat
E-folding time for interparcel mixing in the stratosphere [s].
Definition: mptrac.h:3192
int qnt_Arn222
Quantity array index for radioactive activity of Rn-222.
Definition: mptrac.h:2816
int qnt_mloss_h2o2
Quantity array index for total mass loss due to H2O2 chemistry.
Definition: mptrac.h:2681
double vtk_scale
Vertical scaling factor for VTK data.
Definition: mptrac.h:3553
char clim_ccl2f2_timeseries[LEN]
Filename of CFC-12 time series.
Definition: mptrac.h:3177
double wet_depo_ic_h[2]
Coefficients for wet deposition in cloud (Henry's law: Hb, Cb).
Definition: mptrac.h:3291
double turb_dx_pbl
Horizontal turbulent diffusion coefficient (PBL) [m^2/s].
Definition: mptrac.h:3060
int dd_exchange_mode
Particle exchange: 0=sort/push (default), 1=direct pack/compact.
Definition: mptrac.h:3587
double conv_cin
CIN threshold for convection module [J/kg].
Definition: mptrac.h:3096
int qnt_pv
Quantity array index for potential vorticity.
Definition: mptrac.h:2753
int advect_vert_coord
Vertical velocity of air parcels (0=omega_on_plev, 1=zetadot_on_mlev, 2=omega_on_mlev,...
Definition: mptrac.h:3048
int qnt_mloss_oh
Quantity array index for total mass loss due to OH chemistry.
Definition: mptrac.h:2678
int qnt_Ch2o2
Quantity array index for H2O2 volume mixing ratio (chemistry code).
Definition: mptrac.h:2789
int qnt_sst
Quantity array index for sea surface temperature.
Definition: mptrac.h:2576
double mixing_lon1
Upper longitude of mixing grid [deg].
Definition: mptrac.h:3210
int atm_nc_level
zlib compression level of netCDF atmospheric data files (0=off).
Definition: mptrac.h:3355
char clim_hno3_filename[LEN]
Filename of HNO3 climatology.
Definition: mptrac.h:3153
double wet_depo_ic_ret_ratio
Coefficients for wet deposition in cloud: retention ratio.
Definition: mptrac.h:3300
int qnt_sh
Quantity array index for specific humidity.
Definition: mptrac.h:2708
int met_coord_type
Type of coordinates for meteo data (-1=detect, 0=lat/lon [deg], 1=UTM [m]).
Definition: mptrac.h:2865
int qnt_ess
Quantity array index for eastward turbulent surface stress.
Definition: mptrac.h:2564
double wet_depo_ic_b
Coefficient B for wet deposition in cloud (exponential form).
Definition: mptrac.h:3288
double wet_depo_bc_b
Coefficient B for wet deposition below cloud (exponential form).
Definition: mptrac.h:3282
int met_dy
Stride for latitudes.
Definition: mptrac.h:2951
int qnt_Cx
Quantity array index for trace species x volume mixing ratio (chemistry code).
Definition: mptrac.h:2768
double turb_dz_strat
Vertical turbulent diffusion coefficient (stratosphere) [m^2/s].
Definition: mptrac.h:3075
double bound_zetas
Boundary conditions surface layer zeta [K].
Definition: mptrac.h:3132
int radio_depo
RADIO_DEPO switch for radionuclide deposition (0=off, 1=on, default: 0).
Definition: mptrac.h:3273
int dd_subdomains_zonal
Domain decomposition zonal subdomain number (defaults to 2 if domain decomposition is used,...
Definition: mptrac.h:3573
int qnt_idx
Quantity array index for air parcel IDs.
Definition: mptrac.h:2528
double met_tropo_theta
Dynamical tropopause potential temperature threshold [K].
Definition: mptrac.h:3015
int qnt_rwc
Quantity array index for cloud rain water content.
Definition: mptrac.h:2624
double t_start
Start time of simulation [s].
Definition: mptrac.h:2843
char qnt_longname[NQ][LEN]
Quantity long names.
Definition: mptrac.h:2519
double met_p[EP]
Target pressure levels [hPa].
Definition: mptrac.h:2972
int nq
Number of quantities.
Definition: mptrac.h:2513
double tdec_trop
Life time of particles in the troposphere [s].
Definition: mptrac.h:3144
int met_zfp_prec[METVAR]
ZFP compression precision.
Definition: mptrac.h:2912
double sample_dx
Horizontal radius for sample output [km].
Definition: mptrac.h:3520
int vtk_stride
Particle index stride for VTK data.
Definition: mptrac.h:3550
char stat_basename[LEN]
Basename of station data file.
Definition: mptrac.h:3526
double turb_dz_pbl
Vertical turbulent diffusion coefficient (PBL) [m^2/s].
Definition: mptrac.h:3069
double grid_lat1
Upper latitude of gridded data [deg].
Definition: mptrac.h:3472
int dd_subdomains_meridional
Domain decomposition meridional subdomain number (defaults to 2 if domain decomposition is used,...
Definition: mptrac.h:3577
int qnt_zt
Quantity array index for tropopause geopotential height.
Definition: mptrac.h:2588
int met_type
Type of meteo data files (0=netCDF, 1=binary, 2=pck, 3=ZFP, 4=ZSTD, 5=cms, 6=grib,...
Definition: mptrac.h:2882
int qnt_iso_var
Quantity array index for isosurface reference (units depend on ISOSURF).
Definition: mptrac.h:2720
int qnt_cc
Quantity array index for cloud cover.
Definition: mptrac.h:2633
int qnt_plcl
Quantity array index for pressure at lifted condensation level (LCL).
Definition: mptrac.h:2645
double grid_dt_out
Time step for gridded data output [s].
Definition: mptrac.h:3433
int qnt_tdew
Quantity array index for dew point temperature.
Definition: mptrac.h:2756
Domain decomposition data structure.
Definition: mptrac.h:4061
size_t halo_bnd_count[4]
Extent of the periodic boundary halo hyperslab.
Definition: mptrac.h:4093
int halo_offset_end
Offset of the periodic halo block at the end of the local x-array.
Definition: mptrac.h:4099
int nx_glob
Number of global longitudes.
Definition: mptrac.h:4068
double sort_key[NP]
Auxiliary array for sorting.
Definition: mptrac.h:4111
size_t halo_bnd_start[4]
Start indices of the periodic boundary halo hyperslab.
Definition: mptrac.h:4090
double lon_glob[DD_EX_GLOB]
Longitudes of the global grid [deg].
Definition: mptrac.h:4074
double tmp[NP]
Auxiliary temporary array for sorting.
Definition: mptrac.h:4117
double lat_glob[DD_EY_GLOB]
Latitudes of the global grid [deg].
Definition: mptrac.h:4077
int halo_offset_start
Offset of the periodic halo block at the beginning of the local x-array.
Definition: mptrac.h:4096
MPI_Datatype MPI_Particle
MPI datatype for particle exchange.
Definition: mptrac.h:4108
size_t subdomain_count[4]
Extent of the local subdomain hyperslab (including inner halos).
Definition: mptrac.h:4087
int ny_glob
Number of global latitudes.
Definition: mptrac.h:4071
size_t subdomain_start[4]
Start indices of the local subdomain hyperslab (including inner halos).
Definition: mptrac.h:4084
int perm[NP]
Auxiliary permutation array for sorting.
Definition: mptrac.h:4114
Ground inventories of deposited radionuclides.
Definition: mptrac.h:3694
double Ai131[EX *EY]
Deposited I-131 activity [Bq].
Definition: mptrac.h:3706
double Abe7[EX *EY]
Deposited Be-7 activity [Bq].
Definition: mptrac.h:3700
double Acs137[EX *EY]
Deposited Cs-137 activity [Bq].
Definition: mptrac.h:3703
double Apb210[EX *EY]
Deposited Pb-210 activity [Bq].
Definition: mptrac.h:3697
Meteo data structure.
Definition: mptrac.h:3884
float zt[EX][EY]
Tropopause geopotential height [km].
Definition: mptrac.h:3965
float sst[EX][EY]
Sea surface temperature [K].
Definition: mptrac.h:3953
float rwc[EX][EY][EP]
Cloud rain water content [kg/kg].
Definition: mptrac.h:4025
float o3c[EX][EY]
Total column ozone [DU].
Definition: mptrac.h:3995
float zeta_dotl[EX][EY][EP]
Vertical velocity on model levels [K/s].
Definition: mptrac.h:4052
float h2o[EX][EY][EP]
Water vapor volume mixing ratio [1].
Definition: mptrac.h:4016
float cape[EX][EY]
Convective available potential energy [J/kg].
Definition: mptrac.h:3989
int coord_type
Definition: mptrac.h:3890
float w[EX][EY][EP]
Vertical velocity [hPa/s].
Definition: mptrac.h:4010
float pct[EX][EY]
Cloud top pressure [hPa].
Definition: mptrac.h:3971
double hybrid[EP]
Model hybrid levels.
Definition: mptrac.h:3914
int nx
Number of longitudes.
Definition: mptrac.h:3893
int ny
Number of latitudes.
Definition: mptrac.h:3896
float shf[EX][EY]
Surface sensible heat flux [W/m^2].
Definition: mptrac.h:3947
float ps[EX][EY]
Surface pressure [hPa].
Definition: mptrac.h:3926
float lwc[EX][EY][EP]
Cloud liquid water content [kg/kg].
Definition: mptrac.h:4022
float us[EX][EY]
Surface zonal wind [m/s].
Definition: mptrac.h:3935
float wl[EX][EY][EP]
Vertical velocity on model levels [hPa/s].
Definition: mptrac.h:4046
float vl[EX][EY][EP]
Meridional wind on model levels [m/s].
Definition: mptrac.h:4043
float zs[EX][EY]
Surface geopotential height [km].
Definition: mptrac.h:3932
float o3[EX][EY][EP]
Ozone volume mixing ratio [1].
Definition: mptrac.h:4019
float cc[EX][EY][EP]
Cloud cover [1].
Definition: mptrac.h:4034
int np
Number of pressure levels.
Definition: mptrac.h:3899
float t[EX][EY][EP]
Temperature [K].
Definition: mptrac.h:4001
float ts[EX][EY]
Surface temperature [K].
Definition: mptrac.h:3929
float u[EX][EY][EP]
Zonal wind [m/s].
Definition: mptrac.h:4004
float ess[EX][EY]
Eastward turbulent surface stress [N/m^2].
Definition: mptrac.h:3941
float ul[EX][EY][EP]
Zonal wind on model levels [m/s].
Definition: mptrac.h:4040
float pcb[EX][EY]
Cloud bottom pressure [hPa].
Definition: mptrac.h:3974
float pel[EX][EY]
Pressure at equilibrium level (EL) [hPa].
Definition: mptrac.h:3986
float cin[EX][EY]
Convective inhibition [J/kg].
Definition: mptrac.h:3992
float plcl[EX][EY]
Pressure at lifted condensation level (LCL) [hPa].
Definition: mptrac.h:3980
double lon[EX]
Longitudes [deg].
Definition: mptrac.h:3905
float pt[EX][EY]
Tropopause pressure [hPa].
Definition: mptrac.h:3959
float tt[EX][EY]
Tropopause temperature [K].
Definition: mptrac.h:3962
float pbl[EX][EY]
Boundary layer pressure [hPa].
Definition: mptrac.h:3956
float vs[EX][EY]
Surface meridional wind [m/s].
Definition: mptrac.h:3938
float z[EX][EY][EP]
Geopotential height [km].
Definition: mptrac.h:3998
float v[EX][EY][EP]
Meridional wind [m/s].
Definition: mptrac.h:4007
int npl
Number of model levels.
Definition: mptrac.h:3902
float lsm[EX][EY]
Land-sea mask [1].
Definition: mptrac.h:3950
float iwc[EX][EY][EP]
Cloud ice water content [kg/kg].
Definition: mptrac.h:4028
float h2ot[EX][EY]
Tropopause water vapor volume mixing ratio [ppv].
Definition: mptrac.h:3968
float pv[EX][EY][EP]
Potential vorticity [PVU].
Definition: mptrac.h:4013
double eta[EP]
Model level eta values.
Definition: mptrac.h:3923
double time
Time [s].
Definition: mptrac.h:3887
float cl[EX][EY]
Total column cloud water [kg/m^2].
Definition: mptrac.h:3977
float nss[EX][EY]
Northward turbulent surface stress [N/m^2].
Definition: mptrac.h:3944
float pl[EX][EY][EP]
Pressure on model levels [hPa].
Definition: mptrac.h:4037
float plfc[EX][EY]
Pressure at level of free convection (LFC) [hPa].
Definition: mptrac.h:3983
double hyam[EP]
Model level a coefficients [Pa].
Definition: mptrac.h:3917
double lat[EY]
Latitudes [deg].
Definition: mptrac.h:3908
float swc[EX][EY][EP]
Cloud snow water content [kg/kg].
Definition: mptrac.h:4031
double hybm[EP]
Model level b coefficients.
Definition: mptrac.h:3920
float zetal[EX][EY][EP]
Zeta on model levels [K].
Definition: mptrac.h:4049
double p[EP]
Pressure levels [hPa].
Definition: mptrac.h:3911
Particle data.
Definition: mptrac.h:3633
double p
Pressure [hPa].
Definition: mptrac.h:3639
double lat
Latitude [deg].
Definition: mptrac.h:3645
double time
Time [s].
Definition: mptrac.h:3636
float uvwp[3]
Temporally correlated turbulent velocity perturbations [m/s].
Definition: mptrac.h:3651
double lon
Longitude [deg].
Definition: mptrac.h:3642
double q[NQ]
Quantity data (for various, user-defined attributes).
Definition: mptrac.h:3648