MPTRAC
Loading...
Searching...
No Matches
mptrac.c File Reference

MPTRAC library definitions. More...

#include "mptrac.h"
#include <limits.h>

Go to the source code of this file.

Macros

#define CHUNK_SIZE   2147483647
 

Functions

void broadcast_large_data (void *data, size_t N)
 State variables of cuRAND random number generator.
 
void cart2geo (const double *x, double *z, double *lon, double *lat)
 Converts Cartesian coordinates to geographic coordinates.
 
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 correction based on solar zenith angle.
 
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.
 
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.
 
double clim_tropo (const clim_t *clim, const double t, const double lat)
 Calculates the tropopause pressure based on climatological data.
 
void clim_tropo_init (clim_t *clim)
 Initializes the tropopause data in the climatology structure.
 
double clim_ts (const clim_ts_t *ts, const double t)
 Interpolates a time series of climatological variables.
 
double clim_zm (const clim_zm_t *zm, const double t, const double lat, const double p)
 Interpolates monthly mean zonal mean climatological variables.
 
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.
 
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.
 
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].
 
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.
 
int compress_read_lossy_scale (FILE *in, const size_t nz, double **off, double **scl)
 Read optional lossyscaling metadata for a 3-D field.
 
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.
 
int compress_metvar_index (const char *varname)
 Maps a meteorological variable name to its internal MPTRAC variable index.
 
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.
 
double cos_sza (const double sec, const double lon, const double lat)
 Calculates the cosine of the solar zenith angle.
 
void day2doy (const int year, const int mon, const int day, int *doy)
 Get day of year from date.
 
void dd_assign_subdomains (const ctl_t *ctl, const dd_t *dd, atm_t *atm, const int init)
 Assign or update particle subdomain ownership.
 
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.
 
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.
 
int dd_calc_subdomain_from_coords (const ctl_t *ctl, const dd_t *dd, double lon, double lat)
 Determine MPI subdomain from particle coordinates.
 
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.
 
void dd_init (const ctl_t *ctl, dd_t *dd, atm_t *atm)
 Initialize the domain decomposition infrastructure.
 
void dd_normalize_lon_lat (const dd_t *dd, double *lon, double *lat)
 Normalize geographic coordinates to the global grid convention.
 
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.
 
void sort_array (double *array, const int *perm, double *tmp, const int np)
 Reorder a double array according to particle source indices.
 
void sort_uvwp (cache_t *cache, const int *perm, double *tmp, const int np)
 Reorder particle-specific wind perturbations.
 
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.
 
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.
 
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.
 
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).
 
void fft_help (double *fcReal, double *fcImag, const int n)
 Computes the Fast Fourier Transform (FFT) of a complex sequence.
 
void geo2cart (const double z, const double lon, const double lat, double *x)
 Converts geographic coordinates (longitude, latitude, altitude) to Cartesian coordinates.
 
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.
 
void get_met_replace (char *orig, const char *search, const char *repl)
 Replaces occurrences of a substring in a string with another substring.
 
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.
 
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.
 
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.
 
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.
 
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.
 
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.
 
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.
 
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.
 
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).
 
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.
 
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.
 
double lapse_rate (const double t, const double h2o)
 Calculates the moist adiabatic lapse rate in Kelvin per kilometer.
 
void level_definitions (ctl_t *ctl)
 Defines pressure levels for meteorological data.
 
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.
 
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.
 
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.
 
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.
 
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.
 
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.
 
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.
 
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 data and assigns them back to the parcels.
 
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.
 
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.
 
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.
 
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.
 
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.
 
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).
 
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.
 
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.
 
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.
 
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.
 
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.
 
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.
 
void module_mixing (const ctl_t *ctl, const cache_t *cache, const clim_t *clim, atm_t *atm, const double t)
 Update atmospheric properties through interparcel mixing.
 
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.
 
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.
 
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.
 
void module_radio_decay (const ctl_t *ctl, const cache_t *cache, atm_t *atm)
 Apply radioactive decay to atmospheric tracer species.
 
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.
 
void module_rng_init (const int ntask)
 Initialize random number generators for parallel tasks.
 
void module_rng (const ctl_t *ctl, double *rs, const size_t n, const int method)
 Generate random numbers using various methods and distributions.
 
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.
 
void module_sort (const ctl_t *ctl, const met_t *met0, cache_t *cache, atm_t *atm)
 Sort particles according to box index.
 
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.
 
void module_timesteps_init (ctl_t *ctl, const atm_t *atm)
 Initialize start time and time interval for time-stepping.
 
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.
 
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.
 
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.
 
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.
 
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.
 
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.
 
int mptrac_read_atm (const char *filename, const ctl_t *ctl, atm_t *atm)
 Reads air parcel data from a specified file into the given atmospheric structure.
 
void mptrac_read_clim (const ctl_t *ctl, clim_t *clim)
 Reads various climatological data and populates the given climatology structure.
 
void mptrac_read_ctl (const char *filename, int argc, char *argv[], ctl_t *ctl)
 Reads control parameters from a configuration file and populates the given structure.
 
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.
 
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.
 
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.
 
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.
 
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.
 
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.
 
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.
 
double nat_temperature (const double p, const double h2o, const double hno3)
 Calculates the nitric acid trihydrate (NAT) temperature.
 
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.
 
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.
 
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.
 
int read_atm_clams (const char *filename, const ctl_t *ctl, atm_t *atm)
 Reads atmospheric data from a CLAMS NetCDF file.
 
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.
 
void read_clim_photo (const char *filename, clim_photo_t *photo)
 Reads photolysis rates from a NetCDF file and populates the given photolysis structure.
 
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.
 
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.
 
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.
 
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.
 
int read_met_bin (const char *filename, const ctl_t *ctl, met_t *met)
 Reads meteorological data from a binary file.
 
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.
 
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.
 
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.
 
void read_met_cloud (met_t *met)
 Calculates cloud-related variables for each grid point.
 
void read_met_detrend (const ctl_t *ctl, met_t *met)
 Detrends meteorological data.
 
void read_met_extrapolate (met_t *met)
 Extrapolates meteorological data.
 
void read_met_geopot (const ctl_t *ctl, met_t *met)
 Calculates geopotential heights from meteorological data.
 
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.
 
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.
 
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.
 
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.
 
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.
 
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.
 
void read_met_monotonize (const ctl_t *ctl, met_t *met)
 Makes zeta and pressure profiles monotone.
 
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.
 
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.
 
void read_met_pbl (const ctl_t *ctl, met_t *met)
 Computes the planetary boundary layer (PBL) pressure based on meteorological data.
 
void read_met_periodic (met_t *met)
 Applies periodic boundary conditions to meteorological data along longitudinal axis.
 
void read_met_polar_winds (met_t *met)
 Applies a fix for polar winds in meteorological data.
 
void read_met_pv (met_t *met)
 Calculates potential vorticity (PV) from meteorological data.
 
void read_met_ozone (met_t *met)
 Calculates the total column ozone from meteorological ozone data.
 
void read_met_sample (const ctl_t *ctl, met_t *met)
 Downsamples meteorological data based on specified parameters.
 
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 the results in the meteorological data structure.
 
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.
 
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.
 
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.
 
double scan_ctl (const char *filename, int argc, char *argv[], const char *varname, const int arridx, const char *defvalue, char *value)
 Scans a control file or command-line arguments for a specified variable.
 
double sedi (const double p, const double T, const double rp, const double rhop)
 Calculates the sedimentation velocity of a particle in air.
 
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.
 
float stddev (const float *data, const int n)
 Calculates the standard deviation of a set of data.
 
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.
 
void timer (const char *name, const char *group, const int output)
 Measures and reports elapsed time for named and grouped timers.
 
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.
 
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.
 
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.
 
void write_atm_bin (const char *filename, const ctl_t *ctl, const atm_t *atm)
 Writes air parcel data to a binary file.
 
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.
 
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.
 
void write_atm_nc (const char *filename, const ctl_t *ctl, const atm_t *atm)
 Writes air parcel data to a NetCDF file.
 
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.
 
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.
 
void write_ens (const char *filename, const ctl_t *ctl, const atm_t *atm, const double t)
 Writes ensemble data to a file.
 
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.
 
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.
 
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.
 
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.
 
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.
 
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.
 
void write_met_bin (const char *filename, const ctl_t *ctl, met_t *met)
 Writes meteorological data in binary format to a specified file.
 
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.
 
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.
 
void write_met_nc (const char *filename, const ctl_t *ctl, met_t *met)
 Writes meteorological data to a NetCDF file.
 
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.
 
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.
 
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.
 
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.
 
void write_station (const char *filename, const ctl_t *ctl, atm_t *atm, const double t)
 Writes station data to a specified file.
 
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.
 

Detailed Description

MPTRAC library definitions.

Definition in file mptrac.c.

Macro Definition Documentation

◆ CHUNK_SIZE

#define CHUNK_SIZE   2147483647

Function Documentation

◆ broadcast_large_data()

void broadcast_large_data ( void * data,
size_t N )

State variables of cuRAND random number generator.

Broadcasts large data across all processes in an MPI communicator.

Definition at line 47 of file mptrac.c.

49 {
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}
#define CHUNK_SIZE

◆ cart2geo()

void cart2geo ( const double * x,
double * z,
double * lon,
double * lat )

Converts Cartesian coordinates to geographic coordinates.

This function converts a point from Cartesian coordinates (x, y, z) to geographic coordinates (longitude, latitude, and altitude). It uses the spherical Earth approximation for the conversion.

Parameters
xPointer to an array containing the Cartesian coordinates (x, y, z) in kilometers.
zPointer to a double where the computed altitude (above the reference ellipsoid) will be stored, in kilometers.
lonPointer to a double where the computed longitude (in degrees) will be stored.
latPointer to a double where the computed latitude (in degrees) will be stored.
Author
Lars Hoffmann

Definition at line 76 of file mptrac.c.

80 {
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}
#define RE
Mean radius of Earth [km].
Definition mptrac.h:315
#define DOTP(a, b)
Calculate the dot product of two vectors.
Definition mptrac.h:1038
#define RAD2DEG(rad)
Converts radians to degrees.
Definition mptrac.h:1896

◆ clim_oh()

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 correction based on solar zenith angle.

This function retrieves OH data from a given climatology and applies a diurnal correction if the correction factor (oh_chem_beta) is greater than zero. The diurnal correction accounts for the variation in OH concentration due to changes in the solar zenith angle.

Parameters
ctlPointer to the control structure containing configuration parameters.
climPointer to the climatology structure containing OH data.
tTime at which the OH concentration is to be calculated.
lonLongitude at which the OH concentration is to be calculated.
latLatitude at which the OH concentration is to be calculated.
pPressure level at which the OH concentration is to be calculated.
Returns
The OH concentration at the specified time, location, and pressure, possibly adjusted by a diurnal correction.
Author
Lars Hoffmann
Mingzhao Liu

Definition at line 91 of file mptrac.c.

97 {
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}
double cos_sza(const double sec, const double lon, const double lat)
Calculates the cosine of the solar zenith angle.
Definition mptrac.c:1859
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
#define DEG2RAD(deg)
Converts degrees to radians.
Definition mptrac.h:859
clim_zm_t oh
OH zonal means.
Definition mptrac.h:3861
double met_utm_ref_lon
Reference longitude [deg] for UTM grid.
Definition mptrac.h:2879
double met_utm_ref_lat
Reference latitude [deg] for UTM grid.
Definition mptrac.h:2876
double oh_chem_beta
Beta parameter for diurnal variability of OH.
Definition mptrac.h:3263
int met_coord_type
Type of coordinates for meteo data (-1=detect, 0=lat/lon [deg], 1=UTM [m]).
Definition mptrac.h:2873
Here is the call graph for this function:

◆ clim_oh_diurnal_correction()

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.

This function iterates over the climatology data points for OH concentration and integrates the day/night correction factor over longitude. The correction factor is based on the solar zenith angle, and it adjusts the OH data to account for diurnal variations. The corrected OH data is scaled accordingly.

Parameters
ctlPointer to the control structure containing configuration parameters, including the correction factor (oh_chem_beta).
climPointer to the climatology structure containing OH data that will be corrected.
Author
Lars Hoffmann
Mingzhao Liu

Definition at line 126 of file mptrac.c.

128 {
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}
double time[CT]
Time [s].
Definition mptrac.h:3817
int np
Number of pressure levels.
Definition mptrac.h:3814
double vmr[CT][CP][CY]
Volume mixing ratio [ppv].
Definition mptrac.h:3826
int ntime
Number of timesteps.
Definition mptrac.h:3808
int nlat
Number of latitudes.
Definition mptrac.h:3811
double lat[CY]
Latitude [deg].
Definition mptrac.h:3820
Here is the call graph for this function:

◆ clim_photo()

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.

This function computes the photolysis rate based on provided climatology data and input parameters such as pressure, solar zenith angle (SZA), and ozone column. It ensures that the input parameters are within the valid range of the climatology data and interpolates the photolysis rate accordingly.

Parameters
rate3D array containing the photolysis rates for different combinations of pressure, SZA, and ozone column.
photoPointer to the climatology data structure containing arrays of valid pressure levels, SZAs, and ozone columns.
pPressure at which the photolysis rate is to be calculated.
szaSolar zenith angle at which the photolysis rate is to be calculated.
o3cOzone column at which the photolysis rate is to be calculated.
Returns
The interpolated photolysis rate for the specified conditions. If the calculated rate is negative, it returns 0.0.

This function performs the following steps:

  1. Checks and adjusts the input parameters (pressure, SZA, and ozone column) to ensure they are within the valid range.
  2. Determines the appropriate indices in the climatology data for interpolation.
  3. Performs trilinear interpolation to calculate the photolysis rate based on the input parameters.
Author
Lars Hoffmann
Mingzhao Liu

Definition at line 158 of file mptrac.c.

163 {
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}
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:3865
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:3801
#define LIN(x0, y0, x1, y1, x)
Linear interpolation.
Definition mptrac.h:1360
#define MAX(a, b)
Macro to determine the maximum of two values.
Definition mptrac.h:1387
int nsza
Number of solar zenith angles.
Definition mptrac.h:3735
double sza[CSZA]
Solar zenith angle [rad].
Definition mptrac.h:3744
double p[CP]
Pressure [hPa].
Definition mptrac.h:3741
double o3c[CO3]
Total column ozone [DU].
Definition mptrac.h:3747
int np
Number of pressure levels.
Definition mptrac.h:3732
int no3c
Number of total ozone columns.
Definition mptrac.h:3738
Here is the call graph for this function:

◆ clim_tropo()

double clim_tropo ( const clim_t * clim,
const double t,
const double lat )

Calculates the tropopause pressure based on climatological data.

This function computes the tropopause pressure using climatological data for different times and latitudes. It interpolates the tropopause pressure based on the input time and latitude parameters.

Parameters
climPointer to the climatology structure containing tropopause pressure data.
tTime for which the tropopause pressure is to be calculated, in seconds since the beginning of the year.
latLatitude at which the tropopause pressure is to be calculated.
Returns
The interpolated tropopause pressure for the specified time and latitude.

This function performs the following steps:

  1. Calculates the number of seconds since the beginning of the year.
  2. Determines the appropriate indices in the climatology data for interpolation based on time and latitude.
  3. Interpolates the tropopause pressure using linear interpolation based on latitude and time.
Author
Lars Hoffmann

Definition at line 215 of file mptrac.c.

218 {
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}
#define FMOD(x, y)
Calculate the floating-point remainder of dividing x by y.
Definition mptrac.h:1123
int tropo_ntime
Number of tropopause timesteps.
Definition mptrac.h:3840
double tropo_lat[73]
Tropopause latitudes [deg].
Definition mptrac.h:3849
int tropo_nlat
Number of tropopause latitudes.
Definition mptrac.h:3843
double tropo[12][73]
Tropopause pressure values [hPa].
Definition mptrac.h:3852
double tropo_time[12]
Tropopause time steps [s].
Definition mptrac.h:3846
Here is the call graph for this function:

◆ clim_tropo_init()

void clim_tropo_init ( clim_t * clim)

Initializes the tropopause data in the climatology structure.

This function initializes the tropopause data in the climatology structure. It sets the time steps, latitudes, and tropopause pressure values based on predefined arrays.

Parameters
climPointer to the climatology structure to be initialized.

This function performs the following steps:

  1. Sets the number of time steps and initializes the time array.
  2. Sets the number of latitudes and initializes the latitude array.
  3. Initializes the tropopause pressure values based on predefined arrays.
  4. Computes the range of tropopause pressure values.
  5. Logs information about the initialization process.
Author
Lars Hoffmann

Definition at line 243 of file mptrac.c.

244 {
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}
#define MIN(a, b)
Macro to determine the minimum of two values.
Definition mptrac.h:1488
#define Z(p)
Convert pressure to altitude.
Definition mptrac.h:2263
#define LOG(level,...)
Print a log message with a specified logging level.
Definition mptrac.h:2356

◆ clim_ts()

double clim_ts ( const clim_ts_t * ts,
const double t )

Interpolates a time series of climatological variables.

This function interpolates a time series of climatological variables based on the input time and the provided data points.

Parameters
tsPointer to the time series structure containing data points.
tTime at which to interpolate the climatological variable (in seconds).
Returns
Interpolated value of the climatological variable at the given time.

This function performs linear interpolation between the closest data points to the input time t. If t is outside the range of the provided time series, the value at the nearest boundary is returned.

Author
Lars Hoffmann

Definition at line 398 of file mptrac.c.

400 {
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}
double vmr[CTS]
Volume mixing ratio [ppv].
Definition mptrac.h:3794
double time[CTS]
Time [s].
Definition mptrac.h:3791
int ntime
Number of timesteps.
Definition mptrac.h:3788
Here is the call graph for this function:

◆ clim_zm()

double clim_zm ( const clim_zm_t * zm,
const double t,
const double lat,
const double p )

Interpolates monthly mean zonal mean climatological variables.

This function interpolates climatological variables based on pressure, latitude, and time. The climatological data is provided in the form of monthly mean zonal mean values.

Parameters
zmPointer to the climatological zonal mean structure containing data points.
tTime at which to interpolate the climatological variable (in seconds since the beginning of the year).
latLatitude at which to interpolate the climatological variable (in degrees).
pPressure at which to interpolate the climatological variable (in hPa).
Returns
Interpolated value of the climatological variable at the given pressure, latitude, and time.

This function performs trilinear interpolation between the nearest data points to the input time t, latitude lat, and pressure or altitude p. If the input values are outside the range of the provided data, the function extrapolates by using the nearest boundary values.

Author
Lars Hoffmann

Definition at line 416 of file mptrac.c.

420 {
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}
double p[CP]
Pressure [hPa].
Definition mptrac.h:3823
Here is the call graph for this function:

◆ compress_log_level()

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.

This helper computes the error statistics for one level (from org and cmp) and writes one ASCII table row to out. If rho is NAN, the correlation coefficient is derived from org and cmp using GSL.

Parameters
[in,out]outOutput stream.
[in]codecCompression codec name.
[in]varnameVariable name.
[in]levLevel index.
[in]plevPressure level in hPa.
[in]ratioCompression ratio.
[in]bpvBits per value.
[in]t_compCompression time in seconds.
[in]t_decompDecompression time in seconds.
[in]nNumber of data values in the level slice.
[in]nbytesByte count used for throughput reporting.
[in]orgOriginal level data.
[in]cmpReconstructed level data.
Author
Lars Hoffmann

Definition at line 668 of file mptrac.c.

681 {
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}
#define LEN
Maximum length of ASCII data lines.
Definition mptrac.h:559
#define COMPRESS_SPEED(nbytes, dt)
Calculate compression throughput in MiB/s.
Definition mptrac.h:773

◆ compress_log_levels_3d()

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.

This helper extracts each vertical level from the original and reconstructed 3-D fields and forwards the diagnostics to compress_log_level().

Parameters
[in,out]outOutput stream, or NULL to disable logging.
[in]codecCompression codec name.
[in]varnameVariable name.
[in]metMeteorological meta-data with pressure levels.
[in]org_allOriginal 3-D field in horizontal-major order.
[in]cmp_allReconstructed 3-D field in horizontal-major order.
[in]nxyNumber of horizontal points per level.
[in]nzNumber of vertical levels.
[in]ratioCompression ratio.
[in]bpvBits per value.
[in]t_compCompression time in seconds.
[in]t_decompDecompression time in seconds.
[in]nbytesByte count used for throughput reporting.
Author
Lars Hoffmann

Definition at line 767 of file mptrac.c.

780 {
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}
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 ALLOC(ptr, type, n)
Allocate memory for a pointer with error handling.
Definition mptrac.h:668
double p[EP]
Pressure levels [hPa].
Definition mptrac.h:3923
Here is the call graph for this function:

◆ compress_scale_to_unit()

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].

The routine computes per-level offsets and scales from the minimum and maximum values of each vertical slice and transforms the field in place. Constant or nearly constant levels are mapped to zero and can be reconstructed exactly with the stored offsets.

Parameters
[in,out]arrayField values stored in horizontal-major order.
[in]nxyNumber of horizontal points per level.
[in]nzNumber of vertical levels.
[out]offPer-level offsets (minimum values).
[out]sclPer-level scales (ranges).
Author
Lars Hoffmann

Definition at line 813 of file mptrac.c.

818 {
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}

◆ compress_unscale_from_unit()

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.

This routine applies the inverse transformation of compress_scale_to_unit() using the supplied per-level offsets and scales. Constant levels are restored directly from their offsets.

Parameters
[in,out]arrayField values stored in horizontal-major order.
[in]nxyNumber of horizontal points per level.
[in]nzNumber of vertical levels.
[in]offPer-level offsets (minimum values).
[in]sclPer-level scales (ranges).
Author
Lars Hoffmann

Definition at line 856 of file mptrac.c.

861 {
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}

◆ compress_read_lossy_scale()

int compress_read_lossy_scale ( FILE * in,
const size_t nz,
double ** off,
double ** scl )

Read optional lossyscaling metadata for a 3-D field.

This helper reads the stored MET_LOSSY_SCALE flag and, if enabled, allocates and fills the per-level offset and scale arrays used by compress_unscale_from_unit(). The caller owns the returned arrays and must free them.

Parameters
[in,out]inInput stream.
[in]nzNumber of vertical levels.
[out]offReturned per-level offsets, or NULL if disabled.
[out]sclReturned per-level scales, or NULL if disabled.
Returns
Stored lossyscaling flag (0 or 1).
Author
Lars Hoffmann

Definition at line 876 of file mptrac.c.

880 {
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}
#define ERRMSG(...)
Print an error message with contextual information and terminate the program.
Definition mptrac.h:2426
#define FREAD(ptr, type, size, in)
Read data from a file stream and store it in memory.
Definition mptrac.h:1148

◆ compress_write_lossy_scale()

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.

This helper writes the MET_LOSSY_SCALE flag and, if enabled, allocates the per-level offset and scale arrays, scales the field in place to [0,1], and writes the resulting metadata to out. The caller owns the returned arrays and must free them.

Parameters
[in,out]outOutput stream.
[in]enabledLossyscaling flag (0 or 1).
[in,out]arrayField values stored in horizontal-major order.
[in]nxyNumber of horizontal points per level.
[in]nzNumber of vertical levels.
[out]offReturned per-level offsets, or NULL if disabled.
[out]sclReturned per-level scales, or NULL if disabled.
Author
Lars Hoffmann

Definition at line 911 of file mptrac.c.

918 {
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}
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
#define FWRITE(ptr, type, size, out)
Write data from memory to a file stream.
Definition mptrac.h:1168
Here is the call graph for this function:

◆ compress_metvar_index()

int compress_metvar_index ( const char * varname)

Maps a meteorological variable name to its internal MPTRAC variable index.

The returned index can be used to access variable-specific control arrays such as MET_LOSSY_SCALE, MET_ZFP_PREC, and MET_SZ3_PREC.

Parameters
varnameMeteorological variable name as stored in the meteorological file.
Returns
Internal MPTRAC meteorological variable index.
Author
Lars Hoffmann

Definition at line 971 of file mptrac.c.

972 {
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}

◆ compress_pck()

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.

The routine works on the regular longitude/latitude grid described by the supplied met structure (nx, ny, np and pressure levels). In compress mode the data are scaled to unsigned short values; in decompress mode the values are reconstructed.

Parameters
[in]ctlControl structure with compression parameters.
[in]metMeteorological meta‑data providing grid dimensions and pressure levels.
[in]varnameVariable name for logging and error‑bound selection.
[in,out]arrayFloat array to compress or decompress.
[in]decompressNon‑zero to decompress; zero to compress.
[in,out]level_logOptional per‑level diagnostics stream, or NULL.
[in,out]inoutFile stream for reading/writing the PCK payload.
Author
Lars Hoffmann

Definition at line 988 of file mptrac.c.

995 {
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}
#define COMPRESS_BPV(n, stored_size)
Calculate bits per value from stored size and element count.
Definition mptrac.h:797
#define COMPRESS_RATIO(raw_size, stored_size)
Calculate the compression ratio from raw and stored byte counts.
Definition mptrac.h:785
#define EP
Maximum number of pressure levels for meteo data.
Definition mptrac.h:544
int met_zstd_nworkers
ZSTD number of worker threads (0=single-threaded, default=4).
Definition mptrac.h:2911
int met_zstd_level
ZSTD compression level (from -5 to 22, default=-3).
Definition mptrac.h:2908
int met_pck_zstd
Apply an additional ZSTD compression step to PCK payloads (0=off, 1=on).
Definition mptrac.h:2917
int nx
Number of longitudes.
Definition mptrac.h:3905
int ny
Number of latitudes.
Definition mptrac.h:3908
int np
Number of pressure levels.
Definition mptrac.h:3911
Here is the call graph for this function:

◆ cos_sza()

double cos_sza ( const double sec,
const double lon,
const double lat )

Calculates the cosine of the solar zenith angle.

This function computes the cosine of the solar zenith angle (SZA), which describes the angle between the local zenith (straight up) and the line connecting the observer to the center of the Sun. The cosine of the SZA is often used directly in radiative transfer and photochemical calculations to avoid unnecessary use of trigonometric inverse functions.

Parameters
secSeconds elapsed since 2000-01-01T12:00Z.
lonObserver's longitude in degrees.
latObserver's latitude in degrees.
Returns
The cosine of the solar zenith angle (dimensionless, range [-1, 1]).

The cosine of the solar zenith angle is computed based on the observer's position (longitude and latitude) and the specified time in seconds elapsed since 2000-01-01T12:00Z.

Note
The input longitude and latitude must be specified in degrees.
See also
acos() — can be used to convert the returned value to the solar zenith angle in radians if needed.
Author
Lars Hoffmann

Definition at line 1859 of file mptrac.c.

1862 {
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}
#define SQR(x)
Compute the square of a value.
Definition mptrac.h:2057

◆ day2doy()

void day2doy ( const int year,
const int mon,
const int day,
int * doy )

Get day of year from date.

Converts a given date to the day of the year (DOY).

This function computes the day of the year (DOY) for a given date specified by the year, month, and day. It takes into account whether the given year is a leap year or not.

Parameters
yearThe year of the date.
monThe month of the date (1-12).
dayThe day of the month (1-31).
doyPointer to an integer where the computed day of the year will be stored.

The function uses two arrays, d0 and d0l, which contain the cumulative number of days at the start of each month for non-leap years and leap years respectively. It checks if the year is a leap year and calculates the day of the year accordingly.

Note
The function assumes that the input date is valid.
Author
Lars Hoffmann

Definition at line 1900 of file mptrac.c.

1904 {
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}

◆ dd_assign_subdomains()

void dd_assign_subdomains ( const ctl_t * ctl,
const dd_t * dd,
atm_t * atm,
const int init )

Assign or update particle subdomain ownership.

This routine determines whether each atmospheric particle belongs to the local MPI subdomain and updates the domain-decomposition bookkeeping quantities stored in the atmospheric state.

The particle longitude and latitude are first normalized using dd_normalize_lon_lat() to ensure consistency with the longitude convention and pole handling of the global meteorological grid stored in the domain decomposition structure.

The particle position is then mapped to global grid indices using locate_reg() for longitude and locate_irr() for latitude. These indices are compared with the index range of the local subdomain owned by the current MPI rank.

If init is nonzero, the routine performs the initial ownership assignment. Particles inside the local subdomain are assigned to the current MPI rank by setting both ctl->qnt_current_subdomain and ctl->qnt_target_subdomain to that rank. Particles outside the local subdomain are marked invalid by setting both quantities to -1.

If init is zero, the routine updates only the destination rank. Particles already marked invalid (qnt_current_subdomain == -1) are skipped. For particles that remain inside the local subdomain, ctl->qnt_target_subdomain is set to the current MPI rank. For particles that have left the local subdomain, the destination rank is recomputed with dd_calc_subdomain_from_coords().

Parameters
[in]ctlControl structure containing the indices of the domain-decomposition quantities qnt_current_subdomain and qnt_target_subdomain.
[in]ddDomain decomposition structure containing the global grid and subdomain layout information.
[in,out]atmAtmospheric state. Particle positions are read from this structure and the subdomain and/or destination quantities are updated in place.
[in]initInitialization flag. If nonzero, perform the initial subdomain assignment; otherwise only update destination ranks for particle exchange.
Note
  • The local MPI rank is obtained internally from MPI_COMM_WORLD.
  • During non-initial updates, particles already marked invalid are ignored.
  • When compiled with _OPENACC, the particle loop may execute on an accelerator.
Author
Jan Clemens
Lars Hoffmann

Definition at line 1927 of file mptrac.c.

1931 {
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}
int rank
Definition mptrac.c:10799
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:2167
void dd_normalize_lon_lat(const dd_t *dd, double *lon, double *lat)
Normalize geographic coordinates to the global grid convention.
Definition mptrac.c:2417
#define SELECT_TIMER(id, group)
Select and start a timer with specific attributes.
Definition mptrac.h:2500
double lat[NP]
Latitude [deg].
Definition mptrac.h:3627
double lon[NP]
Longitude [deg].
Definition mptrac.h:3624
int np
Number of air parcels.
Definition mptrac.h:3615
double q[NQ][NP]
Quantity data (for various, user-defined attributes).
Definition mptrac.h:3630
int qnt_target_subdomain
Quantity array index for destination subdomain in domain decomposition.
Definition mptrac.h:2845
int qnt_current_subdomain
Quantity array index for current subdomain in domain decomposition.
Definition mptrac.h:2842
int dd_subdomains_zonal
Domain decomposition zonal subdomain number (defaults to 2 if domain decomposition is used,...
Definition mptrac.h:3581
int dd_subdomains_meridional
Domain decomposition meridional subdomain number (defaults to 2 if domain decomposition is used,...
Definition mptrac.h:3585
int nx_glob
Number of global longitudes.
Definition mptrac.h:4080
double lon_glob[DD_EX_GLOB]
Longitudes of the global grid [deg].
Definition mptrac.h:4086
double lat_glob[DD_EY_GLOB]
Latitudes of the global grid [deg].
Definition mptrac.h:4089
int ny_glob
Number of global latitudes.
Definition mptrac.h:4083
Here is the call graph for this function:

◆ dd_atm2particles()

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.

This routine scans the domain-decomposition particle region of the atmospheric state and copies particles that must be transferred to a different MPI rank into the temporary particle communication buffer.

The routine processes atmospheric entries in the index range [atm->np, atm->np + npart). A particle is selected for export if all of the following conditions are met:

  • atm->q[ctl->qnt_target_subdomain][ip] != rank,
  • atm->q[ctl->qnt_target_subdomain][ip] >= 0,
  • atm->q[ctl->qnt_current_subdomain][ip] >= 0.

For each selected entry, the particle coordinates (time, lon, lat, p), all quantities q[iq], cache->dt[ip], and cache->uvwp[ip] are copied into particles[ip - atm->np].

After copying, the atmospheric entry is marked as inactive for local ownership by setting atm->q[ctl->qnt_current_subdomain][ip] = -1, and the cached time step cache->dt[ip] is reset to zero.

Parameters
[in]ctlPointer to the control structure containing the quantity indices qnt_target_subdomain, qnt_current_subdomain, and the number of quantities nq.
[in,out]cachePointer to the cache structure. The cached time-step values dt[ip] of exported particles are reset to zero.
[in,out]atmPointer to the atmospheric state. Particle data are read from the domain-decomposition particle region, and exported entries are marked by setting their subdomain index to -1.
[out]particlesBuffer receiving the particles selected for communication. Selected atmospheric entries are copied to index ip - atm->np.
[in]npartNumber of candidate particle entries in the atmospheric communication region.
Note
  • The current MPI rank is obtained internally from MPI_COMM_WORLD.
  • This routine does not compact the particle buffer; entries corresponding to non-selected atmospheric particles remain untouched.
  • When compiled with _OPENACC, the selection and copy loop may run on the accelerator.
Author
Jan Clemens
Lars Hoffmann

Definition at line 2000 of file mptrac.c.

2005 {
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 particles[ip - atm->np].dt = cache->dt[ip];
2033 for (int iq = 0; iq < ctl->nq; iq++)
2034 particles[ip - atm->np].q[iq] = atm->q[iq][ip];
2035 for (int i = 0; i < 3; i++)
2036 particles[ip - atm->np].uvwp[i] = cache->uvwp[ip][i];
2037
2038 atm->q[ctl->qnt_current_subdomain][ip] = -1;
2039 cache->dt[ip] = 0;
2040 }
2041#ifdef _OPENACC
2042#pragma acc update host( particles[:npart])
2043#pragma acc exit data delete(npart, particles)
2044#endif
2045}
double time[NP]
Time [s].
Definition mptrac.h:3618
double p[NP]
Pressure [hPa].
Definition mptrac.h:3621
double dt[NP]
Timesteps [s].
Definition mptrac.h:3691
float uvwp[NP][3]
Wind perturbations: horizontal [m/s], mesoscale vertical [hPa/s].
Definition mptrac.h:3685
int nq
Number of quantities.
Definition mptrac.h:2521
double p
Pressure [hPa].
Definition mptrac.h:3648
double lat
Latitude [deg].
Definition mptrac.h:3654
double time
Time [s].
Definition mptrac.h:3645
double lon
Longitude [deg].
Definition mptrac.h:3651
double dt
Individual time step [s].
Definition mptrac.h:3657

◆ dd_pack_compact()

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.

This routine writes particles assigned to another MPI rank into particles, removes those particles and invalid entries from the active atmosphere, and compacts the particles retained on the current rank into the first nkeep slots. It moves time, p, lon, lat, all quantities q[iq], and the particle-specific cache values dt and uvwp together. Random values in rs are regenerated before their next use. The shared iso_ps and iso_ts cache series are not changed.

The caller must first call dd_count_outgoing() and provide space for npart outgoing particles. With OpenACC, that call also places source indices in dd->perm. This routine uses those indices to pack particles on the device before compacting the local arrays, then copies only the outgoing particle buffer to the host for MPI communication. On the CPU, packing and compaction proceed in one sequential pass.

Parameters
[in]ctlControl structure containing the subdomain quantity indices and the number of quantities.
[in,out]cacheParticle-specific cache values for retained particles are compacted with the atmosphere.
[in,out]atmAtmospheric state. On return, atm->np equals the number of retained particles.
[in]ddSource-index mapping produced by dd_count_outgoing() in OpenACC builds.
[out]particlesBuffer for npart outgoing particles, allocated by the caller.
[in]nkeepNumber of particles retained on this rank.
[in]npartNumber of particles sent to other ranks.
Note
dt and uvwp are packed for outgoing particles and preserved for retained particles. Atomic index assignment in OpenACC builds may change particle order relative to the CPU implementation.
Author
Lars Hoffmann

Definition at line 2051 of file mptrac.c.

2058 {
2059
2060 /* Set timer... */
2061 SELECT_TIMER("DD_PACK_COMPACT", "DD");
2062
2063 int rank;
2064 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
2065
2066 const int np = atm->np;
2067#ifdef _OPENACC
2068 /* The permutation was built by dd_count_outgoing(): kept particles at
2069 the front and outgoing particles at the back. Pack before compacting,
2070 since the latter overwrites source slots. */
2071 if (npart > 0) {
2072 SELECT_TIMER("DD_GPU_PACK", "DD");
2073#pragma acc enter data create(particles[0:npart])
2074#pragma acc parallel loop present(ctl, atm, cache, dd, particles)
2075 for (int i = 0; i < npart; i++) {
2076 const int ip = dd->perm[np - 1 - i];
2077 particles[i].time = atm->time[ip];
2078 particles[i].p = atm->p[ip];
2079 particles[i].lon = atm->lon[ip];
2080 particles[i].lat = atm->lat[ip];
2081 particles[i].dt = cache->dt[ip];
2082 for (int iq = 0; iq < ctl->nq; iq++)
2083 particles[i].q[iq] = atm->q[iq][ip];
2084 for (int j = 0; j < 3; j++)
2085 particles[i].uvwp[j] = cache->uvwp[ip][j];
2086 }
2087 SELECT_TIMER("DD_GPU_SEND_D2H", "DD");
2088#pragma acc update self(particles[0:npart])
2089#pragma acc exit data delete(particles[0:npart])
2090 }
2091
2092 /* sort_array stages one field in dd->tmp before writing it back.
2093 Thus no parallel read can race with a write to the original field. */
2094 if (nkeep > 0 && nkeep != np) {
2095 SELECT_TIMER("DD_GPU_COMPACT", "DD");
2096 sort_array(atm->time, dd->perm, dd->tmp, nkeep);
2097 sort_array(atm->p, dd->perm, dd->tmp, nkeep);
2098 sort_array(atm->lon, dd->perm, dd->tmp, nkeep);
2099 sort_array(atm->lat, dd->perm, dd->tmp, nkeep);
2100 for (int iq = 0; iq < ctl->nq; iq++)
2101 sort_array(atm->q[iq], dd->perm, dd->tmp, nkeep);
2102 sort_array(cache->dt, dd->perm, dd->tmp, nkeep);
2103 sort_uvwp(cache, dd->perm, dd->tmp, nkeep);
2104 }
2105
2106 const int nlost = np - nkeep - npart;
2107 if (nlost > 0)
2108 WARN("Rank %d: %d particles have invalid subdomain and will be lost",
2109 rank, nlost);
2110 atm->np = nkeep;
2111#pragma acc update device(atm->np)
2112#else
2113 (void) dd;
2114 (void) nkeep;
2115
2116 /* Pack outgoing particles and fill gaps with the remaining local ones... */
2117 int nkeep_cpu = 0, nsend = 0, nlost = 0;
2118 for (int ip = 0; ip < np; ip++) {
2119 const int current = (int) atm->q[ctl->qnt_current_subdomain][ip];
2120 const int target = (int) atm->q[ctl->qnt_target_subdomain][ip];
2121 if (current < 0 || target < 0) {
2122 nlost++;
2123 continue;
2124 }
2125
2126 if (target != rank) {
2127 particles[nsend].time = atm->time[ip];
2128 particles[nsend].p = atm->p[ip];
2129 particles[nsend].lon = atm->lon[ip];
2130 particles[nsend].lat = atm->lat[ip];
2131 particles[nsend].dt = cache->dt[ip];
2132 for (int iq = 0; iq < ctl->nq; iq++)
2133 particles[nsend].q[iq] = atm->q[iq][ip];
2134 for (int i = 0; i < 3; i++)
2135 particles[nsend].uvwp[i] = cache->uvwp[ip][i];
2136 nsend++;
2137 continue;
2138 }
2139
2140 if (nkeep_cpu != ip) {
2141 atm->time[nkeep_cpu] = atm->time[ip];
2142 atm->p[nkeep_cpu] = atm->p[ip];
2143 atm->lon[nkeep_cpu] = atm->lon[ip];
2144 atm->lat[nkeep_cpu] = atm->lat[ip];
2145 for (int iq = 0; iq < ctl->nq; iq++)
2146 atm->q[iq][nkeep_cpu] = atm->q[iq][ip];
2147 cache->dt[nkeep_cpu] = cache->dt[ip];
2148 for (int i = 0; i < 3; i++)
2149 cache->uvwp[nkeep_cpu][i] = cache->uvwp[ip][i];
2150 }
2151 nkeep_cpu++;
2152 }
2153
2154 if (nsend != npart)
2155 ERRMSG("Inconsistent number of outgoing particles!");
2156 if (nlost > 0)
2157 WARN("Rank %d: %d particles have invalid subdomain and will be lost",
2158 rank, nlost);
2159 atm->np = nkeep_cpu;
2160#endif
2161}
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:2499
void sort_uvwp(cache_t *cache, const int *perm, double *tmp, const int np)
Reorder particle-specific wind perturbations.
Definition mptrac.c:2537
#define WARN(...)
Print a warning message with contextual information.
Definition mptrac.h:2393
double tmp[NP]
Auxiliary temporary array for sorting.
Definition mptrac.h:4129
int perm[NP]
Auxiliary permutation array for sorting.
Definition mptrac.h:4126
Here is the call graph for this function:

◆ dd_calc_subdomain_from_coords()

int dd_calc_subdomain_from_coords ( const ctl_t * ctl,
const dd_t * dd,
const double lon,
const double lat )

Determine MPI subdomain from particle coordinates.

This function maps a particle position to the corresponding MPI rank in the domain-decomposed grid. The longitude and latitude are first normalized using dd_normalize_lon_lat(). The corresponding global grid cell indices are then determined using locate_reg() for longitude and locate_irr() for latitude.

The grid indices are mapped to the zonal and meridional subdomain blocks defined by ctl->dd_subdomains_zonal and ctl->dd_subdomains_meridional. The resulting subdomain indices are converted to an MPI rank assuming the layout:

rank = zonal_rank * dd_subdomains_meridional + merid_rank

This implementation works for both regular and irregular latitude grids.

Parameters
ctlPointer to control structure.
ddPointer to domain decomposition structure.
lonLongitude of particle [deg].
latLatitude of particle [deg].
Returns
MPI rank of the target subdomain.
Author
Jan Clemens
Lars Hoffmann

Definition at line 2167 of file mptrac.c.

2171 {
2172
2173 /* Normalize coordinates... */
2174 dd_normalize_lon_lat(dd, &lon, &lat);
2175
2176 /* Get grid index... */
2177 const int ix = locate_reg(dd->lon_glob, dd->nx_glob, lon);
2178 const int iy = locate_irr(dd->lat_glob, dd->ny_glob, lat);
2179
2180 /* Get zonal subdomain index... */
2181 const int nx_block = dd->nx_glob / ctl->dd_subdomains_zonal;
2182 int zonal_rank = ix / nx_block;
2183 if (zonal_rank >= ctl->dd_subdomains_zonal)
2184 zonal_rank = ctl->dd_subdomains_zonal - 1;
2185
2186 /* Get meridional subdomain index... */
2187 const int ny_block = dd->ny_glob / ctl->dd_subdomains_meridional;
2188 int merid_rank = iy / ny_block;
2189 if (merid_rank >= ctl->dd_subdomains_meridional)
2190 merid_rank = ctl->dd_subdomains_meridional - 1;
2191
2192 /* Return rank... */
2193 return zonal_rank * ctl->dd_subdomains_meridional + merid_rank;
2194}
Here is the call graph for this function:

◆ dd_communicate_particles()

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.

This routine migrates particles to their assigned destination MPI ranks using collective communication. The destination rank of each particle is read from (*particles)[ip].q[ctl->qnt_target_subdomain].

First, the routine counts how many local particles must be sent to each MPI rank, excluding particles whose destination equals the current rank. These counts are exchanged using MPI_Alltoall to determine the number of incoming particles. Temporary send and receive buffers are then allocated, the outgoing particles are packed into the send buffer, and particle data are exchanged with MPI_Alltoallv using the MPI datatype stored in dd->MPI_Particle.

After communication, the received particles replace the leading entries of the particle buffer. For each received particle, both ctl->qnt_target_subdomain and ctl->qnt_current_subdomain are reset to the current MPI rank.

If the receive buffer exceeds the currently allocated particle capacity, the particle array is automatically resized using realloc().

Parameters
[in]ctlPointer to the control structure containing the quantity indices qnt_target_subdomain and qnt_current_subdomain.
[in]ddPointer to the domain decomposition structure containing the MPI datatype MPI_Particle used for particle communication.
[in,out]particlesPointer to the particle buffer. The pointer may be updated if the buffer is resized. On output, the leading entries contain the particles received by the current MPI rank.
[in,out]npartNumber of local particles. On input, it specifies the number of valid particles stored in particles. On output, it is replaced by the number of particles received by the current MPI rank.
[in,out]capacityAllocated capacity of the particle buffer. If necessary, the routine increases this value when resizing the particle array.
Note
  • Particles whose destination rank equals the current MPI rank are not packed into the send buffer.
  • Destination ranks are validated against the number of MPI tasks and must refer to a valid MPI rank.
  • Temporary communication buffers are allocated internally and freed before returning.
Author
Jan Clemens
Lars Hoffmann

Definition at line 2200 of file mptrac.c.

2205 {
2206
2207 /* Set timer... */
2208 SELECT_TIMER("DD_COMMUNICATE_PARTICLES", "DD");
2209
2210 int rank, size;
2211 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
2212 MPI_Comm_size(MPI_COMM_WORLD, &size);
2213
2214 int *send_counts, *recv_counts;
2215 int *send_displs, *recv_displs;
2216 int *offsets;
2217
2218 particle_t *sendbuf = NULL;
2219 particle_t *recvbuf = NULL;
2220
2221 ALLOC(send_counts, int,
2222 size);
2223 ALLOC(recv_counts, int,
2224 size);
2225 ALLOC(send_displs, int,
2226 size);
2227 ALLOC(recv_displs, int,
2228 size);
2229 ALLOC(offsets, int,
2230 size);
2231
2232 /* Count particles per destination rank... */
2233#pragma omp parallel for
2234 for (int ip = 0; ip < *npart; ip++) {
2235 const int dest = (int) (*particles)[ip].q[ctl->qnt_target_subdomain];
2236 if (dest == rank)
2237 continue;
2238 if (dest < 0 || dest >= size)
2239 ERRMSG("Invalid destination rank!");
2240#pragma omp atomic
2241 send_counts[dest]++;
2242 }
2243
2244 /* Compute send displacements... */
2245 int nsend = 0;
2246 for (int i = 0; i < size; i++) {
2247 send_displs[i] = nsend;
2248 nsend += send_counts[i];
2249 }
2250#if LOGLEV >= 3
2251 LOG(3, "DD_SEND rank=%d particles=%d", rank, nsend);
2252#endif
2253
2254 /* Classify the already available MPI counts without scanning particles. */
2255#if LOGLEV >= 2
2256 if (dd_node != NULL) {
2257 dd_sent += (unsigned long long) nsend;
2258 for (int i = 0; i < size; i++) {
2259 if (dd_node[i] == dd_node[rank])
2260 dd_intra += (unsigned long long) send_counts[i];
2261 else
2262 dd_inter += (unsigned long long) send_counts[i];
2263 }
2264 }
2265#endif
2266
2267 /* Exchange particle counts... */
2268 MPI_Alltoall(send_counts, 1, MPI_INT,
2269 recv_counts, 1, MPI_INT, MPI_COMM_WORLD);
2270
2271 /* Compute receive displacements... */
2272 int nrecv = 0;
2273 for (int i = 0; i < size; i++) {
2274 recv_displs[i] = nrecv;
2275 nrecv += recv_counts[i];
2276 }
2277 if (nsend > 0)
2278 ALLOC(sendbuf, particle_t, nsend);
2279 if (nrecv > 0)
2280 ALLOC(recvbuf, particle_t, nrecv);
2281 for (int i = 0; i < size; i++)
2282 offsets[i] = send_displs[i];
2283
2284 /* Pack particles into send buffer... */
2285 for (int ip = 0; ip < *npart; ip++) {
2286 const int dest = (int) (*particles)[ip].q[ctl->qnt_target_subdomain];
2287 if (dest == rank)
2288 continue;
2289 memcpy(&sendbuf[offsets[dest]], &(*particles)[ip], sizeof(particle_t));
2290 offsets[dest]++;
2291 }
2292
2293 /* Exchange particle data... */
2294 MPI_Alltoallv(sendbuf,
2295 send_counts,
2296 send_displs,
2297 dd->MPI_Particle,
2298 recvbuf,
2299 recv_counts, recv_displs, dd->MPI_Particle, MPI_COMM_WORLD);
2300
2301 /* Resize particle buffer if necessary... */
2302 if (nrecv > *capacity) {
2303 const int newcap = nrecv + nrecv / 2 + 1;
2304 particle_t *tmp =
2305 realloc(*particles, (size_t) newcap * sizeof(particle_t));
2306 if (!tmp)
2307 ERRMSG("Out of memory!");
2308 *particles = tmp;
2309 *capacity = newcap;
2310 }
2311
2312 /* Copy received particles... */
2313#pragma omp parallel for
2314 for (int ip = 0; ip < nrecv; ip++) {
2315 (*particles)[ip] = recvbuf[ip];
2316 (*particles)[ip].q[ctl->qnt_target_subdomain] = rank;
2317 (*particles)[ip].q[ctl->qnt_current_subdomain] = rank;
2318 }
2319 *npart = nrecv;
2320
2321 /* Free temporary buffers... */
2322 if (sendbuf)
2323 free(sendbuf);
2324 if (recvbuf)
2325 free(recvbuf);
2326 free(send_counts);
2327 free(recv_counts);
2328 free(send_displs);
2329 free(recv_displs);
2330 free(offsets);
2331}
int size
Definition mptrac.c:10799
MPI_Datatype MPI_Particle
MPI datatype for particle exchange.
Definition mptrac.h:4120
Particle data.
Definition mptrac.h:3642
double q[NQ]
Quantity data (for various, user-defined attributes).
Definition mptrac.h:3660

◆ dd_init()

void dd_init ( const ctl_t * ctl,
dd_t * dd,
atm_t * atm )

Initialize the domain decomposition infrastructure.

This routine initializes the MPI-related data structures required for domain decomposition. It first verifies that the number of MPI ranks in MPI_COMM_WORLD matches the total number of requested subdomains,

ctl->dd_subdomains_meridional * ctl->dd_subdomains_zonal.

It then constructs and commits the MPI datatype dd->MPI_Particle corresponding to the particle_t structure. Its extent is set to sizeof(particle_t) so arrays use the correct stride in MPI exchanges.

Finally, the routine performs the initial particle-to-subdomain assignment by calling dd_assign_subdomains() with init = 1.

Parameters
[in]ctlPointer to the control structure containing the domain-decomposition settings, in particular the number of zonal and meridional subdomains.
[in,out]ddPointer to the domain decomposition structure. On return, the MPI datatype MPI_Particle is initialized and committed.
[in,out]atmPointer to the atmospheric state. Its domain-decomposition bookkeeping quantities are initialized by the call to dd_assign_subdomains().
Note
  • The routine aborts if the number of MPI ranks does not equal the total number of configured subdomains.
  • The committed MPI datatype stored in dd->MPI_Particle must be released later with MPI_Type_free() when the domain decomposition is finalized.
Author
Jan Clemens
Lars Hoffmann

Definition at line 2337 of file mptrac.c.

2340 {
2341
2342 /* Check configuration... */
2343 if ((ctl->dd) &&
2345 ERRMSG("Please provide zonal and meridional subdomain numbers!");
2346 if (!ctl->dd)
2347 ERRMSG("Activate control flag DD!");
2348
2349 /* Check if enough tasks are requested... */
2350 int size;
2351 MPI_Comm_size(MPI_COMM_WORLD, &size);
2353 ERRMSG("Number of tasks and subdomains is not identical!");
2354
2355 /* Register the MPI_Particle data type... */
2356 const MPI_Datatype types[7] =
2357 { MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE,
2358 MPI_FLOAT };
2359 const int blocklengths[7] = { 1, 1, 1, 1, 1, NQ, 3 };
2360 const MPI_Aint displacements[7] =
2361 { offsetof(particle_t, time), offsetof(particle_t, p),
2362 offsetof(particle_t, lon), offsetof(particle_t, lat),
2363 offsetof(particle_t, dt), offsetof(particle_t, q),
2364 offsetof(particle_t, uvwp)
2365 };
2366 MPI_Datatype fields;
2367 MPI_Type_create_struct(7, blocklengths, displacements, types, &fields);
2368 /* The trailing float array can leave padding in particle_t. Alltoallv
2369 counts and displacements must advance by the full C structure size. */
2370 MPI_Type_create_resized(fields, 0, (MPI_Aint) sizeof(particle_t),
2371 &dd->MPI_Particle);
2372 MPI_Type_free(&fields);
2373 MPI_Type_commit(&dd->MPI_Particle);
2374 MPI_Aint lower_bound, extent;
2375 MPI_Type_get_extent(dd->MPI_Particle, &lower_bound, &extent);
2376 if (lower_bound != 0 || extent != (MPI_Aint) sizeof(particle_t))
2377 ERRMSG("Invalid MPI particle datatype extent!");
2378
2379 LOG(1, "DD: %d x %d subdomains | exchange mode %d | initial particles %d",
2381 ctl->dd_exchange_mode, atm->np);
2382
2383 /* Discover node placement once when detailed logging is enabled. */
2384#if LOGLEV >= 2
2385 {
2386 int rank, local_rank, leader;
2387 MPI_Comm shared;
2388 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
2389 MPI_Comm_split_type(MPI_COMM_WORLD, MPI_COMM_TYPE_SHARED, rank,
2390 MPI_INFO_NULL, &shared);
2391 MPI_Comm_rank(shared, &local_rank);
2392 leader = local_rank == 0 ? rank : -1;
2393 MPI_Bcast(&leader, 1, MPI_INT, 0, shared);
2394 ALLOC(dd_node, int, size);
2395 MPI_Allgather(&leader, 1, MPI_INT, dd_node, 1, MPI_INT,
2396 MPI_COMM_WORLD);
2397 MPI_Type_size(dd->MPI_Particle, &dd_particle_bytes);
2398 dd_sent = dd_intra = dd_inter = 0;
2399 char hostname[MPI_MAX_PROCESSOR_NAME];
2400 int hostname_len;
2401 MPI_Get_processor_name(hostname, &hostname_len);
2402 const char *gpu = getenv("CUDA_VISIBLE_DEVICES");
2403 LOG(2, "DD_NODE rank=%d leader=%d local_rank=%d host=%s gpu=%s",
2404 rank, leader, local_rank, hostname, gpu ? gpu : "-");
2405 MPI_Comm_free(&shared);
2406 }
2407#endif
2408
2409 /* Check if particles are in subdomain... */
2410 dd_assign_subdomains(ctl, dd, atm, 1);
2411}
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
#define NQ
Maximum number of quantities per data point.
Definition mptrac.h:574
int dd
Domain decomposition (0=no, 1=yes).
Definition mptrac.h:3577
int dd_exchange_mode
Particle exchange: 0=sort/push (default), 1=direct pack/compact.
Definition mptrac.h:3595
Here is the call graph for this function:

◆ dd_normalize_lon_lat()

void dd_normalize_lon_lat ( const dd_t * dd,
double * lon,
double * lat )

Normalize geographic coordinates to the global grid convention.

This helper routine normalizes a longitude/latitude pair so that it is consistent with the longitude convention of the global meteorological grid stored in the domain decomposition structure.

The function performs the following steps:

  • Normalizes longitude to the grid convention used by dd->lon_glob (either [-180,180) or [0,360)).
  • Wraps latitude values outside the physical range [-90,90] across the poles and shifts longitude by 180° accordingly.
  • Renormalizes longitude again after pole crossing.

This ensures that coordinates used for domain-decomposition checks and subdomain index calculations are always mapped consistently to the global grid representation.

Parameters
[in]ddPointer to the domain decomposition structure containing the global longitude grid (dd->lon_glob).
[in,out]lonPointer to longitude [deg]. The value is normalized in-place to match the grid convention.
[in,out]latPointer to latitude [deg]. Values outside [-90,90] are wrapped across the poles in-place.
Note
  • Uses the FMOD macro instead of fmod() to ensure compatibility with GPU/OpenACC builds.
  • The routine is marked with #pragma acc routine seq so it can be called from within OpenACC parallel loops.
Author
Lars Hoffmann

Definition at line 2417 of file mptrac.c.

2420 {
2421
2422 const double lon_min = (dd->lon_glob[0] < 0) ? -180.0 : 0.0;
2423
2424 /* Normalize longitude to match global grid convention... */
2425 *lon = FMOD(*lon - lon_min, 360.0);
2426 if (*lon < 0.0)
2427 *lon += 360.0;
2428 *lon += lon_min;
2429
2430 /* Wrap latitude across the poles and shift longitude... */
2431 if (*lat > 90.0) {
2432 *lat = 180.0 - *lat;
2433 *lon += 180.0;
2434 } else if (*lat < -90.0) {
2435 *lat = -180.0 - *lat;
2436 *lon += 180.0;
2437 }
2438
2439 /* Renormalize longitude after pole crossing... */
2440 *lon = FMOD(*lon - lon_min, 360.0);
2441 if (*lon < 0.0)
2442 *lon += 360.0;
2443 *lon += lon_min;
2444}

◆ dd_particles2atm()

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.

This routine appends particles from the temporary domain-decomposition particle buffer to the atmospheric particle arrays. The particles are written into the index range

[atm->np, atm->np + npart)

of the atmospheric state.

For each transferred particle, the routine copies the particle coordinates (time, lon, lat, p) and all particle quantities q[iq] into the atmospheric arrays and restores the individual time step and all three uvwp components into the cache.

After all particles have been copied, the total number of atmospheric particles atm->np is increased by npart.

Parameters
[in]ctlPointer to the control structure containing the number of quantities nq.
[in,out]cachePointer to the cache structure. The cached time-step values and uvwp are restored from particles.
[in]particlesBuffer containing the particles to be appended to the atmospheric state.
[in]npartNumber of valid particles stored in particles.
[in,out]atmPointer to the atmospheric state. The particle data are appended to its arrays, and atm->np is incremented accordingly.
Note
  • The routine appends particles starting at the current value of atm->np.
  • The routine aborts if the updated number of atmospheric particles exceeds the compile-time limit NP.
  • When compiled with _OPENACC, particle data may be copied on the accelerator and the updated value of atm->np is synchronized to the device.
Author
Jan Clemens
Lars Hoffmann

Definition at line 2450 of file mptrac.c.

2455 {
2456
2457 /* Set timer... */
2458 SELECT_TIMER("DD_PARTICLES2ATM", "DD");
2459
2460 /* Check if particles are present... */
2461 if (npart == 0)
2462 return;
2463
2464 /* Check number of particles... */
2465 if (atm->np + npart > NP)
2466 ERRMSG("Too many particles. Increase NP!");
2467
2468#ifdef _OPENACC
2469#pragma acc enter data create(npart, particles[:npart])
2470#pragma acc update device(particles[:npart], npart)
2471#pragma acc data present(atm, ctl, cache, particles, npart)
2472#pragma acc parallel loop
2473#endif
2474 for (int ip = atm->np; ip < atm->np + npart; ip++) {
2475 atm->time[ip] = particles[ip - atm->np].time;
2476 atm->lon[ip] = particles[ip - atm->np].lon;
2477 atm->lat[ip] = particles[ip - atm->np].lat;
2478 atm->p[ip] = particles[ip - atm->np].p;
2479 cache->dt[ip] = particles[ip - atm->np].dt;
2480 for (int iq = 0; iq < ctl->nq; iq++)
2481 atm->q[iq][ip] = particles[ip - atm->np].q[iq];
2482 for (int i = 0; i < 3; i++)
2483 cache->uvwp[ip][i] = particles[ip - atm->np].uvwp[i];
2484 }
2485#ifdef _OPENACC
2486#pragma acc exit data delete(npart, particles)
2487#endif
2488
2489 /* Reset size... */
2490 atm->np += npart;
2491#ifdef _OPENACC
2492#pragma acc update device(atm->np)
2493#endif
2494}
#define NP
Maximum number of atmospheric data points.
Definition mptrac.h:569

◆ sort_array()

void sort_array ( double * array,
const int * perm,
double * tmp,
const int np )

Reorder a double array according to particle source indices.

The destination element at index i receives the original value at perm[i]. Values are first gathered into tmp and then copied back, so parallel writes cannot overwrite source values still being read. The caller provides both the permutation and a distinct temporary buffer; this function does not allocate memory.

Parameters
[in,out]arrayData array to reorder. It must contain every source index referenced by perm.
[in]permSource index for each of the np destination values.
[out]tmpTemporary buffer with room for at least np doubles.
[in]npNumber of destination values to reorder.
Note
In OpenACC builds, the arrays must already be present on the device. The two copy passes run on the device; CPU builds use OpenMP.
Author
Lars Hoffmann

Definition at line 2499 of file mptrac.c.

2503 {
2504
2505 if (np == 0)
2506 return;
2507
2508 /* The caller keeps all three buffers resident on the device. */
2509#ifdef _OPENACC
2510#pragma acc data present(array[0:np],perm[0:np],tmp[0:np])
2511#endif
2512 {
2513
2514 /* perm maps each destination index to its original source index. Gather
2515 every source value before any destination can overwrite it. */
2516#ifdef _OPENACC
2517#pragma acc parallel loop independent gang vector
2518#else
2519#pragma omp parallel for default(shared)
2520#endif
2521 for (int ip = 0; ip < np; ip++)
2522 tmp[ip] = array[perm[ip]];
2523
2524 /* The staging pass is complete, so writes to array are independent. */
2525#ifdef _OPENACC
2526#pragma acc parallel loop independent gang vector
2527#else
2528#pragma omp parallel for default(shared)
2529#endif
2530 for (int ip = 0; ip < np; ip++)
2531 array[ip] = tmp[ip];
2532 }
2533}

◆ sort_uvwp()

void sort_uvwp ( cache_t * cache,
const int * perm,
double * tmp,
const int np )

Reorder particle-specific wind perturbations.

Applies the same source-index mapping as sort_array() to all three components of cache->uvwp. Each float component is staged in the caller-provided double buffer before being written back. Other cache fields, including shared isosurface data, are not modified.

Parameters
[in,out]cacheCache containing the wind perturbations to reorder.
[in]permSource index for each of the np destination particles.
[out]tmpTemporary buffer with room for at least np doubles.
[in]npNumber of destination particles to reorder.
Note
In OpenACC builds, cache, perm, and tmp must already be present on the device. CPU builds use OpenMP.
Author
Lars Hoffmann

Definition at line 2537 of file mptrac.c.

2541 {
2542
2543 if (np == 0)
2544 return;
2545
2546 /* Reuse the caller's device-resident permutation and staging buffer. */
2547#ifdef _OPENACC
2548#pragma acc data present(cache,perm[0:np],tmp[0:np])
2549#endif
2550 {
2551
2552 /* The three float components share one double staging buffer. Finish
2553 both passes for a component before reusing the buffer for the next. */
2554 for (int j = 0; j < 3; j++) {
2555 /* Gather first so in-place writes cannot race with source reads. */
2556#ifdef _OPENACC
2557#pragma acc parallel loop
2558#else
2559#pragma omp parallel for
2560#endif
2561 for (int ip = 0; ip < np; ip++)
2562 tmp[ip] = cache->uvwp[perm[ip]][j];
2563
2564 /* Every float value is represented exactly while staged as double. */
2565#ifdef _OPENACC
2566#pragma acc parallel loop
2567#else
2568#pragma omp parallel for
2569#endif
2570 for (int ip = 0; ip < np; ip++)
2571 cache->uvwp[ip][j] = (float) tmp[ip];
2572 }
2573 }
2574}

◆ dd_push()

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.

This routine scans all local atmospheric particles and moves those that need to be transferred to other MPI ranks to a contiguous region at the end of the particle arrays (starting at index atm->np).

A particle is selected for transfer if:

  • atm->q[ctl->qnt_current_subdomain][ip] != -1 (particle is active), AND
  • atm->q[ctl->qnt_target_subdomain][ip] != rank (destination is not current rank)

For each selected particle:

  • All particle data (time, p, lon, lat, all quantities q[iq]) are copied to the end of the respective arrays
  • The original particle is marked as invalid by setting both q[ctl->qnt_target_subdomain][ip] = -1 and q[ctl->qnt_current_subdomain][ip] = -1
  • The cached time step cache->dt[ip] is copied to the new location and reset to zero at the original position
  • All three cache->uvwp[ip] components are copied to the staged entry
Parameters
[in]ctlPointer to the control structure containing the quantity indices qnt_target_subdomain and qnt_current_subdomain.
[in,out]atmPointer to the atmospheric state. Selected particles are copied to indices [atm->np, atm->np+npart) and original entries are invalidated.
[in,out]cachePointer to the cache structure. Time-step values and velocity perturbations follow staged particles; original time-step values are reset to zero.
[out]npartNumber of particles staged for transfer (i.e., the number of valid particles in the range [atm->np, atm->np + npart)).
Note
  • The current MPI rank is obtained internally from MPI_COMM_WORLD.
  • This routine performs a compaction operation: valid local particles remain in [0, atm->np), non-local particles are moved to [atm->np, atm->np + npart).
  • When compiled with _OPENACC, the selection and copy operations execute on the accelerator using atomic operations for the counter.
  • The routine reports an error if the active particles and staged copies exceed the compile-time capacity NP; no out-of-bounds copy is made.
Author
Jan Clemens
Lars Hoffmann

Definition at line 2579 of file mptrac.c.

2583 {
2584 SELECT_TIMER("DD_PUSH", "DD");
2585 int rank;
2586 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
2587
2588 int counter = 0;
2589
2590#ifdef _OPENACC
2591 int *counter_ptr = (int *) acc_malloc(sizeof(int));
2592 if (counter_ptr == NULL) {
2593 ERRMSG("Failed to allocate device counter memory!");
2594 }
2595 acc_memcpy_to_device(counter_ptr, &counter, sizeof(int));
2596#else
2597 int *counter_ptr = &counter;
2598#endif
2599
2600 /* Push non local particles behind the end of the atm... */
2601#ifdef _OPENACC
2602#pragma acc parallel loop present(atm, cache, ctl) deviceptr(counter_ptr)
2603#else
2604#pragma omp parallel for
2605#endif
2606 for (int ip = 0; ip < atm->np; ip++) {
2607 if ((int) atm->q[ctl->qnt_current_subdomain][ip] != -1 &&
2608 (int) atm->q[ctl->qnt_target_subdomain][ip] != rank) {
2609 int local_idx;
2610#ifdef _OPENACC
2611#pragma acc atomic capture
2612#else
2613#pragma omp atomic capture
2614#endif
2615 {
2616 local_idx = *counter_ptr;
2617 (*counter_ptr)++;
2618 }
2619 int global_index = atm->np + local_idx;
2620 if (global_index >= NP)
2621 continue;
2622 /* Copy time, pressure, longitude, and latitude... */
2623 atm->time[global_index] = atm->time[ip];
2624 atm->p[global_index] = atm->p[ip];
2625 atm->lon[global_index] = atm->lon[ip];
2626 atm->lat[global_index] = atm->lat[ip];
2627 /* Copy all quantity data (q array)... */
2628 for (int iq = 0; iq < ctl->nq; iq++) {
2629 atm->q[iq][global_index] = atm->q[iq][ip];
2630 }
2631 /* Mark the original parcel as processed... */
2632 atm->q[ctl->qnt_target_subdomain][ip] = -1;
2633 atm->q[ctl->qnt_current_subdomain][ip] = -1;
2634 /* Reset cache->dt for the shifted particle... */
2635 cache->dt[global_index] = cache->dt[ip];
2636 cache->dt[ip] = 0;
2637 for (int i = 0; i < 3; i++)
2638 cache->uvwp[global_index][i] = cache->uvwp[ip][i];
2639 }
2640 }
2641
2642#ifdef _OPENACC
2643 acc_memcpy_from_device(npart, counter_ptr, sizeof(int));
2644 acc_free(counter_ptr);
2645#else
2646 *npart = counter;
2647#endif
2648
2649 if (atm->np + *npart > NP)
2650 ERRMSG("Too many particles during DD push. Increase NP!");
2651}

◆ dd_sort()

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.

This routine reorders the local atmospheric particle arrays so that particles remaining on the current MPI rank are placed first, followed by particles that must be sent to other ranks, and finally particles already marked invalid with subdomain index -1.

For each particle a sorting key is constructed:

  • particles with a valid subdomain and destination equal to the current MPI rank receive a key derived from their meteorological grid-box index,
  • particles with a valid subdomain but destination on another rank receive a key larger than any valid grid-box key,
  • particles with subdomain index -1 receive the largest keys and are moved to the end of the arrays.

The resulting permutation is applied to all particle arrays (time, p, lon, lat, and all quantities q[iq]) and their persistent particle-specific cache values (dt and uvwp). Random values in rs are regenerated before use; shared cache data remain unchanged.

After sorting, the routine

  • counts the number of particles that remain on the current rank,
  • counts the number of particles that must be exported to other ranks,
  • stores the export count in npart,
  • reduces atm->np to the number of particles kept locally.

Particles with subdomain index -1 are removed from the active particle set. A warning is issued if such particles are encountered.

Parameters
[in]ctlControl structure containing the quantity indices qnt_current_subdomain, qnt_target_subdomain, and the number of quantities nq.
[in]met0Meteorological grid used to compute sorting keys from particle longitude, latitude, and pressure.
[in,out]cacheParticle-specific cache values to reorder with the atmospheric state.
[in,out]atmAtmospheric state. Particle arrays are reordered in place and atm->np is reduced to the number of particles retained locally.
[in,out]ddDomain decomposition structure providing the temporary arrays sort_key and perm used during sorting.
[out]npartNumber of particles that must be sent to other MPI ranks.
Note
  • The current MPI rank is obtained internally from MPI_COMM_WORLD.
  • When compiled with _OPENACC, key generation, counting, and selected data movement may execute on the accelerator.
  • When compiled with THRUST, sorting may use the GPU-based thrustSortWrapper; otherwise a GSL-based index sort is used.
Author
Jan Clemens
Lars Hoffmann

Definition at line 2657 of file mptrac.c.

2663 {
2664
2665 /* Set timer... */
2666 SELECT_TIMER("DD_SORT", "DD");
2667
2668 int rank;
2669 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
2670
2671 /* Allocate... */
2672 const int np = atm->np;
2673 double amax = (met0->nx * met0->ny + met0->ny) * met0->np + met0->np;
2674#ifdef _OPENACC
2675#pragma acc enter data create(amax)
2676#pragma acc update device(amax)
2677#pragma acc data present(ctl,met0,atm,dd,amax)
2678#endif
2679
2680 /* Get box index... */
2681#ifdef _OPENACC
2682#pragma acc parallel loop independent gang vector
2683#else
2684#pragma omp parallel for default(shared)
2685#endif
2686 for (int ip = 0; ip < np; ip++) {
2687 if ((int) atm->q[ctl->qnt_current_subdomain][ip] != -1) {
2688 if ((int) atm->q[ctl->qnt_target_subdomain][ip] == rank)
2689 dd->sort_key[ip] =
2690 (double) ((locate_reg(met0->lon, met0->nx, atm->lon[ip]) *
2691 met0->ny + locate_irr(met0->lat, met0->ny, atm->lat[ip]))
2692 * met0->np + locate_irr(met0->p, met0->np, atm->p[ip]));
2693 else
2694 dd->sort_key[ip] = amax + 1;
2695 } else {
2696 dd->sort_key[ip] = amax + 2;
2697 }
2698 dd->perm[ip] = ip;
2699 }
2700
2701 /* Sorting... */
2702#ifdef THRUST
2703#ifdef _OPENACC
2704#pragma acc host_data use_device(dd->sort_key, dd->perm)
2705#endif
2706 thrustSortWrapper(dd->sort_key, np, dd->perm);
2707#else
2708 size_t *perm_sz = (size_t *) malloc((size_t) np * sizeof(size_t));
2709 if (perm_sz == NULL)
2710 ERRMSG("Out of memory!");
2711#ifdef _OPENACC
2712#pragma acc update self(dd->sort_key[0:np])
2713#endif
2714 gsl_sort_index(perm_sz, dd->sort_key, 1, (size_t) np);
2715 for (int ip = 0; ip < np; ++ip)
2716 dd->perm[ip] = (int) perm_sz[ip];
2717 free(perm_sz);
2718#ifdef _OPENACC
2719#pragma acc update device(dd->perm[0:np])
2720#endif
2721#endif
2722
2723 /* Sort data... */
2724 sort_array(atm->time, dd->perm, dd->tmp, np);
2725 sort_array(atm->p, dd->perm, dd->tmp, np);
2726 sort_array(atm->lon, dd->perm, dd->tmp, np);
2727 sort_array(atm->lat, dd->perm, dd->tmp, np);
2728 for (int iq = 0; iq < ctl->nq; iq++)
2729 sort_array(atm->q[iq], dd->perm, dd->tmp, np);
2730 sort_array(cache->dt, dd->perm, dd->tmp, np);
2731 sort_uvwp(cache, dd->perm, dd->tmp, np);
2732
2733 /* Count kept, outgoing, and invalid particles in one pass... */
2734 int nkeep = 0, nsend = 0, nlost = 0;
2735#ifdef _OPENACC
2736#pragma acc parallel loop reduction(+:nkeep,nsend,nlost) present(atm, ctl)
2737#endif
2738 for (int ip = 0; ip < np; ip++) {
2739 const int current = (int) atm->q[ctl->qnt_current_subdomain][ip];
2740 const int target = (int) atm->q[ctl->qnt_target_subdomain][ip];
2741 if (current == -1)
2742 nlost++;
2743 else if (target == rank)
2744 nkeep++;
2745 else
2746 nsend++;
2747 }
2748
2749 /* Reset sizes... */
2750 *npart = nsend;
2751
2752 if (nlost > 0)
2753 WARN
2754 ("Rank %d: removing %d inactive particle slots during DD sorting (kept: %d, to_send: %d, total_before: %d)",
2755 rank, nlost, nkeep, nsend, np);
2756
2757 atm->np = nkeep;
2758#ifdef _OPENACC
2759#pragma acc update device(atm->np)
2760#endif
2761
2762 /* Free... */
2763#ifdef _OPENACC
2764#pragma acc exit data delete(amax)
2765#endif
2766}
double sort_key[NP]
Auxiliary array for sorting.
Definition mptrac.h:4123
double lon[EX]
Longitudes [deg].
Definition mptrac.h:3917
double lat[EY]
Latitudes [deg].
Definition mptrac.h:3920
Here is the call graph for this function:

◆ dd_count_outgoing()

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.

For DD_EXCHANGE_MODE=1, classify each active particle by its current and target subdomain indices. Particles with either index negative are invalid. Valid particles targeting the current MPI rank are retained; the other valid particles are outgoing. The routine returns the two counts without changing the atmospheric state.

With OpenACC, atomic counters also place retained source indices at the front of dd->perm and outgoing source indices at the back. Invalid particles receive no index. Only the two counts are copied to the host; particle data remain on the device. In CPU builds, the routine counts particles without filling dd->perm.

Parameters
[in]ctlControl structure containing the current and target subdomain quantity indices.
[in]atmAtmospheric state containing particles to classify.
[out]ddSource-index mapping in OpenACC builds; unused in CPU builds.
[out]nkeepNumber of valid particles retained on this rank.
[out]npartNumber of valid particles sent to other ranks.
Note
The local MPI rank is obtained from MPI_COMM_WORLD. The order of indices generated by atomic operations is not deterministic.
Author
Lars Hoffmann

Definition at line 2772 of file mptrac.c.

2777 {
2778
2779 /* Count particles that must be sent to another rank... */
2780 SELECT_TIMER("DD_COUNT_OUTGOING", "DD");
2781 int rank;
2782 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
2783 const int np = atm->np;
2784#ifdef _OPENACC
2785 int counts[2] = { 0, 0 };
2786 if (np > 0) {
2787 SELECT_TIMER("DD_GPU_CLASSIFY", "DD");
2788#pragma acc data copy(counts[0:2]) present(ctl, atm, dd)
2789 {
2790#pragma acc parallel loop
2791 for (int ip = 0; ip < np; ip++) {
2792 const int current = (int) atm->q[ctl->qnt_current_subdomain][ip];
2793 const int target = (int) atm->q[ctl->qnt_target_subdomain][ip];
2794 int i;
2795 if (current < 0 || target < 0)
2796 continue;
2797 if (target == rank) {
2798#pragma acc atomic capture
2799 {
2800 i = counts[0];
2801 counts[0]++;
2802 }
2803 dd->perm[i] = ip;
2804 } else {
2805#pragma acc atomic capture
2806 {
2807 i = counts[1];
2808 counts[1]++;
2809 }
2810 dd->perm[np - 1 - i] = ip;
2811 }
2812 }
2813 }
2814 }
2815 *nkeep = counts[0];
2816 *npart = counts[1];
2817#else
2818 (void) dd;
2819 int kept = 0;
2820 int nsend = 0;
2821 for (int ip = 0; ip < np; ip++) {
2822 if ((int) atm->q[ctl->qnt_current_subdomain][ip] < 0
2823 || (int) atm->q[ctl->qnt_target_subdomain][ip] < 0)
2824 continue;
2825 if ((int) atm->q[ctl->qnt_target_subdomain][ip] == rank)
2826 kept++;
2827 else
2828 nsend++;
2829 }
2830 *nkeep = kept;
2831 *npart = nsend;
2832#endif
2833}

◆ doy2day()

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).

This function computes the month and day for a given day of the year (DOY) and year. It accounts for whether the given year is a leap year or not.

Parameters
yearThe year corresponding to the DOY.
doyThe day of the year (1-365 or 1-366).
monPointer to an integer where the computed month will be stored.
dayPointer to an integer where the computed day of the month will be stored.

The function uses two arrays, d0 and d0l, which contain the cumulative number of days at the start of each month for non-leap years and leap years respectively. It checks if the year is a leap year and calculates the month and day of the month accordingly.

Note
The function assumes that the input DOY is valid for the given year.
Author
Lars Hoffmann

Definition at line 2838 of file mptrac.c.

2842 {
2843
2844 const int
2845 d0[12] = { 1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 },
2846 d0l[12] = { 1, 32, 61, 92, 122, 153, 183, 214, 245, 275, 306, 336 };
2847
2848 int i;
2849
2850 /* Get month and day... */
2851 if (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)) {
2852 for (i = 11; i > 0; i--)
2853 if (d0l[i] <= doy)
2854 break;
2855 *mon = i + 1;
2856 *day = doy - d0l[i] + 1;
2857 } else {
2858 for (i = 11; i > 0; i--)
2859 if (d0[i] <= doy)
2860 break;
2861 *mon = i + 1;
2862 *day = doy - d0[i] + 1;
2863 }
2864}

◆ fft_help()

void fft_help ( double * fcReal,
double * fcImag,
const int n )

Computes the Fast Fourier Transform (FFT) of a complex sequence.

This function calculates the FFT of a complex sequence represented by separate arrays for the real and imaginary parts. The input arrays fcReal and fcImag are modified in place to contain the transformed data.

Parameters
fcRealPointer to an array of doubles representing the real part of the input sequence. The array should have at least n elements.
fcImagPointer to an array of doubles representing the imaginary part of the input sequence. The array should have at least n elements.
nThe number of complex data points in the input sequence. This value should not exceed PMAX.
Precondition
fcReal and fcImag must point to arrays of at least n elements.
n must be less than or equal to PMAX.
Postcondition
The arrays fcReal and fcImag will contain the real and imaginary parts of the FFT result, respectively.
Note
This function uses the GNU Scientific Library (GSL) for computing the FFT. Ensure that GSL is properly installed and linked in your project.
Warning
If n exceeds PMAX, the function will trigger an error message and terminate.
Author
Lars Hoffmann

Definition at line 2868 of file mptrac.c.

2871 {
2872
2873 double data[2 * EX];
2874
2875 /* Check size... */
2876 if (n > EX)
2877 ERRMSG("Too many data points!");
2878
2879 /* Allocate... */
2880 gsl_fft_complex_wavetable *wavetable =
2881 gsl_fft_complex_wavetable_alloc((size_t) n);
2882 gsl_fft_complex_workspace *workspace =
2883 gsl_fft_complex_workspace_alloc((size_t) n);
2884
2885 /* Set data (real, complex)... */
2886 for (int i = 0; i < n; i++) {
2887 data[2 * i] = fcReal[i];
2888 data[2 * i + 1] = fcImag[i];
2889 }
2890
2891 /* Calculate FFT... */
2892 gsl_fft_complex_forward(data, 1, (size_t) n, wavetable, workspace);
2893
2894 /* Copy data... */
2895 for (int i = 0; i < n; i++) {
2896 fcReal[i] = data[2 * i];
2897 fcImag[i] = data[2 * i + 1];
2898 }
2899
2900 /* Free... */
2901 gsl_fft_complex_wavetable_free(wavetable);
2902 gsl_fft_complex_workspace_free(workspace);
2903}
#define EX
Maximum number of longitudes for meteo data.
Definition mptrac.h:549

◆ geo2cart()

void geo2cart ( const double z,
const double lon,
const double lat,
double * x )

Converts geographic coordinates (longitude, latitude, altitude) to Cartesian coordinates.

This function converts geographic coordinates specified by longitude, latitude, and altitude into Cartesian coordinates. The Earth is approximated as a sphere with radius defined by the constant RE.

Parameters
zThe altitude above the Earth's surface in kilometers.
lonThe longitude in degrees.
latThe latitude in degrees.
xPointer to an array of three doubles where the computed Cartesian coordinates (x, y, z) will be stored.

The function computes the Cartesian coordinates using the given altitude, longitude, and latitude. It assumes the Earth is a perfect sphere and uses the following formulas:

  • \( x = (\textrm{radius}) \cos(\textrm{lat in radians}) \cos(\textrm{lon in radians}) \)
  • \( y = (\textrm{radius}) \cos(\textrm{lat in radians}) \sin(\textrm{lon in radians}) \)
  • \( z = (\textrm{radius}) \sin(\textrm{lat in radians}) \)
Note
The constant RE is defined as the Earth's radius in kilometers.
Longitude and latitude should be in degrees.
See also
https://en.wikipedia.org/wiki/Geographic_coordinate_conversion
Author
Lars Hoffmann

Definition at line 2907 of file mptrac.c.

2911 {
2912
2913 const double radius = z + RE;
2914 const double latrad = DEG2RAD(lat);
2915 const double lonrad = DEG2RAD(lon);
2916 const double coslat = cos(latrad);
2917
2918 x[0] = radius * coslat * cos(lonrad);
2919 x[1] = radius * coslat * sin(lonrad);
2920 x[2] = radius * sin(latrad);
2921}

◆ get_met_filename()

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.

This function determines a rounded time interval, decodes the time components (year, month, day, hour, minute, second), and constructs a filename string for meteorological data files in various formats. The filename is adjusted based on the input control settings.

Parameters
[in]ctlPointer to the control structure containing configuration settings.
[in]tThe time value in seconds since a reference epoch.
[in]directDirection to round the time value. Use -1 for rounding down and 1 for rounding up.
[in]metbaseBase string for the filename, representing the dataset.
[in]dt_metTime interval for rounding in seconds.
[out]filenameOutput buffer to store the generated filename.
Note
The function modifies the provided filename buffer to include placeholders (e.g., YYYY, MM, DD, HH) replaced with the corresponding time values. The format of the filename depends on the values in the control structure (e.g., ctl->met_type).
Warning
Ensure that the filename buffer has sufficient size to accommodate the resulting string.
Author
Lars Hoffmann

Definition at line 2925 of file mptrac.c.

2931 {
2932
2933 char repl[LEN];
2934
2935 double t6, r;
2936
2937 int year, mon, day, hour, min, sec;
2938
2939 /* Round time to fixed intervals... */
2940 if (direct == -1)
2941 t6 = floor(t / dt_met) * dt_met;
2942 else
2943 t6 = ceil(t / dt_met) * dt_met;
2944
2945 /* Decode time... */
2946 jsec2time(t6, &year, &mon, &day, &hour, &min, &sec, &r);
2947
2948 /* Set filename of MPTRAC meteo files... */
2949 if (ctl->met_clams == 0) {
2950 if (ctl->met_type == 0)
2951 sprintf(filename, "%s_YYYY_MM_DD_HH.nc", metbase);
2952 else if (ctl->met_type == 1)
2953 sprintf(filename, "%s_YYYY_MM_DD_HH.bin", metbase);
2954 else if (ctl->met_type == 2)
2955 sprintf(filename, "%s_YYYY_MM_DD_HH.pck", metbase);
2956 else if (ctl->met_type == 3)
2957 sprintf(filename, "%s_YYYY_MM_DD_HH.zfp", metbase);
2958 else if (ctl->met_type == 4)
2959 sprintf(filename, "%s_YYYY_MM_DD_HH.zstd", metbase);
2960 else if (ctl->met_type == 5)
2961 sprintf(filename, "%s_YYYY_MM_DD_HH.cms", metbase);
2962 else if (ctl->met_type == 7)
2963 sprintf(filename, "%s_YYYY_MM_DD_HH.sz3", metbase);
2964 else if (ctl->met_type == 8)
2965 sprintf(filename, "%s_YYYY_MM_DD_HH.lz4", metbase);
2966 sprintf(repl, "%d", year);
2967 get_met_replace(filename, "YYYY", repl);
2968 sprintf(repl, "%02d", mon);
2969 get_met_replace(filename, "MM", repl);
2970 sprintf(repl, "%02d", day);
2971 get_met_replace(filename, "DD", repl);
2972 sprintf(repl, "%02d", hour);
2973 get_met_replace(filename, "HH", repl);
2974 }
2975
2976 /* Set filename of CLaMS meteo files... */
2977 else {
2978 sprintf(filename, "%s_YYMMDDHH.nc", metbase);
2979 sprintf(repl, "%d", year);
2980 get_met_replace(filename, "YYYY", repl);
2981 sprintf(repl, "%02d", year % 100);
2982 get_met_replace(filename, "YY", repl);
2983 sprintf(repl, "%02d", mon);
2984 get_met_replace(filename, "MM", repl);
2985 sprintf(repl, "%02d", day);
2986 get_met_replace(filename, "DD", repl);
2987 sprintf(repl, "%02d", hour);
2988 get_met_replace(filename, "HH", repl);
2989 }
2990}
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:3571
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:2994
int met_clams
Read MPTRAC or CLaMS meteo data (0=MPTRAC, 1=CLaMS).
Definition mptrac.h:2893
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:2890
Here is the call graph for this function:

◆ get_met_replace()

void get_met_replace ( char * orig,
const char * search,
const char * repl )

Replaces occurrences of a substring in a string with another substring.

This function replaces occurrences of the substring search in the string orig with the substring repl. The replacement is performed in-place.

Parameters
origThe original string where replacements are to be made.
searchThe substring to be replaced.
replThe substring to replace occurrences of search.

The function iterates over the original string orig and replaces each occurrence of the substring search with the substring repl. It performs the replacement operation up to three times to ensure multiple occurrences are replaced.

Note
We use this function to replace the strings YYYY, MM, and DD by year, month, and day in filenames.
Ensure that orig, search, and repl are properly initialized and have sufficient memory allocated before calling this function.
Author
Lars Hoffmann

Definition at line 2994 of file mptrac.c.

2997 {
2998
2999 char buffer[LEN];
3000
3001 /* Iterate... */
3002 for (int i = 0; i < 3; i++) {
3003
3004 /* Replace sub-string... */
3005 char *ch;
3006 if (!(ch = strstr(orig, search)))
3007 return;
3008 strncpy(buffer, orig, (size_t) (ch - orig));
3009 buffer[ch - orig] = 0;
3010 sprintf(buffer + (ch - orig), "%s%s", repl, ch + strlen(search));
3011 orig[0] = 0;
3012 strcpy(orig, buffer);
3013 }
3014}

◆ get_tropo()

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.

This function reads and interpolates various meteorological parameters such as tropopause pressure, temperature, and ozone concentration at specified latitudes and longitudes. The interpolated data is stored in the provided arrays.

Parameters
met_tropoAn integer specifying the type of meteorological data to use.
ctlPointer to a ctl_t structure that controls the meteorological data processing.
climPointer to a clim_t structure containing climatological data.
metPointer to a met_t structure containing meteorological data.
lonsArray of longitudes at which to interpolate data. The array should have nx elements.
nxNumber of longitude points.
latsArray of latitudes at which to interpolate data. The array should have ny elements.
nyNumber of latitude points.
ptPointer to an array where the interpolated pressure values will be stored. The array should have nx * ny elements.
ztPointer to an array where the interpolated height values will be stored. The array should have nx * ny elements.
ttPointer to an array where the interpolated temperature values will be stored. The array should have nx * ny elements.
qtPointer to an array where the interpolated specific humidity values will be stored. The array should have nx * ny elements.
o3tPointer to an array where the interpolated ozone concentration values will be stored. The array should have nx * ny elements.
psPointer to an array where the interpolated surface pressure values will be stored. The array should have nx * ny elements.
zsPointer to an array where the interpolated surface height values will be stored. The array should have nx * ny elements.
Precondition
lons must have at least nx elements.
lats must have at least ny elements.
pt, zt, tt, qt, o3t, ps, and zs must have at least nx * ny elements.
Postcondition
The arrays pt, zt, tt, qt, o3t, ps, and zs will contain the interpolated meteorological data.
Note
The function utilizes OpenMP for parallel processing of the interpolation tasks.
The function uses the auxiliary functions read_met_tropo, intpol_met_space_2d, and intpol_met_space_3d for reading and interpolating the tropopause data.
Author
Lars Hoffmann

Definition at line 3018 of file mptrac.c.

3033 {
3034
3036
3037 ctl->met_tropo = met_tropo;
3038 read_met_tropo(ctl, clim, met);
3039#pragma omp parallel for default(shared) private(ci,cw)
3040 for (int ix = 0; ix < nx; ix++)
3041 for (int iy = 0; iy < ny; iy++) {
3042 intpol_met_space_2d(met, met->pt, lons[ix], lats[iy],
3043 &pt[iy * nx + ix], ci, cw, 1);
3044 intpol_met_space_2d(met, met->ps, lons[ix], lats[iy],
3045 &ps[iy * nx + ix], ci, cw, 0);
3046 intpol_met_space_2d(met, met->zs, lons[ix], lats[iy],
3047 &zs[iy * nx + ix], ci, cw, 0);
3048 intpol_met_space_3d(met, met->z, pt[iy * nx + ix], lons[ix],
3049 lats[iy], &zt[iy * nx + ix], ci, cw, 1);
3050 intpol_met_space_3d(met, met->t, pt[iy * nx + ix], lons[ix],
3051 lats[iy], &tt[iy * nx + ix], ci, cw, 0);
3052 intpol_met_space_3d(met, met->h2o, pt[iy * nx + ix], lons[ix],
3053 lats[iy], &qt[iy * nx + ix], ci, cw, 0);
3054 intpol_met_space_3d(met, met->o3, pt[iy * nx + ix], lons[ix],
3055 lats[iy], &o3t[iy * nx + ix], ci, cw, 0);
3056 }
3057}
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:3354
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:13054
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:3291
#define INTPOL_INIT
Initialize arrays for interpolation.
Definition mptrac.h:1183
int met_tropo
Tropopause definition (0=none, 1=clim, 2=cold point, 3=WMO_1st, 4=WMO_2nd, 5=dynamical).
Definition mptrac.h:3017
float h2o[EX][EY][EP]
Water vapor volume mixing ratio [1].
Definition mptrac.h:4028
float ps[EX][EY]
Surface pressure [hPa].
Definition mptrac.h:3938
float zs[EX][EY]
Surface geopotential height [km].
Definition mptrac.h:3944
float o3[EX][EY][EP]
Ozone volume mixing ratio [1].
Definition mptrac.h:4031
float t[EX][EY][EP]
Temperature [K].
Definition mptrac.h:4013
float pt[EX][EY]
Tropopause pressure [hPa].
Definition mptrac.h:3971
float z[EX][EY][EP]
Geopotential height [km].
Definition mptrac.h:4010
Here is the call graph for this function:

◆ intpol_check_lon_lat()

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.

This function checks and modifies the given longitude and latitude values to fit within the specified longitude and latitude arrays. The longitude is wrapped within a 360-degree range, and the latitude is clamped within the valid range defined by the latitude array.

Parameters
[in]lonsPointer to an array of valid longitude values.
[in]nlonNumber of elements in the longitude array.
[in]latsPointer to an array of valid latitude values.
[in]nlatNumber of elements in the latitude array.
[in]lonInput longitude to be checked and adjusted.
[in]latInput latitude to be checked and adjusted.
[out]lon2Pointer to the adjusted longitude.
[out]lat2Pointer to the adjusted latitude.
Author
Lars Hoffmann

Definition at line 3061 of file mptrac.c.

3069 {
3070
3071 /* Check longitude... */
3072 *lon2 = FMOD(lon, 360.);
3073 if (*lon2 < lons[0])
3074 *lon2 += 360;
3075 else if (*lon2 > lons[nlon - 1])
3076 *lon2 -= 360;
3077
3078 /* Check latitude... */
3079 *lat2 = lat;
3080 if (lats[0] < lats[nlat - 1])
3081 *lat2 = MIN(MAX(*lat2, lats[0]), lats[nlat - 1]);
3082 else
3083 *lat2 = MIN(MAX(*lat2, lats[nlat - 1]), lats[0]);
3084}

◆ intpol_check_cartesian()

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.

This function constrains the given UTM easting and northing values so they remain within the limits defined by the provided x and y coordinate arrays.

Parameters
[in]lonsPointer to an array of valid x/easting values.
[in]nlonNumber of elements in the x/easting array.
[in]latsPointer to an array of valid y/northing values.
[in]nlatNumber of elements in the y/northing array.
[in]lonInput x/easting coordinate to be checked and adjusted.
[in]latInput y/northing coordinate to be checked and adjusted.
[out]lon2Pointer to the adjusted x/easting coordinate.
[out]lat2Pointer to the adjusted y/northing coordinate.
Author
Robin Brase

Definition at line 3088 of file mptrac.c.

3096 {
3097
3098 *lon2 = lon;
3099 if (lons[0] < lons[nlon - 1])
3100 *lon2 = MIN(MAX(lon, lons[0]), lons[nlon - 1]);
3101 else
3102 *lon2 = MIN(MAX(lon, lons[nlon - 1]), lons[0]);
3103
3104 *lat2 = lat;
3105 if (lats[0] < lats[nlat - 1])
3106 *lat2 = MIN(MAX(lat, lats[0]), lats[nlat - 1]);
3107 else
3108 *lat2 = MIN(MAX(lat, lats[nlat - 1]), lats[0]);
3109}

◆ intpol_met_4d_zeta()

void intpol_met_4d_zeta ( const met_t * met0,
float height0[EX][EY][EP],
float array0[EX][EY][EP],
const met_t * met1,
float height1[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.

This function interpolates meteorological variables to a specified position and time. It calculates the interpolated value based on the values provided at two time steps and performs interpolation in time, longitude, latitude, and altitude dimensions.

Parameters
met0Pointer to the meteorological data at the first time step.
height0Array containing heights at the first time step.
array0Array containing meteorological variable values at the first time step.
met1Pointer to the meteorological data at the second time step.
height1Array containing heights at the second time step.
array1Array containing meteorological variable values at the second time step.
tsAbsolute interpolation time [s] on the same time axis as met0 and met1.
heightVertical coordinate at which to interpolate (e.g., pressure or zeta).
lonLongitude at which to interpolate.
latLatitude at which to interpolate.
varPointer to store the interpolated variable value.
ciArray to store the calculated indices.
cwArray to store the weighting factors.
initFlag indicating if it's the first call (1) or not (0).

The function first restricts the longitude within the range [0, 360) degrees. It then calculates the horizontal indices (ci[0] and ci[1]) based on the provided longitude and latitude. Next, it locates the vertical indices for each edge of the column based on the provided height.

The function then calculates the weighting factors for time, longitude, latitude, and altitude. It iterates over the interpolation process to determine the altitude weighting factor. After initializing the interpolation parameters, it calculates the interpolated variable value and stores it in the memory location pointed to by var.

Time and vertical weights are not clamped, allowing extrapolation from the selected intervals. This is intended primarily to tolerate small boundary excursions from integration, diffusion, or floating-point rounding.

Note
Ensure that all arrays (height0, array0, height1, array1, ci, cw) have sufficient memory allocated before calling this function.
Author
Jan Clemens

Definition at line 3114 of file mptrac.c.

3128 {
3129
3130 if (init) {
3131
3132 /* Check longitude and latitude... */
3133 double lon2, lat2;
3134
3135 if (met0->coord_type == 0)
3136 intpol_check_lon_lat(met0->lon, met0->nx, met0->lat, met0->ny, lon, lat,
3137 &lon2, &lat2);
3138 else
3139 intpol_check_cartesian(met0->lon, met0->nx, met0->lat, met0->ny, lon,
3140 lat, &lon2, &lat2);
3141
3142 /* Get horizontal indizes... */
3143 ci[0] = locate_reg(met0->lon, met0->nx, lon2);
3144 ci[1] = locate_irr(met0->lat, met0->ny, lat2);
3145
3146 /* Locate the vertical indizes for each edge of the column... */
3147 int ind[2][4];
3148 locate_vert(heights0, met0->npl, ci[0], ci[1], height, ind[0]);
3149 locate_vert(heights1, met1->npl, ci[0], ci[1], height, ind[1]);
3150
3151 /* Find minimum and maximum indizes... */
3152 ci[2] = ind[0][0];
3153 int k_max = ind[0][0];
3154 for (int i = 0; i < 2; i++)
3155 for (int j = 0; j < 4; j++) {
3156 if (ci[2] > ind[i][j])
3157 ci[2] = ind[i][j];
3158 if (k_max < ind[i][j])
3159 k_max = ind[i][j];
3160 }
3161
3162 /* Get weighting factors for time, longitude and latitude... */
3163 cw[3] = (ts - met0->time) / (met1->time - met0->time);
3164 cw[0] = (lon2 - met0->lon[ci[0]]) /
3165 (met0->lon[ci[0] + 1] - met0->lon[ci[0]]);
3166 cw[1] = (lat2 - met0->lat[ci[1]]) /
3167 (met0->lat[ci[1] + 1] - met0->lat[ci[1]]);
3168
3169 /* Interpolate in time at the lowest level... */
3170 double height00 = cw[3] * (heights1[ci[0]][ci[1]][ci[2]]
3171 - heights0[ci[0]][ci[1]][ci[2]])
3172 + heights0[ci[0]][ci[1]][ci[2]];
3173 double height01 = cw[3] * (heights1[ci[0]][ci[1] + 1][ci[2]]
3174 - heights0[ci[0]][ci[1] + 1][ci[2]])
3175 + heights0[ci[0]][ci[1] + 1][ci[2]];
3176 double height10 = cw[3] * (heights1[ci[0] + 1][ci[1]][ci[2]]
3177 - heights0[ci[0] + 1][ci[1]][ci[2]])
3178 + heights0[ci[0] + 1][ci[1]][ci[2]];
3179 double height11 = cw[3] * (heights1[ci[0] + 1][ci[1] + 1][ci[2]]
3180 - heights0[ci[0] + 1][ci[1] + 1][ci[2]])
3181 + heights0[ci[0] + 1][ci[1] + 1][ci[2]];
3182
3183 /* Interpolate in latitude direction... */
3184 double height0 = cw[1] * (height01 - height00) + height00;
3185 double height1 = cw[1] * (height11 - height10) + height10;
3186
3187 /* Interpolate in longitude direction... */
3188 double height_bot = cw[0] * (height1 - height0) + height0;
3189
3190 /* Interpolate in time at the upper level... */
3191 height00 = cw[3] * (heights1[ci[0]][ci[1]][ci[2] + 1]
3192 - heights0[ci[0]][ci[1]][ci[2] + 1])
3193 + heights0[ci[0]][ci[1]][ci[2] + 1];
3194 height01 = cw[3] * (heights1[ci[0]][ci[1] + 1][ci[2] + 1]
3195 - heights0[ci[0]][ci[1] + 1][ci[2] + 1])
3196 + heights0[ci[0]][ci[1] + 1][ci[2] + 1];
3197 height10 = cw[3] * (heights1[ci[0] + 1][ci[1]][ci[2] + 1]
3198 - heights0[ci[0] + 1][ci[1]][ci[2] + 1])
3199 + heights0[ci[0] + 1][ci[1]][ci[2] + 1];
3200 height11 = cw[3] * (heights1[ci[0] + 1][ci[1] + 1][ci[2] + 1]
3201 - heights0[ci[0] + 1][ci[1] + 1][ci[2] + 1])
3202 + heights0[ci[0] + 1][ci[1] + 1][ci[2] + 1];
3203
3204 /* Interpolate in latitude direction... */
3205 height0 = cw[1] * (height01 - height00) + height00;
3206 height1 = cw[1] * (height11 - height10) + height10;
3207
3208 /* Interpolate in longitude direction... */
3209 double height_top = cw[0] * (height1 - height0) + height0;
3210
3211 /* Search at higher levels if height is not in box... */
3212 while (((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 ((heights0[0][0][0] < heights0[0][0][1]) &&
3217 ((height_bot >= height) || (height_top < height))
3218 && (height_bot <= height) && (ci[2] < k_max))
3219 ) {
3220
3221 ci[2]++;
3222 height_bot = height_top;
3223
3224 /* Interpolate in time at the next level... */
3225 height00 = cw[3] * (heights1[ci[0]][ci[1]][ci[2] + 1]
3226 - heights0[ci[0]][ci[1]][ci[2] + 1])
3227 + heights0[ci[0]][ci[1]][ci[2] + 1];
3228 height01 = cw[3] * (heights1[ci[0]][ci[1] + 1][ci[2] + 1]
3229 - heights0[ci[0]][ci[1] + 1][ci[2] + 1])
3230 + heights0[ci[0]][ci[1] + 1][ci[2] + 1];
3231 height10 = cw[3] * (heights1[ci[0] + 1][ci[1]][ci[2] + 1]
3232 - heights0[ci[0] + 1][ci[1]][ci[2] + 1])
3233 + heights0[ci[0] + 1][ci[1]][ci[2] + 1];
3234 height11 = cw[3] * (heights1[ci[0] + 1][ci[1] + 1][ci[2] + 1]
3235 - heights0[ci[0] + 1][ci[1] + 1][ci[2] + 1])
3236 + heights0[ci[0] + 1][ci[1] + 1][ci[2] + 1];
3237
3238 /* Interpolate in latitude direction... */
3239 height0 = cw[1] * (height01 - height00) + height00;
3240 height1 = cw[1] * (height11 - height10) + height10;
3241
3242 /* Interpolate in longitude direction... */
3243 height_top = cw[0] * (height1 - height0) + height0;
3244 }
3245
3246 /* Get vertical weighting factors... */
3247 cw[2] = (height - height_bot)
3248 / (height_top - height_bot);
3249 }
3250
3251 /* Calculate the needed array values... */
3252 const double array000 = cw[3] * (array1[ci[0]][ci[1]][ci[2]]
3253 - array0[ci[0]][ci[1]][ci[2]])
3254 + array0[ci[0]][ci[1]][ci[2]];
3255 const double array100 = cw[3] * (array1[ci[0] + 1][ci[1]][ci[2]]
3256 - array0[ci[0] + 1][ci[1]][ci[2]])
3257 + array0[ci[0] + 1][ci[1]][ci[2]];
3258 const double array010 = cw[3] * (array1[ci[0]][ci[1] + 1][ci[2]]
3259 - array0[ci[0]][ci[1] + 1][ci[2]])
3260 + array0[ci[0]][ci[1] + 1][ci[2]];
3261 const double array110 = cw[3] * (array1[ci[0] + 1][ci[1] + 1][ci[2]]
3262 - array0[ci[0] + 1][ci[1] + 1][ci[2]])
3263 + array0[ci[0] + 1][ci[1] + 1][ci[2]];
3264 const double array001 = cw[3] * (array1[ci[0]][ci[1]][ci[2] + 1]
3265 - array0[ci[0]][ci[1]][ci[2] + 1])
3266 + array0[ci[0]][ci[1]][ci[2] + 1];
3267 const double array101 = cw[3] * (array1[ci[0] + 1][ci[1]][ci[2] + 1]
3268 - array0[ci[0] + 1][ci[1]][ci[2] + 1])
3269 + array0[ci[0] + 1][ci[1]][ci[2] + 1];
3270 const double array011 = cw[3] * (array1[ci[0]][ci[1] + 1][ci[2] + 1]
3271 - array0[ci[0]][ci[1] + 1][ci[2] + 1])
3272 + array0[ci[0]][ci[1] + 1][ci[2] + 1];
3273 const double array111 = cw[3] * (array1[ci[0] + 1][ci[1] + 1][ci[2] + 1]
3274 - array0[ci[0] + 1][ci[1] + 1][ci[2] + 1])
3275 + array0[ci[0] + 1][ci[1] + 1][ci[2] + 1];
3276
3277 const double array00 = cw[0] * (array100 - array000) + array000;
3278 const double array10 = cw[0] * (array110 - array010) + array010;
3279 const double array01 = cw[0] * (array101 - array001) + array001;
3280 const double array11 = cw[0] * (array111 - array011) + array011;
3281
3282 const double aux0 = cw[1] * (array10 - array00) + array00;
3283 const double aux1 = cw[1] * (array11 - array01) + array01;
3284
3285 /* Interpolate vertically... */
3286 *var = cw[2] * (aux1 - aux0) + aux0;
3287}
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:3088
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:3884
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:3061
int coord_type
Definition mptrac.h:3902
int npl
Number of model levels.
Definition mptrac.h:3914
double time
Time [s].
Definition mptrac.h:3899
Here is the call graph for this function:

◆ intpol_met_space_3d()

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.

This function interpolates meteorological variables at a specified pressure level and geographic position. It calculates the interpolated value based on the values provided at neighboring grid points and performs interpolation in pressure, longitude, and latitude dimensions.

Parameters
metPointer to the meteorological data.
arrayArray containing meteorological variable values.
pPressure level at which to interpolate.
lonLongitude at which to interpolate.
latLatitude at which to interpolate.
varPointer to store the interpolated variable value.
ciArray to store the calculated indices.
cwArray to store the weighting factors.
initFlag indicating if it's the first call (1) or not (0).

The function first checks the longitude and adjusts it if necessary to ensure it falls within the valid range. It then calculates the interpolation indices based on the provided pressure level, longitude, and latitude. Next, it computes the interpolation weights for pressure, longitude, and latitude.

The function interpolates vertically first and then horizontally. The interpolated value is stored in the memory location pointed to by var.

Pressures outside the level range are linearly extrapolated using the nearest level interval, primarily to tolerate small boundary excursions.

Note
Ensure that the array, ci, and cw arrays have sufficient memory allocated before calling this function.
Author
Lars Hoffmann

Definition at line 3291 of file mptrac.c.

3300 {
3301
3302 /* Initialize interpolation... */
3303 if (init) {
3304
3305 /* Check longitude and latitude... */
3306 double lon2, lat2;
3307
3308 if (met->coord_type == 0)
3309 intpol_check_lon_lat(met->lon, met->nx, met->lat, met->ny, lon, lat,
3310 &lon2, &lat2);
3311 else
3312 intpol_check_cartesian(met->lon, met->nx, met->lat, met->ny, lon, lat,
3313 &lon2, &lat2);
3314
3315 /* Get interpolation indices... */
3316 ci[0] = locate_irr(met->p, met->np, p);
3317 ci[1] = locate_reg(met->lon, met->nx, lon2);
3318 ci[2] = locate_irr(met->lat, met->ny, lat2);
3319
3320 /* Get interpolation weights... */
3321 cw[0] = (met->p[ci[0] + 1] - p)
3322 / (met->p[ci[0] + 1] - met->p[ci[0]]);
3323 cw[1] = (met->lon[ci[1] + 1] - lon2)
3324 / (met->lon[ci[1] + 1] - met->lon[ci[1]]);
3325 cw[2] = (met->lat[ci[2] + 1] - lat2)
3326 / (met->lat[ci[2] + 1] - met->lat[ci[2]]);
3327 }
3328
3329 /* Interpolate vertically... */
3330 const double aux00 =
3331 cw[0] * (array[ci[1]][ci[2]][ci[0]] - array[ci[1]][ci[2]][ci[0] + 1])
3332 + array[ci[1]][ci[2]][ci[0] + 1];
3333 const double aux01 =
3334 cw[0] * (array[ci[1]][ci[2] + 1][ci[0]] -
3335 array[ci[1]][ci[2] + 1][ci[0] + 1])
3336 + array[ci[1]][ci[2] + 1][ci[0] + 1];
3337 const double aux10 =
3338 cw[0] * (array[ci[1] + 1][ci[2]][ci[0]] -
3339 array[ci[1] + 1][ci[2]][ci[0] + 1])
3340 + array[ci[1] + 1][ci[2]][ci[0] + 1];
3341 const double aux11 =
3342 cw[0] * (array[ci[1] + 1][ci[2] + 1][ci[0]] -
3343 array[ci[1] + 1][ci[2] + 1][ci[0] + 1])
3344 + array[ci[1] + 1][ci[2] + 1][ci[0] + 1];
3345
3346 /* Interpolate horizontally... */
3347 const double aux0 = cw[2] * (aux00 - aux01) + aux01;
3348 const double aux1 = cw[2] * (aux10 - aux11) + aux11;
3349 *var = cw[1] * (aux0 - aux1) + aux1;
3350}
Here is the call graph for this function:

◆ intpol_met_space_2d()

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.

This function interpolates meteorological variables at a specified geographic position. It calculates the interpolated value based on the values provided at neighboring grid points and performs interpolation in longitude and latitude dimensions.

Parameters
metPointer to the meteorological data.
arrayArray containing meteorological variable values.
lonLongitude at which to interpolate.
latLatitude at which to interpolate.
varPointer to store the interpolated variable value.
ciArray to store the calculated indices.
cwArray to store the weighting factors.
initFlag indicating if it's the first call (1) or not (0).

The function first checks the longitude and adjusts it if necessary to ensure it falls within the valid range. It then calculates the interpolation indices based on the provided longitude and latitude. Next, it computes the interpolation weights for longitude and latitude.

The function uses bilinear interpolation when all four corners are finite. Otherwise, it returns the nearest corner according to the interpolation weights, even if that value is nonfinite. This preserves the spatial pattern of valid and missing data; selecting only finite corners would expand valid regions, for example for a second tropopause or land-sea classifications.

Note
Ensure that the array, ci, and cw arrays have sufficient memory allocated before calling this function.
Author
Lars Hoffmann

Definition at line 3354 of file mptrac.c.

3362 {
3363
3364 /* Initialize interpolation... */
3365 if (init) {
3366
3367 /* Check longitude and latitude... */
3368 double lon2, lat2;
3369
3370 if (met->coord_type == 0)
3371 intpol_check_lon_lat(met->lon, met->nx, met->lat, met->ny, lon, lat,
3372 &lon2, &lat2);
3373 else
3374 intpol_check_cartesian(met->lon, met->nx, met->lat, met->ny, lon, lat,
3375 &lon2, &lat2);
3376
3377
3378 /* Get interpolation indices... */
3379 ci[1] = locate_reg(met->lon, met->nx, lon2);
3380 ci[2] = locate_irr(met->lat, met->ny, lat2);
3381
3382 /* Get interpolation weights... */
3383 cw[1] = (met->lon[ci[1] + 1] - lon2)
3384 / (met->lon[ci[1] + 1] - met->lon[ci[1]]);
3385 cw[2] = (met->lat[ci[2] + 1] - lat2)
3386 / (met->lat[ci[2] + 1] - met->lat[ci[2]]);
3387 }
3388
3389 /* Set variables... */
3390 const double aux00 = array[ci[1]][ci[2]];
3391 const double aux01 = array[ci[1]][ci[2] + 1];
3392 const double aux10 = array[ci[1] + 1][ci[2]];
3393 const double aux11 = array[ci[1] + 1][ci[2] + 1];
3394
3395 /* Interpolate horizontally... */
3396 if (isfinite(aux00) && isfinite(aux01)
3397 && isfinite(aux10) && isfinite(aux11)) {
3398 const double aux0 = cw[2] * (aux00 - aux01) + aux01;
3399 const double aux1 = cw[2] * (aux10 - aux11) + aux11;
3400 *var = cw[1] * (aux0 - aux1) + aux1;
3401 } else {
3402 if (cw[2] < 0.5) {
3403 if (cw[1] < 0.5)
3404 *var = aux11;
3405 else
3406 *var = aux01;
3407 } else {
3408 if (cw[1] < 0.5)
3409 *var = aux10;
3410 else
3411 *var = aux00;
3412 }
3413 }
3414}
Here is the call graph for this function:

◆ intpol_met_time_3d()

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.

This function interpolates meteorological data in three dimensions (longitude, latitude, and pressure) and time. It calculates the interpolated value based on the values provided at neighboring grid points and performs interpolation both spatially and temporally.

Parameters
met0Pointer to the meteorological data at time t0.
array03D array of meteorological data at time t0.
met1Pointer to the meteorological data at time t1.
array13D array of meteorological data at time t1.
tsTime stamp at which to interpolate.
pPressure level at which to interpolate.
lonLongitude at which to interpolate.
latLatitude at which to interpolate.
varPointer to store the interpolated value.
ciArray to store the calculated indices.
cwArray to store the weighting factors.
initFlag indicating if it's the first call (1) or not (0).

The function first performs spatial interpolation for both time instances (t0 and t1) using the intpol_met_space_3d function. It then calculates the weighting factor wt based on the time stamp ts. Finally, it performs temporal interpolation using the interpolated values at t0 and t1 along with the weighting factor to compute the final interpolated value stored in var.

Times outside the two meteorological timestamps are linearly extrapolated, primarily to tolerate small boundary excursions.

Note
Ensure that the ci and cw arrays have sufficient memory allocated before calling this function.
Author
Lars Hoffmann

Definition at line 3418 of file mptrac.c.

3430 {
3431
3432 double var0, var1;
3433
3434 /* Spatial interpolation... */
3435 intpol_met_space_3d(met0, array0, p, lon, lat, &var0, ci, cw, init);
3436 intpol_met_space_3d(met1, array1, p, lon, lat, &var1, ci, cw, 0);
3437
3438 /* Get weighting factor... */
3439 const double wt = (met1->time - ts) / (met1->time - met0->time);
3440
3441 /* Interpolate... */
3442 *var = wt * (var0 - var1) + var1;
3443}
Here is the call graph for this function:

◆ intpol_met_time_2d()

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.

This function interpolates meteorological data in two dimensions (longitude and latitude) and time. It calculates the interpolated value based on the values provided at neighboring grid points and performs interpolation both spatially and temporally.

Parameters
met0Pointer to the meteorological data at time t0.
array02D array of meteorological data at time t0.
met1Pointer to the meteorological data at time t1.
array12D array of meteorological data at time t1.
tsTime stamp at which to interpolate.
lonLongitude at which to interpolate.
latLatitude at which to interpolate.
varPointer to store the interpolated value.
ciArray to store the calculated indices.
cwArray to store the weighting factors.
initFlag indicating if it's the first call (1) or not (0).

The function first performs spatial interpolation for both time instances (t0 and t1) using the intpol_met_space_2d function. It then calculates the weighting factor wt based on the time stamp ts. Finally, it performs temporal interpolation using the interpolated values at t0 and t1 along with the weighting factor to compute the final interpolated value stored in var. If one of the interpolated values is not finite, it selects the valid value based on the weighting factor wt.

When both spatial values are finite, times outside the two meteorological timestamps are linearly extrapolated, primarily to tolerate small boundary excursions.

Note
Ensure that the ci and cw arrays have sufficient memory allocated before calling this function.
Author
Lars Hoffmann

Definition at line 3447 of file mptrac.c.

3458 {
3459
3460 double var0, var1;
3461
3462 /* Spatial interpolation... */
3463 intpol_met_space_2d(met0, array0, lon, lat, &var0, ci, cw, init);
3464 intpol_met_space_2d(met1, array1, lon, lat, &var1, ci, cw, 0);
3465
3466 /* Get weighting factor... */
3467 const double wt = (met1->time - ts) / (met1->time - met0->time);
3468
3469 /* Interpolate... */
3470 if (isfinite(var0) && isfinite(var1))
3471 *var = wt * (var0 - var1) + var1;
3472 else if (wt < 0.5)
3473 *var = var1;
3474 else
3475 *var = var0;
3476}
Here is the call graph for this function:

◆ intpol_tropo_3d()

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).

This function performs interpolation of tropopause data at a given latitude, longitude, and time. The interpolation can be performed using either linear interpolation or nearest neighbor interpolation. The standard deviation of the data points used in the interpolation is also computed.

Parameters
time0Time corresponding to the first data array array0.
array0A 2D array of tropopause data at time0. The dimensions are EX by EY.
time1Time corresponding to the second data array array1.
array1A 2D array of tropopause data at time1. The dimensions are EX by EY.
lonsArray of longitudes with EX elements.
latsArray of latitudes with EY elements.
nlonNumber of longitudes.
nlatNumber of latitudes.
timeThe specific time at which to interpolate the data.
lonThe specific longitude at which to interpolate the data.
latThe specific latitude at which to interpolate the data.
methodInterpolation method: 1 for linear interpolation, otherwise nearest neighbor interpolation is used.
varPointer to the variable where the interpolated value will be stored.
sigmaPointer to the variable where the standard deviation of the data points will be stored.
Precondition
array0 and array1 must be 2D arrays of size EX by EY.
lons must have at least nlon elements and lats must have at least nlat elements.
Postcondition
var will contain the interpolated value.
sigma will contain the standard deviation of the data points used in the interpolation.
Note
The function adjusts the longitude to ensure it is within the range defined by lons.
This function uses the auxiliary functions locate_reg, LIN, and NN for locating indices and performing interpolation.
Warning
Ensure that EX and EY are defined appropriately to match the dimensions of array0 and array1.
Author
Lars Hoffmann

Definition at line 3480 of file mptrac.c.

3494 {
3495
3496 double mean = 0;
3497
3498 int n = 0;
3499
3500 /* Check longitude and latitude... */
3501 double lon2, lat2;
3502 intpol_check_lon_lat(lons, nlon, lats, nlat, lon, lat, &lon2, &lat2);
3503
3504 /* Get indices... */
3505 const int ix = locate_reg(lons, (int) nlon, lon2);
3506 const int iy = locate_irr(lats, (int) nlat, lat2);
3507
3508 /* Calculate standard deviation... */
3509 *sigma = 0;
3510 for (int dx = 0; dx < 2; dx++)
3511 for (int dy = 0; dy < 2; dy++) {
3512 if (isfinite(array0[ix + dx][iy + dy])) {
3513 mean += array0[ix + dx][iy + dy];
3514 *sigma += SQR(array0[ix + dx][iy + dy]);
3515 n++;
3516 }
3517 if (isfinite(array1[ix + dx][iy + dy])) {
3518 mean += array1[ix + dx][iy + dy];
3519 *sigma += SQR(array1[ix + dx][iy + dy]);
3520 n++;
3521 }
3522 }
3523 if (n > 0)
3524 *sigma = sqrt(MAX(*sigma / n - SQR(mean / n), 0.0));
3525
3526 /* Linear interpolation... */
3527 if (method == 1 && isfinite(array0[ix][iy])
3528 && isfinite(array0[ix][iy + 1])
3529 && isfinite(array0[ix + 1][iy])
3530 && isfinite(array0[ix + 1][iy + 1])
3531 && isfinite(array1[ix][iy])
3532 && isfinite(array1[ix][iy + 1])
3533 && isfinite(array1[ix + 1][iy])
3534 && isfinite(array1[ix + 1][iy + 1])) {
3535
3536 const double aux00 = LIN(lons[ix], array0[ix][iy],
3537 lons[ix + 1], array0[ix + 1][iy], lon2);
3538 const double aux01 = LIN(lons[ix], array0[ix][iy + 1],
3539 lons[ix + 1], array0[ix + 1][iy + 1], lon2);
3540 const double aux0 = LIN(lats[iy], aux00, lats[iy + 1], aux01, lat2);
3541
3542 const double aux10 = LIN(lons[ix], array1[ix][iy],
3543 lons[ix + 1], array1[ix + 1][iy], lon2);
3544 const double aux11 = LIN(lons[ix], array1[ix][iy + 1],
3545 lons[ix + 1], array1[ix + 1][iy + 1], lon2);
3546 const double aux1 = LIN(lats[iy], aux10, lats[iy + 1], aux11, lat2);
3547
3548 *var = LIN(time0, aux0, time1, aux1, time);
3549 }
3550
3551 /* Nearest neighbor interpolation... */
3552 else {
3553 const double aux00 = NN(lons[ix], array0[ix][iy],
3554 lons[ix + 1], array0[ix + 1][iy], lon2);
3555 const double aux01 = NN(lons[ix], array0[ix][iy + 1],
3556 lons[ix + 1], array0[ix + 1][iy + 1], lon2);
3557 const double aux0 = NN(lats[iy], aux00, lats[iy + 1], aux01, lat2);
3558
3559 const double aux10 = NN(lons[ix], array1[ix][iy],
3560 lons[ix + 1], array1[ix + 1][iy], lon2);
3561 const double aux11 = NN(lons[ix], array1[ix][iy + 1],
3562 lons[ix + 1], array1[ix + 1][iy + 1], lon2);
3563 const double aux1 = NN(lats[iy], aux10, lats[iy + 1], aux11, lat2);
3564
3565 *var = NN(time0, aux0, time1, aux1, time);
3566 }
3567}
#define NN(x0, y0, x1, y1, x)
Perform nearest-neighbor interpolation.
Definition mptrac.h:1747
Here is the call graph for this function:

◆ jsec2time()

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.

This function converts Julian seconds to calendar date and time components, including year, month, day, hour, minute, and second. It also calculates the fractional part of the seconds.

Parameters
jsecJulian seconds to convert.
yearPointer to store the year.
monPointer to store the month.
dayPointer to store the day.
hourPointer to store the hour.
minPointer to store the minute.
secPointer to store the second.
remainPointer to store the fractional part of seconds.

The function initializes a time structure t0 with a fixed starting date and time. It then converts the Julian seconds to a time_t type by adding the seconds to the epoch time. Next, it converts the time_t value to a UTC time structure t1. Finally, it extracts the year, month, day, hour, minute, and second components from t1 and calculates the fractional part of seconds, which is stored in remain.

Author
Lars Hoffmann

Definition at line 3571 of file mptrac.c.

3579 {
3580
3581 struct tm t0, *t1;
3582
3583 t0.tm_year = 100;
3584 t0.tm_mon = 0;
3585 t0.tm_mday = 1;
3586 t0.tm_hour = 0;
3587 t0.tm_min = 0;
3588 t0.tm_sec = 0;
3589
3590 const time_t jsec0 = (time_t) jsec + timegm(&t0);
3591 t1 = gmtime(&jsec0);
3592
3593 *year = t1->tm_year + 1900;
3594 *mon = t1->tm_mon + 1;
3595 *day = t1->tm_mday;
3596 *hour = t1->tm_hour;
3597 *min = t1->tm_min;
3598 *sec = t1->tm_sec;
3599 *remain = jsec - floor(jsec);
3600}

◆ kernel_weight()

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.

This function calculates the kernel weight based on altitude and given kernel data. It takes arrays of altitudes (kz) and corresponding weights (kw), the number of data points (nk), and the current altitude (p) as input.

Parameters
kzArray of altitudes.
kwArray of corresponding weights.
nkNumber of data points.
pCurrent altitude.
Returns
The calculated kernel weight.

If the number of data points is less than 2 (nk < 2), the function returns a default weight of 1.0.

The function first computes the altitude z based on the current altitude p. Then it checks whether z is outside the range of altitudes in the kernel data. If so, it returns the corresponding weight at the nearest altitude boundary. Otherwise, it interpolates linearly between the two closest altitudes in the kernel data to determine the weight at altitude z.

Author
Lars Hoffmann

Definition at line 3604 of file mptrac.c.

3608 {
3609
3610 /* Check number of data points... */
3611 if (nk < 2)
3612 return 1.0;
3613
3614 /* Get altitude... */
3615 const double z = Z(p);
3616
3617 /* Get weighting factor... */
3618 if (z < kz[0])
3619 return kw[0];
3620 else if (z > kz[nk - 1])
3621 return kw[nk - 1];
3622 else {
3623 const int idx = locate_irr(kz, nk, z);
3624 return LIN(kz[idx], kw[idx], kz[idx + 1], kw[idx + 1], z);
3625 }
3626}
Here is the call graph for this function:

◆ lapse_rate()

double lapse_rate ( const double t,
const double h2o )

Calculates the moist adiabatic lapse rate in Kelvin per kilometer.

This function calculates the moist adiabatic lapse rate in Kelvin per kilometer from the given temperature (t) in Kelvin and water vapor volume mixing ratio (h2o).

Parameters
tTemperature in Kelvin.
h2oWater vapor volume mixing ratio.
Returns
The moist adiabatic lapse rate in Kelvin per kilometer.

The moist adiabatic lapse rate is calculated using the formula:

\[ \Gamma = \frac{{1000 \times g \times \left(a + L_v \times r \times T\right)}} {{C_{pd} \times a + L_v^2 \times r \times \epsilon}} \]

where:

  • \( \Gamma \) is the lapse rate in Kelvin per kilometer.
  • \( g \) is the acceleration due to gravity (constant).
  • \( a = R_a \times T^2 \) is a term based on the gas constant for dry air and temperature squared.
  • \( R_a \) is the gas constant for dry air.
  • \( T \) is the temperature in Kelvin.
  • \( L_v \) is the latent heat of vaporization.
  • \( r = \frac{{S_h(h_2o)}}{{1 - S_h(h_2o)}} \) is a term based on the water vapor mixing ratio.
  • \( S_h(h_2o) \) is the saturation vapor pressure relative to the pressure at saturation.
  • \( C_{pd} \) is the specific heat of dry air at constant pressure.
  • \( \epsilon \) is the ratio of the gas constants for dry air and water vapor.

The constants used in the calculation are defined externally:

  • \( g \): Acceleration due to gravity (constant).
  • \( R_a \): Gas constant for dry air.
  • \( L_v \): Latent heat of vaporization.
  • \( C_{pd} \): Specific heat of dry air at constant pressure.
  • \( \epsilon \): Ratio of the gas constants for dry air and water vapor.
See also
Wikipedia - Lapse rate
Author
Lars Hoffmann

Definition at line 3630 of file mptrac.c.

3632 {
3633
3634 /*
3635 Calculate moist adiabatic lapse rate [K/km] from temperature [K]
3636 and water vapor volume mixing ratio [1].
3637
3638 Reference: https://en.wikipedia.org/wiki/Lapse_rate
3639 */
3640
3641 const double a = RA * SQR(t), r = SH(h2o) / (1. - SH(h2o));
3642
3643 return 1e3 * G0 * (a + LV * r * t) / (CPD * a + SQR(LV) * r * EPS);
3644}
#define RA
Specific gas constant of dry air [J/(kg K)].
Definition mptrac.h:310
#define SH(h2o)
Compute specific humidity from water vapor volume mixing ratio.
Definition mptrac.h:2044
#define LV
Latent heat of vaporization of water [J/kg].
Definition mptrac.h:275
#define G0
Standard gravity [m/s^2].
Definition mptrac.h:265
#define EPS
Ratio of the specific gas constant of dry air and water vapor [1].
Definition mptrac.h:260
#define CPD
Specific heat of dry air at constant pressure [J/(kg K)].
Definition mptrac.h:255

◆ level_definitions()

void level_definitions ( ctl_t * ctl)

Defines pressure levels for meteorological data.

This function defines pressure levels for meteorological data based on the given control structure (ctl). Pressure levels are defined differently based on the value of met_press_level_def in ctl.

Parameters
ctlControl structure containing information about pressure level definitions.

The function determines the number of pressure levels (met_np) and the corresponding pressure values (met_p) based on the value of met_press_level_def in the control structure ctl. It initializes the met_np and met_p fields accordingly.

Note
Valid values for met_press_level_def are:
  • 3: Define 147 pressure levels.
  • 4: Define 101 pressure levels.
  • 5: Define 62 pressure levels.
  • 6: Define 137 pressure levels.
  • 7: Define 59 pressure levels. Values 0, 1, and 2 are disabled and any other value will result in an error message.
Author
Jan Clemens

Definition at line 3648 of file mptrac.c.

3649 {
3650
3651 if (0 == ctl->met_press_level_def) {
3652
3653 ERRMSG
3654 ("MET_PRESS_LEVEL_DEF=0 is disabled. Use 3 for the extended L137 set.");
3655
3656 } else if (1 == ctl->met_press_level_def) {
3657
3658 ERRMSG
3659 ("MET_PRESS_LEVEL_DEF=1 is disabled. Use 4 for the extended L91 set.");
3660
3661 } else if (2 == ctl->met_press_level_def) {
3662
3663 ERRMSG
3664 ("MET_PRESS_LEVEL_DEF=2 is disabled. Use 5 for the extended L60 set.");
3665
3666 } else if (3 == ctl->met_press_level_def) {
3667
3668 ctl->met_np = 147;
3669
3670 const double press[147] = {
3671 0.0200, 0.0310, 0.0467, 0.0683, 0.0975, 0.1361, 0.1861, 0.2499,
3672 0.3299, 0.4288, 0.5496, 0.6952, 0.8690, 1.0742, 1.3143, 1.5928, 1.9134,
3673 2.2797, 2.6954, 3.1642, 3.6898, 4.2759, 4.9262, 5.6441, 6.4334, 7.2974,
3674 8.2397, 9.2634, 10.3720, 11.5685, 12.8561, 14.2377, 15.7162, 17.2945,
3675 18.9752, 20.7610, 22.6543, 24.6577, 26.7735, 29.0039, 31.3512, 33.8174,
3676 36.4047, 39.1149, 41.9493, 44.9082, 47.9915, 51.1990, 54.5299, 57.9834,
3677 61.5607, 65.2695, 69.1187, 73.1187, 77.2810, 81.6182, 86.1450, 90.8774,
3678 95.8280, 101.0047, 106.4153, 112.0681, 117.9714, 124.1337, 130.5637,
3679 137.2703, 144.2624, 151.5493, 159.1403, 167.0450, 175.2731, 183.8344,
3680 192.7389, 201.9969, 211.6186, 221.6146, 231.9954, 242.7719, 253.9549,
3681 265.5556, 277.5852, 290.0548, 302.9762, 316.3607, 330.2202, 344.5663,
3682 359.4111, 374.7666, 390.6450, 407.0583, 424.0190, 441.5395, 459.6321,
3683 478.3096, 497.5845, 517.4198, 537.7195, 558.3430, 579.1926, 600.1668,
3684 621.1624, 642.0764, 662.8084, 683.2620, 703.3467, 722.9795, 742.0855,
3685 760.5996, 778.4661, 795.6396, 812.0847, 827.7756, 842.6959, 856.8376,
3686 870.2004, 882.7910, 894.6222, 905.7116, 916.0815, 925.7571, 934.7666,
3687 943.1399, 950.9082, 958.1037, 964.7584, 970.9046, 976.5737, 981.7968,
3688 986.6036, 991.0230, 995.0824, 998.8081, 1002.2250, 1005.3562, 1008.2239,
3689 1010.8487, 1013.25, 1016.37, 1019.49, 1022.61, 1025.73, 1028.85,
3690 1031.97,
3691 1035.09, 1038.21, 1041.33, 1044.45
3692 };
3693
3694 for (int ip = 0; ip < ctl->met_np; ip++)
3695 ctl->met_p[ctl->met_np - ip - 1] = press[ip];
3696
3697 } else if (4 == ctl->met_press_level_def) {
3698
3699 ctl->met_np = 101;
3700
3701 const double press[101] = {
3702 0.0200, 0.0398, 0.0739, 0.1291, 0.2141, 0.3395, 0.5175, 0.7617,
3703 1.0872, 1.5099, 2.0464, 2.7136, 3.5282, 4.5069, 5.6652, 7.0181,
3704 8.5795, 10.3617, 12.3759, 14.6316, 17.1371, 19.8987, 22.9216, 26.2090,
3705 29.7630, 33.5843, 37.6720, 42.0242, 46.6378, 51.5086, 56.6316, 61.9984,
3706 67.5973, 73.4150, 79.4434, 85.7016, 92.2162, 99.0182, 106.1445,
3707 113.6382,
3708 121.5502, 129.9403, 138.8558, 148.3260, 158.3816, 169.0545, 180.3786,
3709 192.3889, 205.1222, 218.6172, 232.9140, 248.0547, 264.0833, 281.0456,
3710 298.9895, 317.9651, 338.0245, 359.2221, 381.6144, 405.2606, 430.2069,
3711 456.4813, 483.8505, 512.0662, 540.8577, 569.9401, 599.0310, 627.9668,
3712 656.6129, 684.8491, 712.5573, 739.5739, 765.7697, 791.0376, 815.2774,
3713 838.3507, 860.1516, 880.6080, 899.6602, 917.2205, 933.2247, 947.6584,
3714 960.5245, 971.8169, 981.5301, 989.7322, 996.8732, 1002.8013,
3715 1007.4431, 1010.8487, 1013.25, 1016.37, 1019.49, 1022.61, 1025.73,
3716 1028.85, 1031.97,
3717 1035.09, 1038.21, 1041.33, 1044.45
3718 };
3719
3720 for (int ip = 0; ip < ctl->met_np; ip++)
3721 ctl->met_p[ctl->met_np - ip - 1] = press[ip];
3722
3723 } else if (5 == ctl->met_press_level_def) {
3724
3725 ctl->met_np = 62;
3726
3727 const double press[62] = {
3728 0.01, 0.1361, 0.2499, 0.4288, 0.6952, 1.0742,
3729 2.2797, 3.1642, 4.2759, 7.2974, 9.2634, 11.5685, 14.2377, 20.761,
3730 24.6577, 33.8174, 39.1149, 51.199, 57.9834, 73.1187, 81.6182,
3731 90.8774, 101.005, 112.068, 124.134, 137.27, 151.549, 167.045, 183.834,
3732 201.997, 221.615, 242.772, 265.556, 290.055, 316.361, 344.566, 374.767,
3733 407.058, 441.539, 478.31, 517.42, 558.343, 600.167, 683.262, 722.979,
3734 760.6, 795.64, 827.776, 856.838, 882.791, 905.712, 925.757, 943.14,
3735 958.104, 972.495, 986.886, 1001.28, 1015.67, 1030.06, 1034.86, 1039.65,
3736 1044.45
3737 };
3738
3739 for (int ip = 0; ip < ctl->met_np; ip++)
3740 ctl->met_p[ctl->met_np - ip - 1] = press[ip];
3741
3742 } else if (6 == ctl->met_press_level_def) {
3743
3744 ctl->met_np = 137;
3745
3746 const double press[137] = {
3747 0.01, 0.02, 0.031, 0.0467, 0.0683, 0.0975, 0.1361, 0.1861,
3748 0.2499, 0.3299, 0.4288, 0.5496, 0.6952, 0.869, 1.0742,
3749 1.3143, 1.5928, 1.9134, 2.2797, 2.6954, 3.1642, 3.6898,
3750 4.2759, 4.9262, 5.6441, 6.4334, 7.2974, 8.2397, 9.2634,
3751 10.372, 11.5685, 12.8561, 14.2377, 15.7162, 17.2945, 18.9752,
3752 20.761, 22.6543, 24.6577, 26.7735, 29.0039, 31.3512, 33.8174,
3753 36.4047, 39.1149, 41.9493, 44.9082, 47.9915, 51.199, 54.5299,
3754 57.9834, 61.5607, 65.2695, 69.1187, 73.1187, 77.281, 81.6182,
3755 86.145, 90.8774, 95.828, 101.005, 106.415, 112.068, 117.971,
3756 124.134, 130.564, 137.27, 144.262, 151.549, 159.14, 167.045,
3757 175.273, 183.834, 192.739, 201.997, 211.619, 221.615, 231.995,
3758 242.772, 253.955, 265.556, 277.585, 290.055, 302.976, 316.361,
3759 330.22, 344.566, 359.411, 374.767, 390.645, 407.058, 424.019,
3760 441.539, 459.632, 478.31, 497.584, 517.42, 537.72, 558.343,
3761 579.193, 600.167, 621.162, 642.076, 662.808, 683.262, 703.347,
3762 722.979, 742.086, 760.6, 778.466, 795.64, 812.085, 827.776,
3763 842.696, 856.838, 870.2, 882.791, 894.622, 905.712, 916.081,
3764 925.757, 934.767, 943.14, 950.908, 958.104, 965.299, 972.495,
3765 979.69, 986.886, 994.081, 1001.28, 1008.47, 1015.67, 1022.86,
3766 1030.06, 1037.25, 1044.45
3767 };
3768
3769 for (int ip = 0; ip < ctl->met_np; ip++)
3770 ctl->met_p[ctl->met_np - ip - 1] = press[ip];
3771
3772 } else if (7 == ctl->met_press_level_def) {
3773
3774 ctl->met_np = 59;
3775
3776 const double press[59] = {
3777 0.1, 0.2, 0.3843, 0.6365, 0.9564, 1.3448, 1.8058, 2.3478,
3778 2.985, 3.7397, 4.6462, 5.7565, 7.1322, 8.8366, 10.9483,
3779 13.5647, 16.8064, 20.8227, 25.7989, 31.9642, 39.6029, 49.0671,
3780 60.1802, 73.0663, 87.7274, 104.229, 122.614, 142.902, 165.089,
3781 189.147, 215.025, 242.652, 272.059, 303.217, 336.044, 370.407,
3782 406.133, 443.009, 480.791, 519.209, 557.973, 596.777, 635.306,
3783 673.24, 710.263, 746.063, 780.346, 812.83, 843.263, 871.42,
3784 897.112, 920.189, 940.551, 958.148, 975.744, 993.341, 1010.94,
3785 1028.53, 1046.13
3786 };
3787
3788 for (int ip = 0; ip < ctl->met_np; ip++)
3789 ctl->met_p[ctl->met_np - ip - 1] = press[ip];
3790
3791 } else {
3792 ERRMSG("Use values between 3 and 7.");
3793 }
3794
3795 if (ctl->met_np > EP)
3796 ERRMSG("Recompile with larger EP to use this pressure level definition!");
3797}
int met_press_level_def
Use predefined pressure levels or not.
Definition mptrac.h:2983
int met_np
Number of target pressure levels.
Definition mptrac.h:2977
double met_p[EP]
Target pressure levels [hPa].
Definition mptrac.h:2980

◆ locate_irr()

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.

This function locates the index of the interval containing a given value in a sorted array. It uses a binary search algorithm to efficiently find the interval.

Parameters
xxPointer to the sorted array.
nSize of the array.
xValue to be located.
Returns
Index of the interval containing the value x.

The function assumes that the array xx is sorted in ascending order. It returns the index of the interval where the value x is located. If the value x is outside the range of the array, the function returns the index of the closest interval.

Author
Lars Hoffmann

Definition at line 3801 of file mptrac.c.

3804 {
3805
3806 int ilo = 0;
3807 int ihi = n - 1;
3808 int i = (ihi + ilo) >> 1;
3809
3810 if (xx[i] < xx[i + 1])
3811 while (ihi > ilo + 1) {
3812 i = (ihi + ilo) >> 1;
3813 if (xx[i] > x)
3814 ihi = i;
3815 else
3816 ilo = i;
3817 } else
3818 while (ihi > ilo + 1) {
3819 i = (ihi + ilo) >> 1;
3820 if (xx[i] <= x)
3821 ihi = i;
3822 else
3823 ilo = i;
3824 }
3825
3826 return ilo;
3827}

◆ locate_irr_float()

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.

This function performs a binary search to locate the interval in the array xx such that xx[ig] <= x < xx[ig + 1]. If the value x lies within the interval specified by the initial guess index ig, the function returns ig. Otherwise, it searches the array to find the correct interval.

Parameters
xxPointer to the array of floats representing the irregularly spaced intervals. The array must be of size n.
nThe number of elements in the array xx.
xThe value to locate within the intervals of the array xx.
igThe initial guess index. If the interval [xx[ig], xx[ig+1]) contains x, the function returns ig directly.
Returns
The index i such that xx[i] <= x < xx[i + 1]. If x is out of bounds, it returns the index of the closest interval.
Note
The function assumes that the array xx contains at least two elements.
The function can handle both increasing and decreasing sequences in the array xx.
Warning
The behavior is undefined if the array xx is not sorted in either increasing or decreasing order, or if it contains less than two elements.
Author
Lars Hoffmann

Definition at line 3831 of file mptrac.c.

3835 {
3836
3837 int ilo = 0;
3838 int ihi = n - 1;
3839 int i = (ihi + ilo) >> 1;
3840
3841 if ((xx[ig] <= x && x < xx[ig + 1]) || (xx[ig] >= x && x > xx[ig + 1]))
3842 return ig;
3843
3844 if (xx[i] < xx[i + 1])
3845 while (ihi > ilo + 1) {
3846 i = (ihi + ilo) >> 1;
3847 if (xx[i] > x)
3848 ihi = i;
3849 else
3850 ilo = i;
3851 } else
3852 while (ihi > ilo + 1) {
3853 i = (ihi + ilo) >> 1;
3854 if (xx[i] <= x)
3855 ihi = i;
3856 else
3857 ilo = i;
3858 }
3859
3860 return ilo;
3861}

◆ locate_reg()

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.

This function locates the index of the interval containing a given value in a regular grid. It calculates the index based on the spacing between grid points and the value to be located.

Parameters
xxPointer to the array representing the regular grid.
nSize of the grid (number of grid points).
xValue to be located.
Returns
Index of the interval containing the value x.

The function assumes that the array xx represents a regular grid with equally spaced points. It calculates the index of the interval where the value x is located based on the spacing between grid points. If the value x is outside the range of the grid, the function returns the index of the closest interval.

Author
Lars Hoffmann

Definition at line 3865 of file mptrac.c.

3868 {
3869
3870 /* Calculate index... */
3871 const int i = (int) ((x - xx[0]) / (xx[1] - xx[0]));
3872
3873 /* Check range... */
3874 if (i < 0)
3875 return 0;
3876 else if (i > n - 2)
3877 return n - 2;
3878 else
3879 return i;
3880}

◆ locate_vert()

void locate_vert ( float profiles[EX][EY][EP],
const int np,
const int lon_ap_ind,
const int lat_ap_ind,
const double alt_ap,
int * ind )

Locate the four vertical indizes of a box for a given height value.

This function locates the vertical indices corresponding to a given height in a 3D irregular grid. It calculates the indices based on the specified longitude and latitude indices of the grid.

Parameters
profiles3D array representing the irregular grid.
npSize of the profile (number of data points).
lon_ap_indIndex of the longitude.
lat_ap_indIndex of the latitude.
alt_apHeight value.
indPointer to an array to store the resulting indices.

The function calculates the indices corresponding to the specified height in the 3D irregular grid. It stores the resulting indices in the array pointed to by ind. The indices are calculated based on the specified longitude and latitude indices of the grid.

Author
Lars Hoffmann

Definition at line 3884 of file mptrac.c.

3890 {
3891
3892 ind[0] = locate_irr_float(profiles[lon_ap_ind][lat_ap_ind],
3893 np, height_ap, 0);
3894 ind[1] = locate_irr_float(profiles[lon_ap_ind + 1][lat_ap_ind],
3895 np, height_ap, ind[0]);
3896 ind[2] = locate_irr_float(profiles[lon_ap_ind][lat_ap_ind + 1],
3897 np, height_ap, ind[1]);
3898 ind[3] = locate_irr_float(profiles[lon_ap_ind + 1][lat_ap_ind + 1],
3899 np, height_ap, ind[2]);
3900}
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:3831
Here is the call graph for this function:

◆ module_advect()

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.

This function updates particle positions using omega, zeta tendency, or eta tendency as the vertical velocity, according to advect_vert_coord. The advection is performed over a number of integration nodes, using meteorological data interpolated in time and space.

Parameters
[in]ctlPointer to the control structure containing configuration settings.
[in]cachePointer to the cache structure storing precomputed time step values.
[in]met0Pointer to the meteorological data structure at the initial time.
[in]met1Pointer to the meteorological data structure at the next time step.
[in,out]atmPointer to the atmospheric data structure containing particle states.
  • Modes 0 and 2 use omega on pressure and native model levels, respectively.
  • Modes 1 and 3 integrate zeta and eta tendencies on native model levels.
  • The function interpolates meteorological data either on pressure levels or model levels.
  • The advection scheme supports Euler and explicit midpoint integration.
  • The function updates longitude and latitude; modes 0 and 2 update pressure directly, while modes 1 and 3 convert the updated coordinate to pressure.
Author
Lars Hoffmann
Jan Clemens

Definition at line 3904 of file mptrac.c.

3909 {
3910
3911 /* Set timer... */
3912 SELECT_TIMER("MODULE_ADVECT", "PHYSICS");
3913
3914 /* Use omega vertical velocity... */
3915 if (ctl->advect_vert_coord == 0 || ctl->advect_vert_coord == 2) {
3916
3917 /* Loop over particles... */
3918 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
3919
3920 /* Init... */
3922 double dts, u[2], um = 0, v[2], vm = 0, w[2], wm = 0,
3923 x[3] = { 0, 0, 0 };
3924
3925 /* Loop over integration nodes... */
3926 for (int i = 0; i < ctl->advect; i++) {
3927
3928 /* Set position... */
3929 if (i == 0) {
3930 dts = 0.0;
3931 x[0] = atm->lon[ip];
3932 x[1] = atm->lat[ip];
3933 x[2] = atm->p[ip];
3934 } else {
3935 dts = 0.5 * cache->dt[ip];
3936 x[0] = atm->lon[ip] + DX2COORD(met0, dts * u[i - 1], atm->lat[ip]);
3937 x[1] = atm->lat[ip] + DY2COORD(met0, dts * v[i - 1]);
3938 x[2] = atm->p[ip] + dts * w[i - 1];
3939 }
3940 const double tm = atm->time[ip] + dts;
3941
3942 /* Interpolate meteo data on pressure levels... */
3943 if (ctl->advect_vert_coord == 0) {
3944 intpol_met_time_3d(met0, met0->u, met1, met1->u,
3945 tm, x[2], x[0], x[1], &u[i], ci, cw, 1);
3946 intpol_met_time_3d(met0, met0->v, met1, met1->v,
3947 tm, x[2], x[0], x[1], &v[i], ci, cw, 0);
3948 intpol_met_time_3d(met0, met0->w, met1, met1->w,
3949 tm, x[2], x[0], x[1], &w[i], ci, cw, 0);
3950 }
3951
3952 /* Interpolate meteo data on model levels... */
3953 else {
3954 intpol_met_4d_zeta(met0, met0->pl, met0->ul,
3955 met1, met1->pl, met1->ul,
3956 tm, x[2], x[0], x[1], &u[i], ci, cw, 1);
3957 intpol_met_4d_zeta(met0, met0->pl, met0->vl,
3958 met1, met1->pl, met1->vl,
3959 tm, x[2], x[0], x[1], &v[i], ci, cw, 0);
3960 intpol_met_4d_zeta(met0, met0->pl, met0->wl,
3961 met1, met1->pl, met1->wl,
3962 tm, x[2], x[0], x[1], &w[i], ci, cw, 0);
3963 }
3964
3965 /* Get mean wind... */
3966 const double k = (ctl->advect == 2 && i == 0 ? 0.0 : 1.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[2], um = 0, v[2], vm = 0, wdot[2],
4002 wdotm = 0, x[3] = { 0, 0, 0 };
4003
4004 /* Loop over integration nodes... */
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 = 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 /* Select Euler or midpoint wind... */
4034 const double k = (ctl->advect == 2 && i == 0 ? 0.0 : 1.0);
4035
4036 um += k * u[i];
4037 vm += k * v[i];
4038 wdotm += k * wdot[i];
4039 }
4040
4041 /* Update particle position... */
4042 atm->time[ip] += cache->dt[ip];
4043 atm->lon[ip] += DX2COORD(met0, cache->dt[ip] * um,
4044 (ctl->advect == 2 ? x[1] : atm->lat[ip]));
4045 atm->lat[ip] += DY2COORD(met0, cache->dt[ip] * vm);
4046 atm->q[qnt][ip] += cache->dt[ip] * wdotm;
4047
4048 /* Convert vertical coordinate (zeta or eta) back to pressure... */
4049 intpol_met_4d_zeta(met0, met0->zetal, met0->pl,
4050 met1, met1->zetal, met1->pl,
4051 atm->time[ip],
4052 atm->q[qnt][ip], atm->lon[ip], atm->lat[ip],
4053 &atm->p[ip], ci, cw, 1);
4054 }
4055 }
4056}
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:3418
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:3114
#define PARTICLE_LOOP(ip0, ip1, check_dt,...)
Loop over particle indices with OpenACC acceleration.
Definition mptrac.h:1774
#define DX2COORD(met, dx, lat)
Convert a distance in meters to a coordinate value based on grid type.
Definition mptrac.h:968
#define DY2COORD(met, dy)
Convert a distance to coordinate value based on grid type.
Definition mptrac.h:991
int qnt_eta
Quantity array index for eta vertical coordinate.
Definition mptrac.h:2740
int advect
Advection scheme (1=Euler, 2=midpoint).
Definition mptrac.h:3052
int qnt_zeta
Quantity array index for zeta vertical coordinate.
Definition mptrac.h:2731
int advect_vert_coord
Vertical velocity of air parcels (0=omega_on_plev, 1=zetadot_on_mlev, 2=omega_on_mlev,...
Definition mptrac.h:3056
float zeta_dotl[EX][EY][EP]
Vertical velocity on model levels [K/s].
Definition mptrac.h:4064
float w[EX][EY][EP]
Vertical velocity [hPa/s].
Definition mptrac.h:4022
float wl[EX][EY][EP]
Vertical velocity on model levels [hPa/s].
Definition mptrac.h:4058
float vl[EX][EY][EP]
Meridional wind on model levels [m/s].
Definition mptrac.h:4055
float u[EX][EY][EP]
Zonal wind [m/s].
Definition mptrac.h:4016
float ul[EX][EY][EP]
Zonal wind on model levels [m/s].
Definition mptrac.h:4052
float v[EX][EY][EP]
Meridional wind [m/s].
Definition mptrac.h:4019
float pl[EX][EY][EP]
Pressure on model levels [hPa].
Definition mptrac.h:4049
float zetal[EX][EY][EP]
Zeta on model levels [K].
Definition mptrac.h:4061
Here is the call graph for this function:

◆ module_advect_init()

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.

This function initializes the advection module, setting up the air parcel pressure to be consistent with the given zeta vertical coordinate. It utilizes meteorological data from two time steps and interpolates the pressure values accordingly.

Parameters
ctlPointer to the control structure containing configuration flags.
cachePointer to the cache structure for temporary data and random numbers.
met0Pointer to the initial meteorological data structure.
met1Pointer to the final meteorological data structure.
atmPointer to the air parcel data structure.

The function performs the following operations:

  • Sets up a timer labeled "MODULE_ADVECT_INIT" within the "PHYSICS" category.
  • If ctl->advect_vert_coord == 1, it initializes the pressure fields to be consistent with the zeta coordinate using 4D interpolation.
  • Initial particle times must be identical; mptrac_init checks this once.
Author
Jan Clemens

Definition at line 4060 of file mptrac.c.

4065 {
4066
4067 /* Check parameters... */
4068 if (ctl->advect_vert_coord != 1)
4069 return;
4070
4071 /* Set timer... */
4072 SELECT_TIMER("MODULE_ADVECT_INIT", "PHYSICS");
4073
4074 /* Loop over particles... */
4075 PARTICLE_LOOP(0, atm->np, 0, "acc data present(ctl,met0,met1,atm)") {
4076
4077 /* Initialize pressure consistent with zeta... */
4079 intpol_met_4d_zeta(met0, met0->zetal, met0->pl, met1, met1->zetal,
4080 met1->pl, atm->time[ip], atm->q[ctl->qnt_zeta][ip],
4081 atm->lon[ip], atm->lat[ip], &atm->p[ip], ci, cw, 1);
4082 }
4083}
Here is the call graph for this function:

◆ module_bound_cond()

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.

This function applies boundary conditions to particles based on specified criteria, including latitude, pressure, surface layer parameters, and climatological data. It loops over each particle and checks whether it satisfies the specified boundary conditions. If a particle satisfies the conditions, its properties such as mass, volume mixing ratio, and age of air are updated accordingly.

It checks for quantity flags to determine which properties need to be updated. If the latitude or pressure of a particle falls outside the specified ranges, it skips the particle. It also considers surface layer parameters such as surface pressure, height, zeta range, and planetary boundary layer. If a particle is within the specified surface layer boundaries, its properties are updated accordingly.

The function updates properties such as mass and volume mixing ratio if the corresponding flags are set. It retrieves volume mixing ratio values for various trace gases (e.g., CFC-10, CFC-11, N2O, SF6) from climatological time series data and updates the particle properties accordingly. Additionally, it updates the age of air for each particle based on the current simulation time.

Parameters
ctlPointer to the control structure containing simulation parameters.
cachePointer to the cache structure for temporary data and random numbers.
climPointer to the climatological data structure containing time series data.
met0Pointer to the meteorological data structure at the initial time step.
met1Pointer to the meteorological data structure at the next time step.
atmPointer to the atmospheric data structure containing particle information.
Author
Lars Hoffmann
Mingzhao Liu

Definition at line 4087 of file mptrac.c.

4093 {
4094
4095 /* Set timer... */
4096 SELECT_TIMER("MODULE_BOUND_COND", "PHYSICS");
4097
4098 /* Check quantity flags... */
4099 if (ctl->qnt_m < 0 && ctl->qnt_vmr < 0 && ctl->qnt_Cccl4
4100 && ctl->qnt_Cccl3f < 0 && ctl->qnt_Cccl2f2 < 0
4101 && ctl->qnt_Cn2o < 0 && ctl->qnt_Csf6 < 0 && ctl->qnt_aoa < 0)
4102 return;
4103
4104 /* Loop over particles... */
4105 PARTICLE_LOOP(0, atm->np, 1,
4106 "acc data present(ctl,cache,clim,met0,met1,atm)") {
4107
4108 /* Check latitude and pressure range... */
4109 if (atm->lat[ip] < ctl->bound_lat0 || atm->lat[ip] > ctl->bound_lat1
4110 || atm->p[ip] > ctl->bound_p0 || atm->p[ip] < ctl->bound_p1)
4111 continue;
4112
4113 /* Check surface layer... */
4114 if (ctl->bound_dps > 0 || ctl->bound_dzs > 0
4115 || ctl->bound_zetas > 0 || ctl->bound_pbl) {
4116
4117 /* Get surface pressure... */
4118 double ps;
4120 INTPOL_2D(ps, 1);
4121
4122 /* Check pressure... */
4123 if (ctl->bound_dps > 0 && atm->p[ip] < ps - ctl->bound_dps)
4124 continue;
4125
4126 /* Check height... */
4127 if (ctl->bound_dzs > 0 && Z(atm->p[ip]) > Z(ps) + ctl->bound_dzs)
4128 continue;
4129
4130 /* Check zeta range... */
4131 if (ctl->bound_zetas > 0) {
4132 double t;
4133 INTPOL_3D(t, 1);
4134 if (ZETA(ps, atm->p[ip], t) > ctl->bound_zetas)
4135 continue;
4136 }
4137
4138 /* Check planetary boundary layer... */
4139 if (ctl->bound_pbl) {
4140 double pbl;
4141 INTPOL_2D(pbl, 0);
4142 if (atm->p[ip] < pbl)
4143 continue;
4144 }
4145 }
4146
4147 /* Set mass and volume mixing ratio... */
4148 if (ctl->qnt_m >= 0 && ctl->bound_mass >= 0)
4149 atm->q[ctl->qnt_m][ip] =
4150 ctl->bound_mass + ctl->bound_mass_trend * atm->time[ip];
4151 if (ctl->qnt_vmr >= 0 && ctl->bound_vmr >= 0)
4152 atm->q[ctl->qnt_vmr][ip] =
4153 ctl->bound_vmr + ctl->bound_vmr_trend * atm->time[ip];
4154
4155 /* Set CFC-10 volume mixing ratio... */
4156 if (ctl->qnt_Cccl4 >= 0 && ctl->clim_ccl4_timeseries[0] != '-')
4157 atm->q[ctl->qnt_Cccl4][ip] = clim_ts(&clim->ccl4, atm->time[ip]);
4158
4159 /* Set CFC-11 volume mixing ratio... */
4160 if (ctl->qnt_Cccl3f >= 0 && ctl->clim_ccl3f_timeseries[0] != '-')
4161 atm->q[ctl->qnt_Cccl3f][ip] = clim_ts(&clim->ccl3f, atm->time[ip]);
4162
4163 /* Set CFC-12 volume mixing ratio... */
4164 if (ctl->qnt_Cccl2f2 >= 0 && ctl->clim_ccl2f2_timeseries[0] != '-')
4165 atm->q[ctl->qnt_Cccl2f2][ip] = clim_ts(&clim->ccl2f2, atm->time[ip]);
4166
4167 /* Set N2O volume mixing ratio... */
4168 if (ctl->qnt_Cn2o >= 0 && ctl->clim_n2o_timeseries[0] != '-')
4169 atm->q[ctl->qnt_Cn2o][ip] = clim_ts(&clim->n2o, atm->time[ip]);
4170
4171 /* Set SF6 volume mixing ratio... */
4172 if (ctl->qnt_Csf6 >= 0 && ctl->clim_sf6_timeseries[0] != '-')
4173 atm->q[ctl->qnt_Csf6][ip] = clim_ts(&clim->sf6, atm->time[ip]);
4174
4175 /* Set age of air... */
4176 if (ctl->qnt_aoa >= 0)
4177 atm->q[ctl->qnt_aoa][ip] = atm->time[ip];
4178 }
4179}
double clim_ts(const clim_ts_t *ts, const double t)
Interpolates a time series of climatological variables.
Definition mptrac.c:398
#define INTPOL_3D(var, init)
Perform 3D interpolation for a meteorological variable.
Definition mptrac.h:1214
#define ZETA(ps, p, t)
Computes the value of the zeta vertical coordinate.
Definition mptrac.h:2313
#define INTPOL_2D(var, init)
Perform 2D interpolation for a meteorological variable.
Definition mptrac.h:1197
clim_ts_t ccl2f2
CFC-12 time series.
Definition mptrac.h:3879
clim_ts_t sf6
SF6 time series.
Definition mptrac.h:3885
clim_ts_t ccl4
CFC-10 time series.
Definition mptrac.h:3873
clim_ts_t ccl3f
CFC-11 time series.
Definition mptrac.h:3876
clim_ts_t n2o
N2O time series.
Definition mptrac.h:3882
int qnt_Cccl2f2
Quantity array index for CFC-12 volume mixing ratio (chemistry code).
Definition mptrac.h:2812
int qnt_m
Quantity array index for mass.
Definition mptrac.h:2545
int qnt_aoa
Quantity array index for age of air.
Definition mptrac.h:2821
char clim_n2o_timeseries[LEN]
Filename of N2O time series.
Definition mptrac.h:3188
double bound_dzs
Boundary conditions surface layer depth [km].
Definition mptrac.h:3137
int qnt_Cccl4
Quantity array index for CFC-10 volume mixing ratio (chemistry code).
Definition mptrac.h:2806
double bound_mass
Boundary conditions mass per particle [kg].
Definition mptrac.h:3110
int qnt_vmr
Quantity array index for volume mixing ratio.
Definition mptrac.h:2548
double bound_lat1
Boundary conditions maximum longitude [deg].
Definition mptrac.h:3125
int bound_pbl
Boundary conditions planetary boundary layer (0=no, 1=yes).
Definition mptrac.h:3143
double bound_p1
Boundary conditions top pressure [hPa].
Definition mptrac.h:3131
double bound_vmr
Boundary conditions volume mixing ratio [ppv].
Definition mptrac.h:3116
double bound_lat0
Boundary conditions minimum longitude [deg].
Definition mptrac.h:3122
double bound_vmr_trend
Boundary conditions volume mixing ratio trend [ppv/s].
Definition mptrac.h:3119
int qnt_Cn2o
Quantity array index for N2O volume mixing ratio (chemistry code).
Definition mptrac.h:2815
int qnt_Cccl3f
Quantity array index for CFC-11 volume mixing ratio (chemistry code).
Definition mptrac.h:2809
int qnt_Csf6
Quantity array index for SF6 volume mixing ratio (chemistry code).
Definition mptrac.h:2818
double bound_dps
Boundary conditions surface layer depth [hPa].
Definition mptrac.h:3134
double bound_mass_trend
Boundary conditions mass per particle trend [kg/s].
Definition mptrac.h:3113
double bound_p0
Boundary conditions bottom pressure [hPa].
Definition mptrac.h:3128
char clim_ccl4_timeseries[LEN]
Filename of CFC-10 time series.
Definition mptrac.h:3179
char clim_sf6_timeseries[LEN]
Filename of SF6 time series.
Definition mptrac.h:3191
char clim_ccl3f_timeseries[LEN]
Filename of CFC-11 time series.
Definition mptrac.h:3182
char clim_ccl2f2_timeseries[LEN]
Filename of CFC-12 time series.
Definition mptrac.h:3185
double bound_zetas
Boundary conditions surface layer zeta [K].
Definition mptrac.h:3140
Here is the call graph for this function:

◆ module_chem_grid()

void module_chem_grid ( const ctl_t * ctl,
met_t * met0,
met_t * met1,
atm_t * atm,
const double t )

Computes gridded chemical tracer concentrations (volume mixing ratio) from individual air parcel mass data and assigns them back to the parcels.

This function aggregates the mass of tracer particles onto a 3D chemical grid (longitude × latitude × altitude), accounting for either single or ensemble simulations depending on ctl->nens. It then interpolates meteorological temperature fields and computes volume mixing ratios, storing them in the specified tracer quantity (e.g., ctl->qnt_Cx).

If the molar mass is undefined or required quantity indices are missing, the function exits early.

Parallelization is supported via OpenMP or OpenACC.

Parameters
[in]ctlPointer to the control structure containing configuration parameters, including grid dimensions, tracer indices, and simulation mode.
[in]met0Pointer to the meteorological data at the beginning of the interpolation interval.
[in]met1Pointer to the meteorological data at the end of the interpolation interval.
[in,out]atmPointer to the atmospheric state, including parcel coordinates, time, mass, and output tracer fields.
[in]tCentral time step used for output and interpolation.
Note
  • Requires ctl->molmass > 0, and ctl->qnt_m and ctl->qnt_Cx to be set.
  • Uses ensemble mode if ctl->nens > 0 and assigns each parcel to its ensemble member via ctl->qnt_ens.
  • Grid box volume mixing ratios are computed assuming ideal gas law and a layered spherical grid.
  • The output quantity (e.g., qnt_Cx) is given in ppbv.
See also
intpol_met_time_3d()
P(), Z(), RHO()
ARRAY_3D macro
Author
Mingzhao Liu
Lars Hoffmann

Definition at line 4183 of file mptrac.c.

4188 {
4189
4190 if (met0->coord_type != 0)
4191 ERRMSG("Only lat/lon grid supported");
4192
4193 /* Check quantities... */
4194 if (ctl->qnt_m < 0 || ctl->qnt_Cx < 0)
4195 return;
4196 if (ctl->molmass <= 0)
4197 ERRMSG("Molar mass is not defined!");
4198
4199 /* Set timer... */
4200 SELECT_TIMER("MODULE_CHEM_GRID", "PHYSICS");
4201
4202 /* Allocate... */
4203 const int ensemble_mode = (ctl->nens > 0);
4204 const int np = atm->np;
4205 const int nz = ctl->chemgrid_nz;
4206 const int nx = ctl->chemgrid_nx;
4207 const int ny = ctl->chemgrid_ny;
4208 const int ngrid = nx * ny * nz;
4209 const int nens = ensemble_mode ? ctl->nens : 1;
4210
4211 double *restrict const z = (double *) malloc((size_t) nz * sizeof(double));
4212 double *restrict const press =
4213 (double *) malloc((size_t) nz * sizeof(double));
4214 double *restrict const mass =
4215 (double *) calloc((size_t) ngrid * (size_t) nens, sizeof(double));
4216 double *restrict const area =
4217 (double *) malloc((size_t) ny * sizeof(double));
4218 double *restrict const lon =
4219 (double *) malloc((size_t) nx * sizeof(double));
4220 double *restrict const lat =
4221 (double *) malloc((size_t) ny * sizeof(double));
4222
4223 int *restrict const ixs = (int *) malloc((size_t) np * sizeof(int));
4224 int *restrict const iys = (int *) malloc((size_t) np * sizeof(int));
4225 int *restrict const izs = (int *) malloc((size_t) np * sizeof(int));
4226
4227 /* Set grid box size... */
4228 const double dz = (ctl->chemgrid_z1 - ctl->chemgrid_z0) / nz;
4229 const double dlon = (ctl->chemgrid_lon1 - ctl->chemgrid_lon0) / nx;
4230 const double dlat = (ctl->chemgrid_lat1 - ctl->chemgrid_lat0) / ny;
4231
4232 /* Set vertical coordinates... */
4233#ifdef _OPENACC
4234#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])
4235#pragma acc data present(ctl,met0,met1,atm,ixs,iys,izs,z,press,mass,area,lon,lat)
4236#pragma acc parallel loop independent gang vector
4237#else
4238#pragma omp parallel for default(shared)
4239#endif
4240 for (int iz = 0; iz < nz; iz++) {
4241 z[iz] = ctl->chemgrid_z0 + dz * (iz + 0.5);
4242 press[iz] = P(z[iz]);
4243 }
4244
4245 /* Initialize mass grid... */
4246#ifdef _OPENACC
4247#pragma acc parallel loop independent gang vector
4248#endif
4249 for (int i = 0; i < ngrid * nens; i++)
4250 mass[i] = 0;
4251
4252 /* Set time interval for output... */
4253 const double t0 = tt - 0.5 * ctl->dt_mod;
4254 const double t1 = tt + 0.5 * ctl->dt_mod;
4255
4256 /* Get indices... */
4257#ifdef _OPENACC
4258#pragma acc parallel loop independent gang vector
4259#else
4260#pragma omp parallel for default(shared)
4261#endif
4262 for (int ip = 0; ip < np; ip++) {
4263 const double zpart = Z(atm->p[ip]);
4264 if (atm->time[ip] < t0 || atm->time[ip] > t1
4265 || atm->lon[ip] < ctl->chemgrid_lon0
4266 || atm->lon[ip] >= ctl->chemgrid_lon1
4267 || atm->lat[ip] < ctl->chemgrid_lat0
4268 || atm->lat[ip] >= ctl->chemgrid_lat1
4269 || zpart < ctl->chemgrid_z0 || zpart >= ctl->chemgrid_z1) {
4270 izs[ip] = -1;
4271 continue;
4272 }
4273 ixs[ip] = (int) ((atm->lon[ip] - ctl->chemgrid_lon0) / dlon);
4274 iys[ip] = (int) ((atm->lat[ip] - ctl->chemgrid_lat0) / dlat);
4275 izs[ip] = (int) ((zpart - ctl->chemgrid_z0) / dz);
4276 if (ixs[ip] >= nx || iys[ip] >= ny || izs[ip] >= nz)
4277 izs[ip] = -1;
4278 }
4279
4280 /* Set horizontal coordinates... */
4281#ifdef _OPENACC
4282#pragma acc parallel loop independent gang vector
4283#else
4284#pragma omp parallel for default(shared)
4285#endif
4286 for (int ix = 0; ix < nx; ix++)
4287 lon[ix] = ctl->chemgrid_lon0 + dlon * (ix + 0.5);
4288
4289#ifdef _OPENACC
4290#pragma acc parallel loop independent gang vector
4291#else
4292#pragma omp parallel for default(shared)
4293#endif
4294 for (int iy = 0; iy < ny; iy++) {
4295 lat[iy] = ctl->chemgrid_lat0 + dlat * (iy + 0.5);
4296 area[iy] = dlat * dlon * SQR(RE * M_PI / 180.) * cos(DEG2RAD(lat[iy]));
4297 }
4298
4299 /* Get mass per grid box... */
4300#ifdef _OPENACC
4301#pragma acc parallel loop independent gang vector
4302#endif
4303 for (int ip = 0; ip < np; ip++) {
4304 if (izs[ip] >= 0) {
4305 int mass_idx = ARRAY_3D(ixs[ip], iys[ip], ny, izs[ip], nz);
4306 if (ensemble_mode) {
4307 const int ens = (int) atm->q[ctl->qnt_ens][ip];
4308 mass_idx += ens * ngrid;
4309 }
4310#ifdef _OPENACC
4311#pragma acc atomic update
4312#endif
4313 mass[mass_idx] += atm->q[ctl->qnt_m][ip];
4314 }
4315 }
4316
4317 /* Assign grid data to air parcels ... */
4318#ifdef _OPENACC
4319#pragma acc parallel loop independent gang vector
4320#else
4321#pragma omp parallel for default(shared)
4322#endif
4323 for (int ip = 0; ip < np; ip++)
4324 if (izs[ip] >= 0) {
4325
4326 /* Interpolate temperature... */
4327 double temp;
4329 intpol_met_time_3d(met0, met0->t, met1, met1->t, tt,
4330 press[izs[ip]],
4331 lon[ixs[ip]], lat[iys[ip]], &temp, ci, cw, 1);
4332
4333 /* Set mass... */
4334 int mass_idx = ARRAY_3D(ixs[ip], iys[ip], ny, izs[ip], nz);
4335 if (ensemble_mode) {
4336 const int ens = (int) atm->q[ctl->qnt_ens][ip];
4337 mass_idx += ens * ngrid;
4338 }
4339
4340 /* Calculate volume mixing ratio... */
4341 const double m = mass[mass_idx];
4342 atm->q[ctl->qnt_Cx][ip] = MA / ctl->molmass * m
4343 / (RHO(press[izs[ip]], temp) * area[iys[ip]] * dz * 1e9);
4344 }
4345
4346 /* Free... */
4347#ifdef _OPENACC
4348#pragma acc exit data delete(ixs,iys,izs,z,press,mass,area,lon,lat)
4349#endif
4350 free(mass);
4351 free(lon);
4352 free(lat);
4353 free(area);
4354 free(z);
4355 free(press);
4356 free(ixs);
4357 free(iys);
4358 free(izs);
4359}
#define ARRAY_3D(ix, iy, ny, iz, nz)
Compute the linear index of a 3D array element.
Definition mptrac.h:711
#define MA
Molar mass of dry air [g/mol].
Definition mptrac.h:290
#define P(z)
Compute pressure at given altitude.
Definition mptrac.h:1804
#define RHO(p, t)
Compute density of air.
Definition mptrac.h:1981
double molmass
Molar mass [g/mol].
Definition mptrac.h:3149
double chemgrid_z1
Upper altitude of chemistry grid [km].
Definition mptrac.h:3236
double chemgrid_z0
Lower altitude of chemistry grid [km].
Definition mptrac.h:3233
double chemgrid_lat0
Lower latitude of chemistry grid [deg].
Definition mptrac.h:3251
double chemgrid_lat1
Upper latitude of chemistry grid [deg].
Definition mptrac.h:3254
double chemgrid_lon0
Lower longitude of chemistry grid [deg].
Definition mptrac.h:3242
double chemgrid_lon1
Upper longitude of chemistry grid [deg].
Definition mptrac.h:3245
double dt_mod
Time step of simulation [s].
Definition mptrac.h:2857
int nens
Number of ensembles.
Definition mptrac.h:3423
int chemgrid_nz
Number of altitudes of chemistry grid.
Definition mptrac.h:3230
int chemgrid_nx
Number of longitudes of chemistry grid.
Definition mptrac.h:3239
int chemgrid_ny
Number of latitudes of chemistry grid.
Definition mptrac.h:3248
int qnt_ens
Quantity array index for ensemble IDs.
Definition mptrac.h:2539
int qnt_Cx
Quantity array index for trace species x volume mixing ratio (chemistry code).
Definition mptrac.h:2776
Here is the call graph for this function:

◆ module_chem_init()

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.

This function initializes various chemical components of the atmosphere using meteorological data and climatological information. It interpolates and sets values for water vapor (H2O), ozone (O3), and several radical species such as OH, HO2, H2O2, and O1D for each air parcel.

Parameters
ctlPointer to the control structure containing quantity flags.
cachePointer to the cache structure for temporary data and random numbers.
climPointer to the climatology structure containing climatological data.
met0Pointer to the initial meteorological data structure.
met1Pointer to the final meteorological data structure.
atmPointer to the air parcel data structure.

The function uses OpenMP for parallel processing, iterating over each point in the atmosphere (atm->np) to initialize chemical species concentrations. It performs the following steps:

  • Interpolates H2O and O3 data from meteorological input if the respective quantity flags (ctl->qnt_Ch2o and ctl->qnt_Co3) are set.
  • Sets the concentrations of OH, HO2, H2O2, and O1D using climatological data if the respective quantity flags are set.
Author
Mingzhao Liu

Definition at line 4363 of file mptrac.c.

4369 {
4370
4371 /* Set timer... */
4372 SELECT_TIMER("MODULE_CHEM_INIT", "PHYSICS");
4373
4374 /* Loop over particles... */
4375 PARTICLE_LOOP(0, atm->np, 0,
4376 "acc data present(ctl,cache,clim,met0,met1,atm)") {
4377
4378 /* Set H2O and O3 using meteo data... */
4380 if (ctl->qnt_Ch2o >= 0) {
4381 double h2o;
4382 INTPOL_3D(h2o, 1);
4383 SET_ATM(qnt_Ch2o, h2o);
4384 }
4385 if (ctl->qnt_Co3 >= 0) {
4386 double o3;
4387 INTPOL_3D(o3, 1);
4388 SET_ATM(qnt_Co3, o3);
4389 }
4390
4391 /* Set radical species... */
4392 const double lat_ref =
4393 ctl->met_coord_type == 0 ? atm->lat[ip] : ctl->met_utm_ref_lat;
4394 SET_ATM(qnt_Coh, clim_oh(ctl, clim, atm->time[ip],
4395 atm->lon[ip], atm->lat[ip], atm->p[ip]));
4396 SET_ATM(qnt_Cho2, clim_zm(&clim->ho2, atm->time[ip],
4397 lat_ref, atm->p[ip]));
4398 SET_ATM(qnt_Ch2o2, clim_zm(&clim->h2o2, atm->time[ip],
4399 lat_ref, atm->p[ip]));
4400 SET_ATM(qnt_Co1d, clim_zm(&clim->o1d, atm->time[ip],
4401 lat_ref, atm->p[ip]));
4402 }
4403}
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
#define SET_ATM(qnt, val)
Set atmospheric quantity value.
Definition mptrac.h:2000
clim_zm_t ho2
HO2 zonal means.
Definition mptrac.h:3867
clim_zm_t o1d
O(1D) zonal means.
Definition mptrac.h:3870
clim_zm_t h2o2
H2O2 zonal means.
Definition mptrac.h:3864
int qnt_Ch2o
Quantity array index for H2O volume mixing ratio (chemistry code).
Definition mptrac.h:2779
int qnt_Co3
Quantity array index for O3 volume mixing ratio (chemistry code).
Definition mptrac.h:2782
Here is the call graph for this function:

◆ module_convection()

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.

Randomly redistributes particle pressure within a PBL or CAPE/EL column. A positive CIN threshold additionally limits ECP to columns with CIN at or below that threshold. ECP requires a finite EL pressure above the surface; PBL-only redistribution does not require EL. Particles with zero individual timestep are skipped. Particle number and tracer mass are not directly changed.

Parameters
[in]ctlPointer to the control structure with simulation settings.
[in,out]cachePointer to the cache structure for temporary data and random numbers.
[in]met0Pointer to the earlier meteorological data.
[in]met1Pointer to the later meteorological data.
[in,out]atmPointer to the atmospheric data structure with particle properties.
Author
Lars Hoffmann

Definition at line 4407 of file mptrac.c.

4412 {
4413
4414 /* Set timer... */
4415 SELECT_TIMER("MODULE_CONVECTION", "PHYSICS");
4416
4417 /* Create random numbers... */
4418 module_rng(ctl, cache->rs, (size_t) atm->np, 0);
4419
4420 /* Loop over particles... */
4421 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
4422
4423 /* Interpolate surface pressure... */
4424 double ps;
4426 INTPOL_2D(ps, 1);
4427
4428 /* Initialize pressure range for vertical mixing... */
4429 double pbot = ps, ptop = ps;
4430
4431 /* Mixing in the PBL... */
4432 if (ctl->conv_mix_pbl) {
4433
4434 /* Interpolate PBL... */
4435 double pbl;
4436 INTPOL_2D(pbl, 0);
4437
4438 /* Set pressure range... */
4439 ptop = pbl - ctl->conv_pbl_trans * (ps - pbl);
4440 }
4441
4442 /* Convective mixing... */
4443 if (ctl->conv_cape >= 0) {
4444
4445 /* Interpolate CAPE, CIN, and equilibrium level... */
4446 double cape, cin, pel;
4447 INTPOL_2D(cape, 0);
4448 INTPOL_2D(cin, 0);
4449 INTPOL_2D(pel, 0);
4450
4451 /* Set pressure range... */
4452 if (isfinite(cape) && cape >= ctl->conv_cape
4453 && (ctl->conv_cin <= 0 || (isfinite(cin) && cin <= ctl->conv_cin))
4454 && isfinite(pel) && pel > 0 && pel < ps)
4455 ptop = MIN(ptop, pel);
4456 }
4457
4458 /* Apply vertical mixing... */
4459 if (ptop != pbot && atm->p[ip] >= ptop) {
4460
4461 /* Get density range... */
4462 double tbot, ttop;
4463 intpol_met_time_3d(met0, met0->t, met1, met1->t, atm->time[ip],
4464 pbot, atm->lon[ip], atm->lat[ip], &tbot, ci, cw, 1);
4465 intpol_met_time_3d(met0, met0->t, met1, met1->t, atm->time[ip], ptop,
4466 atm->lon[ip], atm->lat[ip], &ttop, ci, cw, 1);
4467 const double rhobot = pbot / tbot;
4468 const double rhotop = ptop / ttop;
4469
4470 /* Get new density... */
4471 const double rho = rhobot + (rhotop - rhobot) * cache->rs[ip];
4472
4473 /* Get pressure... */
4474 atm->p[ip] = LIN(rhobot, pbot, rhotop, ptop, rho);
4475 }
4476 }
4477}
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:6112
double rs[3 *NP+1]
Random numbers.
Definition mptrac.h:3688
double conv_cape
CAPE threshold for convection module [J/kg].
Definition mptrac.h:3101
double conv_pbl_trans
Depth of PBL transition layer (fraction of PBL pressure thickness).
Definition mptrac.h:3098
int conv_mix_pbl
Vertical mixing in the PBL (0=off, 1=on).
Definition mptrac.h:3095
double conv_cin
CIN threshold for convection module [J/kg].
Definition mptrac.h:3104
Here is the call graph for this function:

◆ module_dd()

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.

This routine redistributes atmospheric particles across MPI processes according to the current spatial domain decomposition so that each process owns the particles located inside its subdomain.

The routine performs the following steps:

  1. Determine the destination subdomain for each particle.
  2. With DD_EXCHANGE_MODE=0, use the original sort/push path; with mode 1, pack outgoing particles and compact retained particles without sorting.
  3. Convert particle data from the atmospheric structure-of-arrays representation into a temporary particle buffer (array-of-structures).
  4. Exchange particles between MPI ranks according to their destination rank.
  5. Convert the received particle data back into the atmospheric structure-of-arrays representation.

A temporary particle buffer is maintained locally and resized dynamically when necessary to hold outgoing or incoming particles. After the communication step, the atmospheric particle arrays are updated to contain only the particles owned by the current MPI rank.

Parameters
[in]tCurrent simulation time in seconds since 2000-01-01.
[in]ctlControl structure containing simulation settings and quantity indices.
[in,out]cacheCache structure used for temporary particle data during data layout transformations.
[in,out]ddDomain decomposition structure containing grid metadata and auxiliary buffers.
[in,out]atmAtmospheric particle state. Particle ownership and particle arrays are updated in place.
[in]metPointer to the meteorological data used for subdomain assignment and sorting.
Note
  • This routine is available only when MPTRAC is compiled with domain decomposition support (DD) and MPI enabled.
  • Particle exchange is performed using MPI collective communication.
Author
Jan Clemens
Lars Hoffmann

Definition at line 4482 of file mptrac.c.

4488 {
4489
4490 /* Initialize particles locally... */
4491 int npart = 0, nkeep = 0, capacity = 0;
4492 particle_t *particles = NULL;
4493
4494 /* Assign particles to new subdomains... */
4495 dd_assign_subdomains(ctl, dd, atm, 0);
4496
4497 if (ctl->dd_exchange_mode == 0) {
4498 /* Original sort/push exchange path... */
4499 if (fmod(t, ctl->dd_sort_dt) == 0)
4500 dd_sort(ctl, *met, cache, atm, dd, &npart);
4501 else
4502 dd_push(ctl, atm, cache, &npart);
4503 } else {
4504 /* Count outgoing particles for direct packing and compaction... */
4505 dd_count_outgoing(ctl, atm, dd, &nkeep, &npart);
4506 }
4507
4508 /* Ensure particle buffer is large enough for outgoing particles... */
4509 if (npart > capacity) {
4510 const int newcap = npart + npart / 2 + 1;
4511 particle_t *tmp =
4512 realloc(particles, (size_t) newcap * sizeof(particle_t));
4513 if (!tmp)
4514 ERRMSG("Out of memory!");
4515 particles = tmp;
4516 capacity = newcap;
4517 }
4518
4519 /* Transform from struct of array to array of struct... */
4520 if (ctl->dd_exchange_mode == 0)
4521 dd_atm2particles(ctl, cache, atm, particles, npart);
4522 else
4523 dd_pack_compact(ctl, cache, atm, dd, particles, nkeep, npart);
4524
4525 SELECT_TIMER("SYNC", "DD");
4526 MPI_Barrier(MPI_COMM_WORLD);
4527
4528 /* Perform the communication... */
4529 dd_communicate_particles(ctl, dd, &particles, &npart, &capacity);
4530
4531 /* Transform from array of struct to struct of array... */
4532 dd_particles2atm(ctl, cache, particles, npart, atm);
4533
4534 /* Free local particle array... */
4535 free(particles);
4536}
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:2657
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:2051
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:2450
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:2772
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 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:2200
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:2579
double dd_sort_dt
Sorting time interval for the compactification (DD_EXCHANGE_MODE=0).
Definition mptrac.h:3592
Here is the call graph for this function:

◆ module_decay()

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.

This function simulates decay processes for atmospheric particles based on their mass or volume mixing ratio. It loops over each particle and calculates the decay rate using weighting factors for tropospheric and stratospheric lifetimes. Exponential decay is then calculated, and the mass or volume mixing ratio of particles is updated accordingly. Loss rates can also be calculated and updated based on the decay process.

The function checks for quantity flags to ensure that mass or volume mixing ratio data is available. It then calculates the weighting factor based on the particle's location in the atmosphere and sets the lifetime accordingly. Exponential decay is calculated using the time step and the lifetime, and the particle's mass or volume mixing ratio is updated. Loss rates can also be updated based on the decay process.

Parameters
ctlPointer to the control structure containing simulation parameters.
cachePointer to the cache structure for temporary data and random numbers.
climPointer to the climate data structure containing atmospheric data.
atmPointer to the atmospheric data structure containing particle information.
Author
Lars Hoffmann

Definition at line 4541 of file mptrac.c.

4545 {
4546
4547 /* Set timer... */
4548 SELECT_TIMER("MODULE_DECAY", "PHYSICS");
4549
4550 /* Check quantity flags... */
4551 if (ctl->qnt_m < 0 && ctl->qnt_vmr < 0)
4552 ERRMSG("Module needs quantity mass or volume mixing ratio!");
4553
4554 /* Loop over particles... */
4555 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,clim,atm)") {
4556
4557 /* Get weighting factor... */
4558 const double w = tropo_weight(ctl, clim, atm, ip);
4559
4560 /* Set lifetime... */
4561 const double tdec = w * ctl->tdec_trop + (1 - w) * ctl->tdec_strat;
4562
4563 /* Calculate exponential decay... */
4564 const double aux = exp(-cache->dt[ip] / tdec);
4565 if (ctl->qnt_m >= 0) {
4566 if (ctl->qnt_mloss_decay >= 0)
4567 atm->q[ctl->qnt_mloss_decay][ip]
4568 += atm->q[ctl->qnt_m][ip] * (1 - aux);
4569 atm->q[ctl->qnt_m][ip] *= aux;
4570 if (ctl->qnt_loss_rate >= 0)
4571 atm->q[ctl->qnt_loss_rate][ip] += 1. / tdec;
4572 }
4573 if (ctl->qnt_vmr >= 0)
4574 atm->q[ctl->qnt_vmr][ip] *= aux;
4575 }
4576}
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:13646
int qnt_loss_rate
Quantity array index for total loss rate.
Definition mptrac.h:2704
int qnt_mloss_decay
Quantity array index for total mass loss due to exponential decay.
Definition mptrac.h:2701
double tdec_strat
Life time of particles in the stratosphere [s].
Definition mptrac.h:3155
double tdec_trop
Life time of particles in the troposphere [s].
Definition mptrac.h:3152
Here is the call graph for this function:

◆ module_diff_meso()

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.

This function simulates mesoscale diffusion for atmospheric particles, including horizontal and vertical wind fluctuations. It calculates standard deviations of local wind data and temporal correlations for mesoscale fluctuations. Mesoscale wind fluctuations are then calculated based on the provided random numbers and turbulence parameters. The particle positions are updated accordingly.

The function loops over each particle and calculates indices for interpolation of wind data. It then computes standard deviations of local wind data and temporal correlations for mesoscale fluctuations. Based on the turbulence parameters and provided random numbers, it calculates horizontal and vertical mesoscale wind fluctuations. Finally, it updates the particle positions based on the calculated wind fluctuations.

Parameters
ctlPointer to the control structure containing simulation parameters.
cachePointer to the cache structure for temporary data and random numbers.
met0Pointer to the meteorological data structure at the current time step.
met1Pointer to the meteorological data structure at the next time step.
atmPointer to the atmospheric data structure containing particle information.
Note
Control parameters TURB_MESOX and TURB_MESOZ define the subgrid-scale variability as a fraction of the grid-scale variance. Stohl et al. (2005) recommend a default value of 0.16 for both parameters, providing a standard approach for turbulence representation. However, recent findings by Bakels et al. (2024) suggest disabling this approach to improve model accuracy under certain conditions. It is advised to evaluate the applicability of these recommendations based on the specific simulation context and objectives.
Author
Lars Hoffmann

Definition at line 4580 of file mptrac.c.

4585 {
4586
4587 /* Set timer... */
4588 SELECT_TIMER("MODULE_DIFF_MESO", "PHYSICS");
4589
4590 /* Create random numbers... */
4591 module_rng(ctl, cache->rs, 3 * (size_t) atm->np, 1);
4592
4593 /* Loop over particles... */
4594 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
4595
4596 /* Get indices... */
4597 const int ix = locate_reg(met0->lon, met0->nx, atm->lon[ip]);
4598 const int iy = locate_irr(met0->lat, met0->ny, atm->lat[ip]);
4599 const int iz = locate_irr(met0->p, met0->np, atm->p[ip]);
4600
4601 /* Get standard deviations of local wind data... */
4602 float umean = 0, usig = 0, vmean = 0, vsig = 0, wmean = 0, wsig = 0;
4603 for (int i = 0; i < 2; i++)
4604 for (int j = 0; j < 2; j++)
4605 for (int k = 0; k < 2; k++) {
4606 umean += met0->u[ix + i][iy + j][iz + k];
4607 usig += SQR(met0->u[ix + i][iy + j][iz + k]);
4608 vmean += met0->v[ix + i][iy + j][iz + k];
4609 vsig += SQR(met0->v[ix + i][iy + j][iz + k]);
4610 wmean += met0->w[ix + i][iy + j][iz + k];
4611 wsig += SQR(met0->w[ix + i][iy + j][iz + k]);
4612
4613 umean += met1->u[ix + i][iy + j][iz + k];
4614 usig += SQR(met1->u[ix + i][iy + j][iz + k]);
4615 vmean += met1->v[ix + i][iy + j][iz + k];
4616 vsig += SQR(met1->v[ix + i][iy + j][iz + k]);
4617 wmean += met1->w[ix + i][iy + j][iz + k];
4618 wsig += SQR(met1->w[ix + i][iy + j][iz + k]);
4619 }
4620 usig = usig / 16.f - SQR(umean / 16.f);
4621 usig = (usig > 0 ? sqrtf(usig) : 0);
4622 vsig = vsig / 16.f - SQR(vmean / 16.f);
4623 vsig = (vsig > 0 ? sqrtf(vsig) : 0);
4624 wsig = wsig / 16.f - SQR(wmean / 16.f);
4625 wsig = (wsig > 0 ? sqrtf(wsig) : 0);
4626
4627 /* Set temporal correlations for mesoscale fluctuations... */
4628 const double r = 1 - 2 * fabs(cache->dt[ip]) / ctl->dt_met;
4629 const double r2 = sqrt(1 - r * r);
4630
4631 /* Calculate horizontal mesoscale wind fluctuations... */
4632 if (ctl->turb_mesox > 0) {
4633 cache->uvwp[ip][0] =
4634 (float) (r * cache->uvwp[ip][0] +
4635 r2 * cache->rs[3 * ip] * ctl->turb_mesox * usig);
4636 atm->lon[ip] +=
4637 DX2COORD(met0, cache->uvwp[ip][0] * cache->dt[ip], atm->lat[ip]);
4638
4639 cache->uvwp[ip][1] =
4640 (float) (r * cache->uvwp[ip][1] +
4641 r2 * cache->rs[3 * ip + 1] * ctl->turb_mesox * vsig);
4642 atm->lat[ip] += DY2COORD(met0, cache->uvwp[ip][1] * cache->dt[ip]);
4643 }
4644
4645 /* Calculate vertical mesoscale wind fluctuations... */
4646 if (ctl->turb_mesoz > 0) {
4647 cache->uvwp[ip][2] =
4648 (float) (r * cache->uvwp[ip][2] +
4649 r2 * cache->rs[3 * ip + 2] * ctl->turb_mesoz * wsig);
4650 atm->p[ip] += cache->uvwp[ip][2] * cache->dt[ip];
4651 }
4652 }
4653}
double turb_mesoz
Vertical scaling factor for mesoscale wind fluctuations.
Definition mptrac.h:3089
double dt_met
Time step of meteo data [s].
Definition mptrac.h:2867
double turb_mesox
Horizontal scaling factor for mesoscale wind fluctuations.
Definition mptrac.h:3086
Here is the call graph for this function:

◆ module_diff_pbl()

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).

This function handles the effects of turbulence on particles within the PBL. It calculates turbulent velocity variances, Lagrangian timescales, and updates particle positions and perturbations based on random fluctuations and boundary layer physics. This module adapts the approach of Ryall and Maryon (1998) and Stohl et al. (2005).

Parameters
ctlPointer to the control structure containing model settings.
cachePointer to the cache structure for temporary data and random numbers.
met0Pointer to the meteorological data structure for the current timestep.
met1Pointer to the meteorological data structure for the next timestep.
atmPointer to the atmospheric data structure containing particle states.

The function:

  • Allocates memory for random numbers and generates them using module_rng.
  • Loops over all particles to compute their behavior within the boundary layer.
  • Handles both stable/neutral and unstable conditions based on the surface sensible heat flux.
  • Calculates turbulent velocity variances (sig_u, sig_w), their vertical derivatives, and Lagrangian timescales (tau_u, tau_w).
  • Updates particle velocity perturbations and positions using turbulent diffusion equations.

The function uses OpenACC directives for GPU acceleration.

Note
The function handles edge cases like zero diffusivity and imposes minimum limits on calculated values to ensure stability.
Warning
Ensure that all input pointers are properly initialized and accessible before calling this function.
Author
Lars Hoffmann

Definition at line 4657 of file mptrac.c.

4662 {
4663
4664 /* Set timer... */
4665 SELECT_TIMER("MODULE_DIFF_PBL", "PHYSICS");
4666
4667 /* Create random numbers... */
4668 module_rng(ctl, cache->rs, 3 * (size_t) atm->np, 1);
4669
4670 /* Loop over particles... */
4671 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
4672
4673 double pbl, ps, dsigw_dz = 0.0, sig_u = 0.0, sig_v = 0.0, sig_w = 0.0;
4674 double tau_u = 0.0, tau_v = 0.0, tau_w = 0.0;
4675
4676 /* Get PBL pressure... */
4678 INTPOL_2D(pbl, 1);
4679
4680 /* Let the background diffusion scheme handle particles above the PBL. */
4681 if (atm->p[ip] < pbl)
4682 continue;
4683
4684 /* Get surface pressure... */
4685 INTPOL_2D(ps, 0);
4686
4687 /* Skip invalid or vanishing PBL layers. */
4688 if (!(ps > 0.0 && pbl > 0.0 && ps > pbl))
4689 continue;
4690
4691 /* Calculate heights [m] above ground.
4692 Z() returns altitude in km, so multiply differences by 1e3. */
4693 const double p = MIN(atm->p[ip], ps);
4694 const double zs = Z(ps);
4695 const double z_raw = 1e3 * (Z(p) - zs);
4696 const double zi = 1e3 * (Z(pbl) - zs);
4697
4698 /* Require a physically meaningful PBL depth. */
4699 if (!(zi > 1.0))
4700 continue;
4701
4702 /* Clamp height to the PBL interval for closure evaluation. */
4703 const double z = CLAMP(z_raw, 0.0, zi);
4704 const double zeta = CLAMP(z / zi, 1e-6, 1.0 - 1e-6);
4705 const double z_m = MAX(z, 1.0);
4706
4707 /* Temporarily use clamped pressure for thermodynamic interpolation.
4708 This avoids inconsistent interpolation if a particle has slipped
4709 below the surface pressure. */
4710 const double p_save = atm->p[ip];
4711 atm->p[ip] = p;
4712
4713 /* Calculate friction velocity... */
4714 double ess, nss, h2o, t;
4715 INTPOL_2D(ess, 0);
4716 INTPOL_2D(nss, 0);
4717 INTPOL_3D(t, 1);
4718 INTPOL_3D(h2o, 0);
4719
4720 /* Restore particle pressure before any continue/update path. */
4721 atm->p[ip] = p_save;
4722
4723 const double tv = TVIRT(t, h2o);
4724 const double thetav = THETAVIRT(p, t, h2o);
4725 const double rho = RHO(p, tv);
4726 const double tau = sqrt(SQR(ess) + SQR(nss));
4727
4728 if (!(rho > 0.0))
4729 continue;
4730
4731 const double ustar = sqrt(MAX(tau / rho, 0.0));
4732 const double ust = MAX(1e-4, ustar);
4733
4734 /* Get surface sensible heat flux.
4735 Sign convention assumed here: unstable surface heating gives shf < 0,
4736 as in the existing implementation. */
4737 double shf;
4738 INTPOL_2D(shf, 1);
4739
4740 /* Estimate Monin-Obukhov length [m] to distinguish
4741 neutral, stable, and unstable cases. */
4742 double ol = 1e12;
4743 if (fabs(shf) > 1e-6)
4744 ol = thetav * rho * CPD * SQR(ust) * ust / (KARMAN * G0 * shf);
4745
4746 /* Neutral conditions... */
4747 if (zi / fabs(ol) < 1.0) {
4748
4749 /* corr has units of seconds, hence the exponential coefficients
4750 have units of s^-1. The derivative d(sig_w)/dz therefore needs
4751 the extra factor 1/ust. */
4752 const double corr = z_m / ust;
4753 const double sigw0 = 1.3 * ust * exp(-2e-4 * corr);
4754
4755 sig_u = MAX(2.0 * ust * exp(-3e-4 * corr), 1e-5);
4756 sig_v = MAX(sigw0, 1e-5);
4757 sig_w = MAX(sigw0, 1e-5);
4758 dsigw_dz = -2e-4 * sigw0 / ust;
4759
4760 tau_u = 0.5 * z_m / sig_w / (1.0 + 1.5e-3 * corr);
4761 tau_v = tau_u;
4762 tau_w = tau_u;
4763 }
4764
4765 /* Unstable conditions... */
4766 else if (ol < 0.0) {
4767
4768 /* Convective velocity scale [m/s]. */
4769 const double wstar_arg = -G0 / thetav * shf / (rho * CPD) * zi;
4770 const double wstar = pow(MAX(wstar_arg, 0.0), 1.0 / 3.0);
4771 double dsigw2_dz = 0.0;
4772
4773 /* Hanna1/FLEXPART turbulent velocity standard deviations [m/s]. */
4774 sig_u = MAX(ust * pow(MAX(12.0 - 0.5 * zi / ol, 0.0), 1.0 / 3.0), 1e-6);
4775 sig_v = sig_u;
4776
4777 if (zeta < 0.03) {
4778 const double arg = MAX(3.0 * zeta - ol / zi, 1e-12);
4779 sig_w = 0.96 * wstar * pow(arg, 1.0 / 3.0);
4780 dsigw2_dz = 1.8432 * SQR(wstar) / zi * pow(arg, -1.0 / 3.0);
4781 } else if (zeta < 0.4) {
4782 const double arg = MAX(3.0 * zeta - ol / zi, 1e-12);
4783 const double s1 = 0.96 * pow(arg, 1.0 / 3.0);
4784 const double s2 = 0.763 * pow(zeta, 0.175);
4785 if (s1 < s2) {
4786 sig_w = wstar * s1;
4787 dsigw2_dz = 1.8432 * SQR(wstar) / zi * pow(arg, -1.0 / 3.0);
4788 } else {
4789 sig_w = wstar * s2;
4790 dsigw2_dz = 0.203759 * SQR(wstar) / zi * pow(zeta, -0.65);
4791 }
4792 } else if (zeta < 0.96) {
4793 sig_w = 0.722 * wstar * pow(1.0 - zeta, 0.207);
4794 dsigw2_dz = -0.215812 * SQR(wstar) / zi * pow(1.0 - zeta, -0.586);
4795 } else {
4796 sig_w = 0.37 * wstar;
4797 dsigw2_dz = 0.0;
4798 }
4799
4800 sig_w = MAX(sig_w, 1e-6);
4801 dsigw_dz = sig_w > 1e-12 ? 0.5 * dsigw2_dz / sig_w : 0.0;
4802
4803 /* Hanna/FLEXPART Lagrangian timescales [s]. */
4804 tau_u = 0.15 * zi / MAX(sig_u, 1e-12);
4805 tau_v = tau_u;
4806
4807 if (z_m < fabs(ol)) {
4808 const double denom = 0.55 - 0.38 * fabs(z_m / ol);
4809 tau_w = 0.1 * z_m / (sig_w * MAX(denom, 0.05));
4810 } else if (zeta < 0.1)
4811 tau_w = 0.59 * z_m / sig_w;
4812 else
4813 tau_w = 0.15 * zi / sig_w * (1.0 - exp(-5.0 * zeta));
4814 }
4815
4816 /* Stable conditions... */
4817 else {
4818
4819 sig_u = MAX(2.0 * ust * (1.0 - zeta), 1e-6);
4820 sig_v = MAX(1.3 * ust * (1.0 - zeta), 1e-6);
4821 sig_w = MAX(1.3 * ust * (1.0 - zeta), 1e-6);
4822 dsigw_dz = -1.3 * ust / zi;
4823
4824 tau_u = 0.15 * zi / sig_u * sqrt(zeta);
4825 tau_v = 0.467 * tau_u;
4826 tau_w = 0.1 * zi / sig_w * pow(zeta, 0.8);
4827 }
4828
4829 /* Apply FLEXPART-consistent lower bounds for timescales. */
4830 tau_u = MAX(tau_u, 10.0);
4831 tau_v = MAX(tau_v, 10.0);
4832 tau_w = MAX(tau_w, 30.0);
4833
4834 /* Skip pathological states. */
4835 if (!(sig_u > 0.0 && sig_v > 0.0
4836 && sig_w > 0.0 && tau_u > 0.0 && tau_v > 0.0 && tau_w > 0.0))
4837 continue;
4838
4839 /* Update horizontal perturbation [m/s]. */
4840 const double dt = cache->dt[ip];
4841 const double dt_abs = fabs(dt);
4842
4843 const double ru = exp(-dt_abs / tau_u);
4844 const double ru2 = sqrt(MAX(0.0, 1.0 - SQR(ru)));
4845 const double rv = exp(-dt_abs / tau_v);
4846 const double rv2 = sqrt(MAX(0.0, 1.0 - SQR(rv)));
4847
4848 cache->uvwp[ip][0]
4849 = (float) (cache->uvwp[ip][0] * ru + sig_u * ru2 * cache->rs[3 * ip]);
4850
4851 cache->uvwp[ip][1]
4852 = (float) (cache->uvwp[ip][1] * rv
4853 + sig_v * rv2 * cache->rs[3 * ip + 1]);
4854
4855 /* Update vertical perturbation [m/s].
4856 The drift term is d(sig_w^2)/dz + sig_w^2/rho * d(rho)/dz.
4857 With exponential scale height H0 [km], dln(rho)/dz ~= -1/(1000 H0). */
4858 const double rw = exp(-dt_abs / tau_w);
4859 const double rw2 = sqrt(MAX(0.0, 1.0 - SQR(rw)));
4860 const double rhoaux = -1.0 / (1e3 * H0);
4861
4862 cache->uvwp[ip][2]
4863 = (float) (cache->uvwp[ip][2] * rw + sig_w * rw2 * cache->rs[3 * ip + 2]
4864 + tau_w * (1.0 - rw)
4865 * (2.0 * sig_w * dsigw_dz + rhoaux * SQR(sig_w)));
4866
4867 /* Calculate new horizontal air parcel position. */
4868 atm->lon[ip] += DX2COORD(met0, cache->uvwp[ip][0] * dt, atm->lat[ip]);
4869 atm->lat[ip] += DY2COORD(met0, cache->uvwp[ip][1] * dt);
4870
4871 /* Calculate new height and reflect robustly at surface and PBL top.
4872 Each boundary reflection reverses the vertical turbulent velocity.
4873 This loop is intentionally simple and explicit: it remains correct
4874 even if a large step crosses multiple boundaries. */
4875 double znew = z + cache->uvwp[ip][2] * dt;
4876
4877 while (znew < 0.0 || znew > zi) {
4878
4879 if (znew < 0.0) {
4880 znew = -znew;
4881 cache->uvwp[ip][2] = -cache->uvwp[ip][2];
4882 }
4883
4884 if (znew > zi) {
4885 znew = 2.0 * zi - znew;
4886 cache->uvwp[ip][2] = -cache->uvwp[ip][2];
4887 }
4888 }
4889
4890 /* Set pressure from reflected geometric height.
4891 This is more consistent than a linearized DZ2DP update for
4892 potentially large turbulent steps. */
4893 atm->p[ip] = P(zs + znew / 1000.0);
4894
4895 /* Enforce exact pressure limits of the local PBL column. */
4896 atm->p[ip] = CLAMP(atm->p[ip], pbl, ps);
4897 }
4898}
#define TVIRT(t, h2o)
Compute virtual temperature.
Definition mptrac.h:2219
#define H0
Scale height [km].
Definition mptrac.h:270
#define KARMAN
Karman's constant.
Definition mptrac.h:280
#define CLAMP(v, lo, hi)
Clamp a value to a specified range.
Definition mptrac.h:758
#define THETAVIRT(p, t, h2o)
Compute virtual potential temperature.
Definition mptrac.h:2173
Here is the call graph for this function:

◆ module_diff_turb()

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.

This function calculates and applies turbulent diffusion effects, including horizontal and vertical diffusion, as well as vertical mixing in the planetary boundary layer (PBL), to a set of atmospheric particles based on input parameters and environmental conditions.

Parameters
[in]ctlPointer to the control structure containing simulation parameters.
[in,out]cachePointer to the cache structure for temporary data and random numbers.
[in]climPointer to the climate structure containing climatological data.
[in,out]met0Pointer to the meteorological data structure for the initial timestep.
[in,out]met1Pointer to the meteorological data structure for the next timestep.
[in,out]atmPointer to the atmospheric structure containing particle data.

The function performs the following operations:

  • Allocates temporary arrays for random number generation.
  • Generates random numbers for simulating diffusion effects.
  • Loops over atmospheric particles to compute and apply:
    • Horizontal turbulent diffusion, based on prescribed diffusivity values.
    • Vertical turbulent diffusion, using vertical diffusivity values.
  • Cleans up allocated resources after processing.

Turbulent diffusivity parameters are derived from control inputs and weighted based on atmospheric layer influences (PBL, troposphere, stratosphere).

Note
  • Control parameters TURB_DX_PBL, TURB_DX_TROP, TURB_DX_STRAT, TURB_DZ_PBL, TURB_DZ_TROP, and TURB_DZ_STRAT define horizontal and vertical diffusivities (in units of m**2 s**-1) in the PBL, troposphere, and stratosphere, respectively.
  • Control parameter TURB_PBL_SCHEME activates an optional PBL-specific closure scheme. If a PBL scheme is active, the fixed-K module is only applied outside the PBL.
  • Apply the following settings to reproduce Stohl et al. (2005): TURB_DX_PBL = 50 TURB_DX_TROP = 50 TURB_DX_STRAT = 0 TURB_DZ_PBL = 0 TURB_DZ_TROP = 0 TURB_DZ_STRAT = 0.1 TURB_MESOX = 0.16 TURB_MESOZ = 0.16 TURB_PBL_SCHEME = 0
  • Apply the following setting to reproduce Maryon et al. (1991) and Ryall et al. (1998): TURB_DX_PBL = 5300 TURB_DX_TROP = 1325 TURB_DX_STRAT = 1325 TURB_DZ_PBL = 0 TURB_DZ_TROP = 1.5 TURB_DZ_STRAT = 1.5 TURB_MESOX = 0 TURB_MESOZ = 0 TURB_PBL_SCHEME = 1
Author
Lars Hoffmann

Definition at line 4902 of file mptrac.c.

4908 {
4909
4910 /* Set timer... */
4911 SELECT_TIMER("MODULE_DIFF_TURB", "PHYSICS");
4912
4913 /* Create random numbers... */
4914 module_rng(ctl, cache->rs, 3 * (size_t) atm->np, 1);
4915
4916 /* Loop over particles... */
4917 PARTICLE_LOOP(0, atm->np, 1,
4918 "acc data present(ctl,cache,clim,met0,met1,atm)") {
4919
4920 /* Get PBL pressure... */
4921 double pbl;
4923 INTPOL_2D(pbl, 1);
4924
4925 /* Let optional PBL closure schemes handle turbulent diffusion inside the PBL. */
4926 if (ctl->turb_pbl_scheme > 0 && atm->p[ip] >= pbl)
4927 continue;
4928
4929 /* Get surface pressure... */
4930 double ps;
4931 INTPOL_2D(ps, 0);
4932
4933 /* Pressure at model top [hPa]. */
4934 const double ptop = met0->p[met0->np - 1];
4935
4936 /* Get weighting factors at current particle position... */
4937 const double wpbl = pbl_weight(ctl, atm, ip, pbl, ps);
4938 const double wtrop = tropo_weight(ctl, clim, atm, ip) * (1.0 - wpbl);
4939 const double wstrat = 1.0 - wpbl - wtrop;
4940
4941 /* Set diffusivities [m2/s]... */
4942 const double Kx =
4943 wpbl * ctl->turb_dx_pbl
4944 + wtrop * ctl->turb_dx_trop + wstrat * ctl->turb_dx_strat;
4945
4946 const double Kz =
4947 wpbl * ctl->turb_dz_pbl
4948 + wtrop * ctl->turb_dz_trop + wstrat * ctl->turb_dz_strat;
4949
4950 /* Set time step... */
4951 const double dt_abs = fabs(cache->dt[ip]);
4952
4953 /* Horizontal turbulent diffusion...
4954 Kx [m2/s], dt [s] => sigma_h [m]. */
4955 if (Kx > 0) {
4956 const double sigma_h = sqrt(2.0 * Kx * dt_abs);
4957
4958 atm->lon[ip] +=
4959 DX2COORD(met0, cache->rs[3 * ip] * sigma_h, atm->lat[ip]);
4960
4961 atm->lat[ip] += DY2COORD(met0, cache->rs[3 * ip + 1] * sigma_h);
4962 }
4963
4964 /* Vertical turbulent diffusion... */
4965 if (Kz > 0) {
4966
4967 /* Random displacement:
4968 Kz [m2/s], dt [s] => sigma_z [m], converted to [km]. */
4969 const double sigma_z = sqrt(2.0 * Kz * dt_abs) * 1e-3;
4970
4971 /* Save current pressure because pbl_weight() and tropo_weight()
4972 use atm->p[ip]. */
4973 const double p_save = atm->p[ip];
4974
4975 /* Estimate dKz/dz by centered finite difference.
4976 eps_km = 0.01 km = 10 m.
4977 Positive z is upward; therefore p_up < p_save and p_dn > p_save. */
4978 const double eps_km = 0.01;
4979 const double p_up = p_save + DZ2DP(eps_km, p_save);
4980 const double p_dn = p_save + DZ2DP(-eps_km, p_save);
4981
4982 /* Kz above... */
4983 atm->p[ip] = MAX(ptop, MIN(ps, p_up));
4984 const double wpbl_up = pbl_weight(ctl, atm, ip, pbl, ps);
4985 const double wtrop_up =
4986 tropo_weight(ctl, clim, atm, ip) * (1.0 - wpbl_up);
4987 const double wstrat_up = 1.0 - wpbl_up - wtrop_up;
4988
4989 const double Kz_up =
4990 wpbl_up * ctl->turb_dz_pbl
4991 + wtrop_up * ctl->turb_dz_trop + wstrat_up * ctl->turb_dz_strat;
4992
4993 /* Kz below... */
4994 atm->p[ip] = MAX(ptop, MIN(ps, p_dn));
4995 const double wpbl_dn = pbl_weight(ctl, atm, ip, pbl, ps);
4996 const double wtrop_dn =
4997 tropo_weight(ctl, clim, atm, ip) * (1.0 - wpbl_dn);
4998 const double wstrat_dn = 1.0 - wpbl_dn - wtrop_dn;
4999
5000 const double Kz_dn =
5001 wpbl_dn * ctl->turb_dz_pbl
5002 + wtrop_dn * ctl->turb_dz_trop + wstrat_dn * ctl->turb_dz_strat;
5003
5004 /* Restore current pressure... */
5005 atm->p[ip] = p_save;
5006
5007 /* Well-mixed drift:
5008 w_drift = dKz/dz + Kz * dlnrho/dz
5009
5010 Units:
5011 dKz_dz [m2/s] / [m] = [m/s]
5012 dlnrho_dz [1/m]
5013 Kz*dlnrho [m2/s] * [1/m] = [m/s]
5014 dz_drift [m/s] * [s] * 1e-3 = [km]
5015
5016 With exponential atmosphere rho ~ exp(-z/H0):
5017 dlnrho/dz = -1 / (1000 * H0)
5018 because H0 is in [km]. */
5019 const double dKz_dz = (Kz_up - Kz_dn) / (2.0 * eps_km * 1e3);
5020 const double dlnrho_dz = -1.0 / (1e3 * H0);
5021 const double w_drift = dKz_dz + Kz * dlnrho_dz;
5022 const double dz_drift = w_drift * dt_abs * 1e-3;
5023
5024 /* Total vertical displacement [km]. */
5025 const double dz_tot = cache->rs[3 * ip + 2] * sigma_z + dz_drift;
5026
5027 /* Update particle pressure... */
5028 double ptrial = p_save + DZ2DP(dz_tot, p_save);
5029
5030 /* Reflect at surface and model top.
5031 The transformation p -> pb^2 / p corresponds to reflection in
5032 logarithmic pressure / approximate height coordinates. Use repeated
5033 reflection for robustness in case a large random step crosses more
5034 than one boundary. */
5035 for (int iter = 0; iter < 10; iter++) {
5036 if (ptrial > ps)
5037 ptrial = ps * ps / ptrial;
5038 else if (ptrial < ptop)
5039 ptrial = ptop * ptop / ptrial;
5040 else
5041 break;
5042 }
5043
5044 /* Final safety clamp in case of an exceptionally large displacement... */
5045 atm->p[ip] = MAX(ptop, MIN(ps, ptrial));
5046 }
5047 }
5048}
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:8950
#define DZ2DP(dz, p)
Convert a change in altitude to a change in pressure.
Definition mptrac.h:943
double turb_dz_trop
Vertical turbulent diffusion coefficient (troposphere) [m^2/s].
Definition mptrac.h:3080
double turb_dx_strat
Horizontal turbulent diffusion coefficient (stratosphere) [m^2/s].
Definition mptrac.h:3074
double turb_dx_trop
Horizontal turbulent diffusion coefficient (troposphere) [m^2/s].
Definition mptrac.h:3071
int turb_pbl_scheme
PBL turbulence scheme (0=none, 1=closure).
Definition mptrac.h:3065
double turb_dx_pbl
Horizontal turbulent diffusion coefficient (PBL) [m^2/s].
Definition mptrac.h:3068
double turb_dz_strat
Vertical turbulent diffusion coefficient (stratosphere) [m^2/s].
Definition mptrac.h:3083
double turb_dz_pbl
Vertical turbulent diffusion coefficient (PBL) [m^2/s].
Definition mptrac.h:3077
Here is the call graph for this function:

◆ module_dry_depo()

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.

This function simulates the dry deposition of atmospheric particles, including both particulate matter and gases. It calculates the sedimentation velocity for particles based on the atmospheric properties and applies it to determine the loss of mass or volume mixing ratio due to deposition. The function loops over each particle and calculates the loss of mass or volume mixing ratio based on the deposition velocity and time step.

Parameters
ctlPointer to the control structure containing simulation parameters.
cachePointer to the cache structure for temporary data and random numbers.
met0Pointer to the meteorological data structure at the current time step.
met1Pointer to the meteorological data structure at the next time step.
atmPointer to the atmospheric data structure containing particle information.
Author
Lars Hoffmann

Definition at line 5052 of file mptrac.c.

5057 {
5058
5059 /* Set timer... */
5060 SELECT_TIMER("MODULE_DRY_DEPO", "PHYSICS");
5061
5062 /* Check quantity flags... */
5063 if (ctl->qnt_m < 0 && ctl->qnt_vmr < 0)
5064 ERRMSG("Module needs quantity mass or volume mixing ratio!");
5065
5066 /* Loop over particles... */
5067 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
5068
5069 /* Get surface pressure... */
5070 double ps;
5072 INTPOL_2D(ps, 1);
5073
5074 /* Check whether particle is above the surface layer... */
5075 if (atm->p[ip] < ps - ctl->dry_depo_dp)
5076 continue;
5077
5078 /* Set depth of surface layer... */
5079 const double dz = 1000. * (Z(ps - ctl->dry_depo_dp) - Z(ps));
5080
5081 /* Calculate sedimentation velocity for particles... */
5082 double v_dep;
5083 if (ctl->qnt_rp > 0 && ctl->qnt_rhop > 0) {
5084
5085 /* Get temperature... */
5086 double t;
5087 INTPOL_3D(t, 1);
5088
5089 /* Set deposition velocity... */
5090 v_dep = sedi(atm->p[ip], t, atm->q[ctl->qnt_rp][ip],
5091 atm->q[ctl->qnt_rhop][ip]);
5092 }
5093
5094 /* Use explicit sedimentation velocity for gases... */
5095 else
5096 v_dep = ctl->dry_depo_vdep;
5097
5098 /* Calculate loss of mass based on deposition velocity... */
5099 const double aux = exp(-cache->dt[ip] * v_dep / dz);
5100 if (ctl->qnt_m >= 0) {
5101 if (ctl->qnt_mloss_dry >= 0)
5102 atm->q[ctl->qnt_mloss_dry][ip]
5103 += atm->q[ctl->qnt_m][ip] * (1 - aux);
5104 atm->q[ctl->qnt_m][ip] *= aux;
5105 if (ctl->qnt_loss_rate >= 0)
5106 atm->q[ctl->qnt_loss_rate][ip] += v_dep / dz;
5107 }
5108 if (ctl->qnt_vmr >= 0)
5109 atm->q[ctl->qnt_vmr][ip] *= aux;
5110 }
5111}
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:13401
int qnt_rhop
Quantity array index for particle density.
Definition mptrac.h:2554
int qnt_rp
Quantity array index for particle radius.
Definition mptrac.h:2551
double dry_depo_dp
Dry deposition surface layer [hPa].
Definition mptrac.h:3314
int qnt_mloss_dry
Quantity array index for total mass loss due to dry deposition.
Definition mptrac.h:2698
double dry_depo_vdep
Dry deposition velocity [m/s].
Definition mptrac.h:3317
Here is the call graph for this function:

◆ module_h2o2_chem()

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.

This function simulates chemical reactions involving hydrogen peroxide (H2O2) within cloud particles. It calculates the change in H2O2 concentration over time due to chemical reactions. The reaction rates are determined based on temperature and cloud properties such as liquid water content.

Parameters
ctlPointer to the control structure containing simulation parameters.
cachePointer to the cache structure for temporary data and random numbers.
climPointer to the climatological data structure.
met0Pointer to the first meteorological data structure.
met1Pointer to the second meteorological data structure.
atmPointer to the atmospheric data structure containing particle information.
Note
The function assumes that the necessary control structure (ctl), climatological data structure (clim), meteorological data structures (met0, met1), and atmospheric data structure (atm) have been initialized and are accessible.
Chemical reactions involving H2O2 are simulated for particles within clouds, as indicated by a positive liquid water content (LWC).
The function calculates reaction rates based on temperature and cloud properties, including the liquid water content (LWC) and the concentration of SO2.
The exponential decay of H2O2 concentration due to chemical reactions is calculated using the reaction rate coefficient and the time step (dt) for each particle.
If the particle has a quantity flag for either mass (ctl->qnt_m) or volume mixing ratio (ctl->qnt_vmr), the function updates the quantity based on the exponential decay.
If the particle has a loss rate quantity flag (ctl->qnt_loss_rate), the function accumulates the reaction rate coefficient to quantify the loss rate.
Author
Mingzhao Liu

Definition at line 5115 of file mptrac.c.

5121 {
5122
5123 if (ctl->met_coord_type != 0)
5124 ERRMSG("Only lat/lon grid supported");
5125
5126 /* Set timer... */
5127 SELECT_TIMER("MODULE_H2O2_CHEM", "PHYSICS");
5128
5129 /* Check quantity flags... */
5130 if (ctl->qnt_m < 0 && ctl->qnt_vmr < 0)
5131 ERRMSG("Module needs quantity mass or volume mixing ratio!");
5132
5133 /* Parameter of SO2 correction... */
5134 const double low = pow(1. / SO2_CORR_A, 1. / SO2_CORR_B);
5135
5136 /* Loop over particles... */
5137 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
5138
5139 /* Check whether particle is inside cloud... */
5140 double lwc, rwc;
5142 INTPOL_3D(lwc, 1);
5143 INTPOL_3D(rwc, 0);
5144 if (!(lwc > 0 || rwc > 0))
5145 continue;
5146
5147 /* Get temperature... */
5148 double t;
5149 INTPOL_3D(t, 0);
5150
5151 /* Get molecular density... */
5152 const double M = MOLEC_DENS(atm->p[ip], t);
5153
5154 /* Reaction rate (Berglen et al., 2004)... */
5155 const double k = H2O2_SO2_RATE_REF * exp(-H2O2_SO2_RATE_TEMP / RI * (1. / t - 1. / CHEM_REF_TEMP)); /* Maass (1999), M^(-2) */
5156
5157 /* Henry constant of SO2... */
5158 const double H_SO2 =
5160 * (1. / t - 1. / CHEM_REF_TEMP)) * RI * t;
5161 const double K_1S = SO2_DISS_K1_REF * exp(SO2_DISS_K1_TEMP * (1. / t - 1. / CHEM_REF_TEMP)); /* unit: mol/L */
5162
5163 /* Henry constant of H2O2... */
5164 const double H_h2o2 =
5166 * (1. / t - 1. / CHEM_REF_TEMP)) * RI * t;
5167
5168 /* Correction factor for high SO2 concentration
5169 (if qnt_Cx is defined, the correction is switched on)... */
5170 double cor = 1.0;
5171 if (ctl->qnt_Cx >= 0)
5172 cor = atm->q[ctl->qnt_Cx][ip] >
5173 low ? SO2_CORR_A * pow(atm->q[ctl->qnt_Cx][ip], SO2_CORR_B) : 1;
5174
5175 const double h2o2 = H_h2o2
5176 * clim_zm(&clim->h2o2, atm->time[ip], atm->lat[ip], atm->p[ip])
5177 * M * cor * 1000. / AVO; /* unit: mol/L */
5178
5179 /* Volume water content in cloud [m^3 m^(-3)]... */
5180 const double rho_air = atm->p[ip] / (RI * t) * MA / 10.;
5181 const double CWC = (lwc + rwc) * rho_air / 1e3;
5182
5183 /* Calculate exponential decay (Rolph et al., 1992)... */
5184 const double rate_coef = k * K_1S * h2o2 * H_SO2 * CWC;
5185 const double aux = exp(-cache->dt[ip] * rate_coef);
5186 if (ctl->qnt_m >= 0) {
5187 if (ctl->qnt_mloss_h2o2 >= 0)
5188 atm->q[ctl->qnt_mloss_h2o2][ip] += atm->q[ctl->qnt_m][ip] * (1 - aux);
5189 atm->q[ctl->qnt_m][ip] *= aux;
5190 if (ctl->qnt_loss_rate >= 0)
5191 atm->q[ctl->qnt_loss_rate][ip] += rate_coef;
5192 }
5193 if (ctl->qnt_vmr >= 0)
5194 atm->q[ctl->qnt_vmr][ip] *= aux;
5195 }
5196}
#define AVO
Avogadro constant [1/mol].
Definition mptrac.h:250
#define SO2_CORR_B
Exponent of the high-SO2 correction [1].
Definition mptrac.h:385
#define MOLEC_DENS(p, t)
Calculate the density of a gas molecule.
Definition mptrac.h:1503
#define SO2_DISS_K1_REF
First SO2 dissociation constant at CHEM_REF_TEMP [mol/L].
Definition mptrac.h:430
#define SO2_DISS_K1_TEMP
Temperature dependence of the first SO2 dissociation constant [K].
Definition mptrac.h:435
#define H2O2_HENRY_TEMP
Temperature dependence of the H2O2 Henry constant [K].
Definition mptrac.h:375
#define SO2_CORR_A
Scale factor of the high-SO2 correction [1].
Definition mptrac.h:380
#define H2O2_SO2_RATE_REF
Reference rate for aqueous H2O2-SO2 chemistry [L^2/(mol^2 s)].
Definition mptrac.h:350
#define RI
Ideal gas constant [J/(mol K)].
Definition mptrac.h:320
#define H2O2_SO2_RATE_TEMP
Temperature parameter for the aqueous H2O2-SO2 rate [K].
Definition mptrac.h:355
#define H2O2_HENRY_REF
Henry constant of H2O2 at CHEM_REF_TEMP [mol/(L atm)].
Definition mptrac.h:370
#define CHEM_REF_TEMP
Reference temperature for chemical equilibrium constants [K].
Definition mptrac.h:345
#define SO2_HENRY_REF
Henry constant of SO2 at CHEM_REF_TEMP [mol/(L atm)].
Definition mptrac.h:360
#define SO2_HENRY_TEMP
Temperature dependence of the SO2 Henry constant [K].
Definition mptrac.h:365
int qnt_mloss_h2o2
Quantity array index for total mass loss due to H2O2 chemistry.
Definition mptrac.h:2689
Here is the call graph for this function:

◆ module_isosurf_init()

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.

This function initializes the isosurface module based on the atmospheric data provided. It calculates the necessary variables required for generating the isosurface, such as pressure, density, or potential temperature. Additionally, it can read balloon pressure data from a file if specified in the control structure. For modes 1-3 the reference is stored in the iso_var particle quantity; mode 4 stores a shared pressure series in the cache.

Parameters
ctlPointer to the control structure containing simulation parameters.
met0Pointer to the meteorological data structure at the current time step.
met1Pointer to the meteorological data structure at the next time step.
atmPointer to the atmospheric data structure containing particle information.
cachePointer to the cache structure for the shared balloon series.
Author
Lars Hoffmann

Definition at line 5200 of file mptrac.c.

5205 {
5206
5207 double t;
5208
5209 /* Set timer... */
5210 SELECT_TIMER("MODULE_ISOSURF_INIT", "PHYSICS");
5211
5212 /* Save pressure... */
5213 if (ctl->isosurf == 1) {
5214 PARTICLE_LOOP(0, atm->np, 0, "acc data present(ctl,atm)") {
5215 atm->q[ctl->qnt_iso_var][ip] = atm->p[ip];
5216 }
5217 }
5218
5219 /* Save density... */
5220 else if (ctl->isosurf == 2) {
5221 PARTICLE_LOOP(0, atm->np, 0, "acc data present(ctl,met0,met1,atm)") {
5223 INTPOL_3D(t, 1);
5224 atm->q[ctl->qnt_iso_var][ip] = atm->p[ip] / t;
5225 }
5226 }
5227
5228 /* Save potential temperature... */
5229 else if (ctl->isosurf == 3) {
5230 PARTICLE_LOOP(0, atm->np, 0, "acc data present(ctl,met0,met1,atm)") {
5232 INTPOL_3D(t, 1);
5233 atm->q[ctl->qnt_iso_var][ip] = THETA(atm->p[ip], t);
5234 }
5235 }
5236
5237 /* Read balloon pressure data... */
5238 else if (ctl->isosurf == 4) {
5239
5240 /* Write info... */
5241 LOG(1, "Read balloon pressure data: %s", ctl->balloon);
5242
5243 /* Open file... */
5244 FILE *in;
5245 if (!(in = fopen(ctl->balloon, "r")))
5246 ERRMSG("Cannot open file!");
5247
5248 /* Read pressure time series... */
5249 char line[LEN];
5250 while (fgets(line, LEN, in))
5251 if (sscanf(line, "%lg %lg", &(cache->iso_ts[cache->iso_n]),
5252 &(cache->iso_ps[cache->iso_n])) == 2)
5253 if ((++cache->iso_n) > NP)
5254 ERRMSG("Too many data points!");
5255
5256 /* Check number of points... */
5257 if (cache->iso_n < 1)
5258 ERRMSG("Could not read any data!");
5259
5260 /* Close file... */
5261 fclose(in);
5262
5263 /* Update of cache data on device... */
5264 mptrac_update_device(NULL, cache, NULL, NULL, NULL, NULL);
5265 }
5266}
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:8581
#define THETA(p, t)
Compute potential temperature.
Definition mptrac.h:2144
double iso_ts[NP]
Shared balloon times [s] for ISOSURF=4.
Definition mptrac.h:3679
int iso_n
Isosurface balloon number of data points.
Definition mptrac.h:3682
double iso_ps[NP]
Shared balloon pressure time series [hPa] for ISOSURF=4.
Definition mptrac.h:3676
char balloon[LEN]
Balloon position filename.
Definition mptrac.h:3049
int isosurf
Isosurface parameter (0=none, 1=pressure, 2=density, 3=theta, 4=balloon).
Definition mptrac.h:3046
int qnt_iso_var
Quantity array index for isosurface reference (units depend on ISOSURF).
Definition mptrac.h:2728
Here is the call graph for this function:

◆ module_isosurf()

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.

This function applies the isosurface module to adjust atmospheric properties based on the initialized particle quantity or balloon cache. It interpolates and restores atmospheric pressure, density, or potential temperature according to the specified isosurface mode in the control structure.

Parameters
ctlPointer to the control structure containing simulation parameters.
cachePointer to the cache structure for temporary data and random numbers.
met0Pointer to the meteorological data structure at the current time step.
met1Pointer to the meteorological data structure at the next time step.
atmPointer to the atmospheric data structure containing particle information.
Author
Lars Hoffmann

Definition at line 5270 of file mptrac.c.

5275 {
5276
5277 /* Set timer... */
5278 SELECT_TIMER("MODULE_ISOSURF", "PHYSICS");
5279
5280 /* Loop over particles... */
5281 PARTICLE_LOOP(0, atm->np, 0, "acc data present(ctl,cache,met0,met1,atm)") {
5282
5283 /* Init... */
5284 double t;
5286
5287 /* Restore pressure... */
5288 if (ctl->isosurf == 1)
5289 atm->p[ip] = atm->q[ctl->qnt_iso_var][ip];
5290
5291 /* Restore density... */
5292 else if (ctl->isosurf == 2) {
5293 INTPOL_3D(t, 1);
5294 atm->p[ip] = atm->q[ctl->qnt_iso_var][ip] * t;
5295 }
5296
5297 /* Restore potential temperature... */
5298 else if (ctl->isosurf == 3) {
5299 INTPOL_3D(t, 1);
5300 atm->p[ip] = 1000. * pow(atm->q[ctl->qnt_iso_var][ip] / t, -1. / KAPPA);
5301 }
5302
5303 /* Interpolate pressure... */
5304 else if (ctl->isosurf == 4) {
5305 if (atm->time[ip] <= cache->iso_ts[0])
5306 atm->p[ip] = cache->iso_ps[0];
5307 else if (atm->time[ip] >= cache->iso_ts[cache->iso_n - 1])
5308 atm->p[ip] = cache->iso_ps[cache->iso_n - 1];
5309 else {
5310 const int idx =
5311 locate_irr(cache->iso_ts, cache->iso_n, atm->time[ip]);
5312 atm->p[ip] =
5313 LIN(cache->iso_ts[idx], cache->iso_ps[idx], cache->iso_ts[idx + 1],
5314 cache->iso_ps[idx + 1], atm->time[ip]);
5315 }
5316 }
5317 }
5318}
#define KAPPA
Exponent used for potential-temperature calculations [1].
Definition mptrac.h:325
Here is the call graph for this function:

◆ module_meteo()

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.

This function updates atmospheric properties based on meteorological data interpolated between two time steps. It calculates various atmospheric quantities such as pressure, temperature, wind speed, humidity, etc., and updates the corresponding fields in the atmospheric data structure.

Parameters
ctlPointer to the control structure containing simulation parameters.
cachePointer to the cache structure for temporary data and random numbers.
climPointer to the climate data structure containing climatological data.
met0Pointer to the meteorological data structure at the current time step.
met1Pointer to the meteorological data structure at the next time step.
atmPointer to the atmospheric data structure containing particle information.
Author
Lars Hoffmann

Definition at line 5376 of file mptrac.c.

5382 {
5383
5384 /* Set timer... */
5385 SELECT_TIMER("MODULE_METEO", "PHYSICS");
5386
5387 /* Check quantity flags... */
5388 if (ctl->qnt_tsts >= 0)
5389 if (ctl->qnt_tice < 0 || ctl->qnt_tnat < 0)
5390 ERRMSG("Need T_ice and T_NAT to calculate T_STS!");
5391
5392 /* Loop over particles... */
5393 PARTICLE_LOOP(0, atm->np, 0,
5394 "acc data present(ctl,cache,clim,met0,met1,atm)") {
5395
5396 double ps, ts, zs, us, vs, ess, nss, shf, lsm, sst, pbl, pt, pct, pcb,
5397 cl, plcl, plfc, pel, cape, cin, o3c, pv, t, tt, u, v, w, h2o, h2ot,
5398 o3, lwc, rwc, iwc, swc, cc, z, zt, eta_d = 0, wdot = 0;
5399
5400 /* Interpolate meteo data... */
5402 INTPOL_TIME_ALL(atm->time[ip], atm->p[ip], atm->lon[ip], atm->lat[ip]);
5403
5404 /* Diagnose eta coordinate on native model levels... */
5405 if (ctl->qnt_eta_d >= 0)
5406 intpol_met_4d_zeta(met0, met0->pl, met0->zetal,
5407 met1, met1->pl, met1->zetal,
5408 atm->time[ip], atm->p[ip], atm->lon[ip],
5409 atm->lat[ip], &eta_d, ci, cw, 1);
5410
5411 /* Interpolate vertical velocity on native model levels... */
5412 if ((ctl->advect_vert_coord == 1 && ctl->qnt_zeta_dot >= 0)
5413 || (ctl->advect_vert_coord == 3 && ctl->qnt_eta_dot >= 0))
5414 intpol_met_4d_zeta(met0, met0->pl, met0->zeta_dotl,
5415 met1, met1->pl, met1->zeta_dotl,
5416 atm->time[ip], atm->p[ip], atm->lon[ip],
5417 atm->lat[ip], &wdot, ci, cw, 1);
5418
5419 /* Set quantities... */
5420 SET_ATM(qnt_ps, ps);
5421 SET_ATM(qnt_ts, ts);
5422 SET_ATM(qnt_zs, zs);
5423 SET_ATM(qnt_us, us);
5424 SET_ATM(qnt_vs, vs);
5425 SET_ATM(qnt_ess, ess);
5426 SET_ATM(qnt_nss, nss);
5427 SET_ATM(qnt_shf, shf);
5428 SET_ATM(qnt_lsm, lsm);
5429 SET_ATM(qnt_sst, sst);
5430 SET_ATM(qnt_pbl, pbl);
5431 SET_ATM(qnt_pt, pt);
5432 SET_ATM(qnt_tt, tt);
5433 SET_ATM(qnt_zt, zt);
5434 SET_ATM(qnt_h2ot, h2ot);
5435 SET_ATM(qnt_zg, z);
5436 SET_ATM(qnt_p, atm->p[ip]);
5437 SET_ATM(qnt_t, t);
5438 SET_ATM(qnt_rho, RHO(atm->p[ip], t));
5439 SET_ATM(qnt_u, u);
5440 SET_ATM(qnt_v, v);
5441 SET_ATM(qnt_w, w);
5442 SET_ATM(qnt_h2o, h2o);
5443 SET_ATM(qnt_o3, o3);
5444 SET_ATM(qnt_lwc, lwc);
5445 SET_ATM(qnt_rwc, rwc);
5446 SET_ATM(qnt_iwc, iwc);
5447 SET_ATM(qnt_swc, swc);
5448 SET_ATM(qnt_cc, cc);
5449 SET_ATM(qnt_pct, pct);
5450 SET_ATM(qnt_pcb, pcb);
5451 SET_ATM(qnt_cl, cl);
5452 SET_ATM(qnt_plcl, plcl);
5453 SET_ATM(qnt_plfc, plfc);
5454 SET_ATM(qnt_pel, pel);
5455 SET_ATM(qnt_cape, cape);
5456 SET_ATM(qnt_cin, cin);
5457 SET_ATM(qnt_o3c, o3c);
5458 const double lat_ref =
5459 ctl->met_coord_type == 0 ? atm->lat[ip] : ctl->met_utm_ref_lat;
5460 SET_ATM(qnt_hno3,
5461 clim_zm(&clim->hno3, atm->time[ip], lat_ref, atm->p[ip]));
5462 SET_ATM(qnt_oh, clim_oh(ctl, clim, atm->time[ip],
5463 atm->lon[ip], atm->lat[ip], atm->p[ip]));
5464 SET_ATM(qnt_h2o2, clim_zm(&clim->h2o2, atm->time[ip],
5465 lat_ref, atm->p[ip]));
5466 SET_ATM(qnt_ho2, clim_zm(&clim->ho2, atm->time[ip], lat_ref, atm->p[ip]));
5467 SET_ATM(qnt_o1d, clim_zm(&clim->o1d, atm->time[ip], lat_ref, atm->p[ip]));
5468 SET_ATM(qnt_vh, sqrt(u * u + v * v));
5469 SET_ATM(qnt_vz, -1e3 * H0 / atm->p[ip] * w);
5470 SET_ATM(qnt_psat, PSAT(t));
5471 SET_ATM(qnt_psice, PSICE(t));
5472 SET_ATM(qnt_pw, PW(atm->p[ip], h2o));
5473 SET_ATM(qnt_sh, SH(h2o));
5474 SET_ATM(qnt_rh, RH(atm->p[ip], t, h2o));
5475 SET_ATM(qnt_rhice, RHICE(atm->p[ip], t, h2o));
5476 SET_ATM(qnt_theta, THETA(atm->p[ip], t));
5477 SET_ATM(qnt_zeta, atm->q[ctl->qnt_zeta][ip]);
5478 SET_ATM(qnt_zeta_d, ZETA(ps, atm->p[ip], t));
5479 SET_ATM(qnt_eta, atm->q[ctl->qnt_eta][ip]);
5480 SET_ATM(qnt_eta_d, eta_d);
5481 if (ctl->advect_vert_coord == 1) {
5482 SET_ATM(qnt_zeta_dot, wdot);
5483 } else if (ctl->advect_vert_coord == 3) {
5484 SET_ATM(qnt_eta_dot, wdot);
5485 }
5486 SET_ATM(qnt_tvirt, TVIRT(t, h2o));
5487 SET_ATM(qnt_lapse, lapse_rate(t, h2o));
5488 SET_ATM(qnt_pv, pv);
5489 SET_ATM(qnt_tdew, TDEW(atm->p[ip], h2o));
5490 SET_ATM(qnt_tice, TICE(atm->p[ip], h2o));
5491 SET_ATM(qnt_tnat,
5492 nat_temperature(atm->p[ip], h2o,
5493 clim_zm(&clim->hno3, atm->time[ip],
5494 atm->lat[ip], atm->p[ip])));
5495 SET_ATM(qnt_tsts,
5496 0.5 * (atm->q[ctl->qnt_tice][ip] + atm->q[ctl->qnt_tnat][ip]));
5497 }
5498}
double nat_temperature(const double p, const double h2o, const double hno3)
Calculates the nitric acid trihydrate (NAT) temperature.
Definition mptrac.c:8926
double lapse_rate(const double t, const double h2o)
Calculates the moist adiabatic lapse rate in Kelvin per kilometer.
Definition mptrac.c:3630
#define PW(p, h2o)
Calculate partial water vapor pressure.
Definition mptrac.h:1879
#define PSICE(t)
Compute saturation pressure over ice (WMO, 2018).
Definition mptrac.h:1852
#define TICE(p, h2o)
Calculate frost point temperature (WMO, 2018).
Definition mptrac.h:2120
#define RHICE(p, t, h2o)
Compute relative humidity over ice.
Definition mptrac.h:1956
#define INTPOL_TIME_ALL(time, p, lon, lat)
Interpolate multiple meteorological variables in time.
Definition mptrac.h:1287
#define RH(p, t, h2o)
Compute relative humidity over water.
Definition mptrac.h:1926
#define TDEW(p, h2o)
Calculate dew point temperature.
Definition mptrac.h:2095
#define PSAT(t)
Compute saturation pressure over water.
Definition mptrac.h:1828
clim_zm_t hno3
HNO3 zonal means.
Definition mptrac.h:3858
int qnt_eta_d
Quantity array index for diagnosed eta vertical coordinate.
Definition mptrac.h:2743
int qnt_tnat
Quantity array index for T_NAT.
Definition mptrac.h:2773
int qnt_eta_dot
Quantity array index for velocity of eta vertical coordinate.
Definition mptrac.h:2746
int qnt_tice
Quantity array index for T_ice.
Definition mptrac.h:2767
int qnt_zeta_dot
Quantity array index for velocity of zeta vertical coordinate.
Definition mptrac.h:2737
int qnt_tsts
Quantity array index for T_STS.
Definition mptrac.h:2770
Here is the call graph for this function:

◆ module_mixing()

void module_mixing ( const ctl_t * ctl,
const cache_t * cache,
const clim_t * clim,
atm_t * atm,
const double t )

Update atmospheric properties through interparcel mixing.

This function updates atmospheric properties by performing interparcel mixing based on the given meteorological and climatological data. It calculates the indices of grid boxes and performs mixing for various quantities such as mass, volume mixing ratio, and other chemical species concentrations. Only particles with nonzero individual time steps participate. Geographic mixing boxes are evaluated locally on each MPI rank.

Parameters
ctlPointer to the control structure containing simulation parameters.
cachePointer to the cache structure containing individual time steps.
climPointer to the climate data structure containing climatological data.
atmPointer to the atmospheric data structure containing particle information.
tTime at which mixing is performed.
Author
Mingzhao Liu
Lars Hoffmann

Definition at line 5502 of file mptrac.c.

5507 {
5508
5509 /* Set timer... */
5510 SELECT_TIMER("MODULE_MIXING", "PHYSICS");
5511
5512 if (atm->np == 0)
5513 return;
5514
5515 /* Allocate... */
5516 const int np = atm->np;
5517 int *restrict const ixs = (int *) malloc((size_t) np * sizeof(int));
5518 int *restrict const iys = (int *) malloc((size_t) np * sizeof(int));
5519 int *restrict const izs = (int *) malloc((size_t) np * sizeof(int));
5520 double *restrict const mixparam =
5521 (double *) malloc((size_t) np * sizeof(double));
5522 if (ixs == NULL || iys == NULL || izs == NULL || mixparam == NULL)
5523 ERRMSG("Out of memory!");
5524
5525 /* Set grid box size... */
5526 const double dz = (ctl->mixing_z1 - ctl->mixing_z0) / ctl->mixing_nz;
5527 const double dlon = (ctl->mixing_lon1 - ctl->mixing_lon0) / ctl->mixing_nx;
5528 const double dlat = (ctl->mixing_lat1 - ctl->mixing_lat0) / ctl->mixing_ny;
5529
5530 /* Set time interval... */
5531 const double t0 = t - 0.5 * ctl->dt_mod;
5532 const double t1 = t + 0.5 * ctl->dt_mod;
5533 const double dt_mix = ctl->mixing_dt > 0 ? ctl->mixing_dt : ctl->dt_mod;
5534
5535 /* Get indices... */
5536#ifdef _OPENACC
5537#pragma acc enter data create(ixs[0:np],iys[0:np],izs[0:np],mixparam[0:np])
5538#pragma acc data present(ctl,cache,clim,atm,ixs,iys,izs,mixparam)
5539#pragma acc parallel loop independent gang vector
5540#else
5541#pragma omp parallel for default(shared)
5542#endif
5543 for (int ip = 0; ip < np; ip++) {
5544 if (cache->dt[ip] == 0) {
5545 izs[ip] = -1;
5546 continue;
5547 }
5548 const double zpart = Z(atm->p[ip]);
5549 if (atm->time[ip] < t0 || atm->time[ip] > t1
5550 || atm->lon[ip] < ctl->mixing_lon0
5551 || atm->lon[ip] >= ctl->mixing_lon1
5552 || atm->lat[ip] < ctl->mixing_lat0
5553 || atm->lat[ip] >= ctl->mixing_lat1
5554 || zpart < ctl->mixing_z0 || zpart >= ctl->mixing_z1) {
5555 izs[ip] = -1;
5556 continue;
5557 }
5558 ixs[ip] = (int) ((atm->lon[ip] - ctl->mixing_lon0) / dlon);
5559 iys[ip] = (int) ((atm->lat[ip] - ctl->mixing_lat0) / dlat);
5560 izs[ip] = (int) ((zpart - ctl->mixing_z0) / dz);
5561 if (ixs[ip] >= ctl->mixing_nx || iys[ip] >= ctl->mixing_ny
5562 || izs[ip] >= ctl->mixing_nz) {
5563 izs[ip] = -1;
5564 continue;
5565 }
5566 const double w = tropo_weight(ctl, clim, atm, ip);
5567 const double rate =
5568 w / ctl->mixing_tau_trop + (1.0 - w) / ctl->mixing_tau_strat;
5569 mixparam[ip] = -expm1(-dt_mix * rate);
5570 }
5571
5572 /* Calculate interparcel mixing... */
5573 const int use_ensemble = (ctl->nens > 0);
5574
5575 const int quantities[] = {
5576 ctl->qnt_m, ctl->qnt_vmr, ctl->qnt_Ch2o, ctl->qnt_Co3,
5577 ctl->qnt_Cco, ctl->qnt_Coh, ctl->qnt_Ch, ctl->qnt_Cho2,
5578 ctl->qnt_Ch2o2, ctl->qnt_Co1d, ctl->qnt_Co3p, ctl->qnt_Cccl4,
5579 ctl->qnt_Cccl3f, ctl->qnt_Cccl2f2, ctl->qnt_Cn2o,
5580 ctl->qnt_Csf6, ctl->qnt_aoa, ctl->qnt_Arn222, ctl->qnt_Apb210,
5581 ctl->qnt_Abe7, ctl->qnt_Acs137, ctl->qnt_Ai131, ctl->qnt_Axe133
5582 };
5583 const int n_qnt = sizeof(quantities) / sizeof(quantities[0]);
5584
5585 for (int i = 0; i < n_qnt; i++)
5586 if (quantities[i] >= 0)
5587 module_mixing_help(ctl, atm, ixs, iys, izs, mixparam, quantities[i],
5588 use_ensemble);
5589
5590 /* Free... */
5591#ifdef _OPENACC
5592#pragma acc exit data delete(ixs,iys,izs,mixparam)
5593#endif
5594 free(ixs);
5595 free(iys);
5596 free(izs);
5597 free(mixparam);
5598}
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:5602
int qnt_Coh
Quantity array index for OH volume mixing ratio (chemistry code).
Definition mptrac.h:2788
int mixing_nx
Number of longitudes of mixing grid.
Definition mptrac.h:3212
double mixing_z1
Upper altitude of mixing grid [km].
Definition mptrac.h:3209
int qnt_Co1d
Quantity array index for O(1D) volume mixing ratio (chemistry code).
Definition mptrac.h:2800
int qnt_Acs137
Quantity array index for radioactive activity of Cs-137.
Definition mptrac.h:2833
double mixing_z0
Lower altitude of mixing grid [km].
Definition mptrac.h:3206
int qnt_Cco
Quantity array index for CO volume mixing ratio (chemistry code).
Definition mptrac.h:2785
int mixing_ny
Number of latitudes of mixing grid.
Definition mptrac.h:3221
int qnt_Ch
Quantity array index for H volume mixing ratio (chemistry code).
Definition mptrac.h:2791
double mixing_lat0
Lower latitude of mixing grid [deg].
Definition mptrac.h:3224
int qnt_Ai131
Quantity array index for radioactive activity of I-131.
Definition mptrac.h:2836
double mixing_tau_trop
E-folding time for interparcel mixing in the troposphere [s].
Definition mptrac.h:3197
int qnt_Apb210
Quantity array index for radioactive activity of Pb-210.
Definition mptrac.h:2827
int qnt_Cho2
Quantity array index for HO2 volume mixing ratio (chemistry code).
Definition mptrac.h:2794
int mixing_nz
Number of altitudes of mixing grid.
Definition mptrac.h:3203
double mixing_lon0
Lower longitude of mixing grid [deg].
Definition mptrac.h:3215
double mixing_lat1
Upper latitude of mixing grid [deg].
Definition mptrac.h:3227
int qnt_Axe133
Quantity array index for radioactive activity of Xe-133.
Definition mptrac.h:2839
int qnt_Co3p
Quantity array index for O(3P) volume mixing ratio (chemistry code).
Definition mptrac.h:2803
int qnt_Abe7
Quantity array index for radioactive activity of Be-7.
Definition mptrac.h:2830
double mixing_dt
Time interval for mixing [s].
Definition mptrac.h:3194
double mixing_tau_strat
E-folding time for interparcel mixing in the stratosphere [s].
Definition mptrac.h:3200
int qnt_Arn222
Quantity array index for radioactive activity of Rn-222.
Definition mptrac.h:2824
int qnt_Ch2o2
Quantity array index for H2O2 volume mixing ratio (chemistry code).
Definition mptrac.h:2797
double mixing_lon1
Upper longitude of mixing grid [deg].
Definition mptrac.h:3218
Here is the call graph for this function:

◆ module_mixing_help()

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.

This function computes the mixing-parameter-weighted mean of a specified quantity within each subgrid box (and optionally for each ensemble member) and applies a mixing adjustment to particle values based on the computed local mean. The weighted mean conserves the unweighted sum of the quantity within each rank-local box and ensemble when the mixing parameter varies between particles. Air-mass-weighted conservation requires additional assumptions about the mass represented by each particle.

The mixing accounts for differences in tropospheric and stratospheric mixing via a weighted parameterization. It supports both ensemble and non-ensemble modes using the use_ensemble flag.

Parameters
[in]ctlPointer to control/configuration structure.
[in,out]atmPointer to atmospheric state (includes particles).
[in]ixsArray of x-grid indices for each particle.
[in]iysArray of y-grid indices for each particle.
[in]izsArray of z-grid indices for each particle (-1 for invalid).
[in]mixparamMixing parameter for each particle.
[in]qnt_idxIndex of the quantity in atm->q to be mixed.
[in]use_ensembleFlag indicating whether to use ensemble-aware logic (0 = no, 1 = yes).
Note
Particles with izs[ip] < 0 are excluded from mixing.
Uses OpenACC or OpenMP for parallelism depending on compilation options.
Requires ctl->qnt_ens to be valid if use_ensemble is true.
Author
Mingzhao Liu
Lars Hoffmann

Definition at line 5602 of file mptrac.c.

5610 {
5611
5612 const int np = atm->np;
5613 const int ngrid = ctl->mixing_nx * ctl->mixing_ny * ctl->mixing_nz;
5614 const int nens = use_ensemble ? ctl->nens : 1;
5615 const int total_grid = ngrid * nens;
5616
5617 double *restrict const cmean =
5618 (double *) malloc((size_t) total_grid * sizeof(double));
5619 double *restrict const cweight =
5620 (double *) malloc((size_t) total_grid * sizeof(double));
5621 if (cmean == NULL || cweight == NULL)
5622 ERRMSG("Out of memory!");
5623
5624 /* Init... */
5625#ifdef _OPENACC
5626#pragma acc enter data create(cmean[0:total_grid],cweight[0:total_grid])
5627#pragma acc data present(ctl,atm,ixs,iys,izs,mixparam,cmean,cweight)
5628#pragma acc parallel loop independent gang vector
5629#else
5630#ifdef __NVCOMPILER
5631#pragma novector
5632#endif
5633#pragma omp parallel for
5634#endif
5635 for (int i = 0; i < total_grid; i++) {
5636 cmean[i] = 0.0;
5637 cweight[i] = 0.0;
5638 }
5639
5640 /* Loop over particles... */
5641#ifdef _OPENACC
5642#pragma acc parallel loop independent gang vector
5643#endif
5644 for (int ip = 0; ip < np; ip++)
5645 if (izs[ip] >= 0) {
5646 const int ens = use_ensemble ? (int) atm->q[ctl->qnt_ens][ip] : 0;
5647 if (ens < 0 || ens >= nens)
5648 continue;
5649 const int idx =
5650 ens * ngrid + ARRAY_3D(ixs[ip], iys[ip], ctl->mixing_ny, izs[ip],
5651 ctl->mixing_nz);
5652#ifdef _OPENACC
5653#pragma acc atomic update
5654#endif
5655 cmean[idx] += mixparam[ip] * atm->q[qnt_idx][ip];
5656#ifdef _OPENACC
5657#pragma acc atomic update
5658#endif
5659 cweight[idx] += mixparam[ip];
5660 }
5661
5662 /* Compute means... */
5663#ifdef _OPENACC
5664#pragma acc parallel loop independent gang vector
5665#else
5666#ifdef __NVCOMPILER
5667#pragma novector
5668#endif
5669#pragma omp parallel for
5670#endif
5671 for (int i = 0; i < total_grid; i++)
5672 if (cweight[i] > 0)
5673 cmean[i] /= cweight[i];
5674
5675 /* Interparcel mixing... */
5676#ifdef _OPENACC
5677#pragma acc parallel loop independent gang vector
5678#else
5679#pragma omp parallel for
5680#endif
5681 for (int ip = 0; ip < np; ip++) {
5682 if (izs[ip] >= 0) {
5683 const int ens = use_ensemble ? (int) atm->q[ctl->qnt_ens][ip] : 0;
5684
5685 if (ens < 0 || ens >= nens)
5686 continue;
5687 const int idx =
5688 ens * ngrid + ARRAY_3D(ixs[ip], iys[ip], ctl->mixing_ny, izs[ip],
5689 ctl->mixing_nz);
5690 atm->q[qnt_idx][ip] +=
5691 (cmean[idx] - atm->q[qnt_idx][ip]) * mixparam[ip];
5692 }
5693 }
5694
5695 /* Free... */
5696#ifdef _OPENACC
5697#pragma acc exit data delete(cmean,cweight)
5698#endif
5699 free(cmean);
5700 free(cweight);
5701}

◆ module_oh_chem()

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.

This function calculates the OH chemistry for each atmospheric particle based on the specified reaction mechanism and updates the particle quantities accordingly. The OH chemistry includes bimolecular and termolecular reactions, and the reaction rates are calculated based on the provided climatological data and atmospheric conditions. The function supports both mass and volume mixing ratio quantities for the particles.

Parameters
ctlPointer to the control structure containing simulation parameters.
cachePointer to the cache structure for temporary data and random numbers.
climPointer to the climate data structure containing climatological data.
met0Pointer to the first meteorological data structure.
met1Pointer to the second meteorological data structure.
atmPointer to the atmospheric data structure containing particle information.
Note
The function assumes that the necessary meteorological and climatological data structures have been initialized and are accessible via the pointers met0, met1, and clim, respectively.
The reaction rates are calculated based on the provided reaction mechanism and atmospheric conditions, including temperature, pressure, and the concentrations of relevant species.
The function updates the particle quantities based on the calculated reaction rates and the specified time steps. The update can include both mass and volume mixing ratio quantities, as determined by the control structure (ctl).
Author
Lars Hoffmann
Mingzhao Liu

Definition at line 5705 of file mptrac.c.

5711 {
5712
5713 /* Set timer... */
5714 SELECT_TIMER("MODULE_OH_CHEM", "PHYSICS");
5715
5716 /* Check quantity flags... */
5717 if (ctl->qnt_m < 0 && ctl->qnt_vmr < 0)
5718 ERRMSG("Module needs quantity mass or volume mixing ratio!");
5719
5720 /* Parameter of SO2 correction... */
5721 const double a = 4.71572206e-08;
5722 const double b = -8.28782867e-01;
5723 const double low = pow(1. / a, 1. / b);
5724
5725 /* Loop over particles... */
5726 PARTICLE_LOOP(0, atm->np, 1,
5727 "acc data present(ctl,cache,clim,met0,met1,atm)") {
5728
5729 /* Get temperature... */
5730 double t;
5732 INTPOL_3D(t, 1);
5733
5734 /* Calculate molecular density... */
5735 const double M = MOLEC_DENS(atm->p[ip], t);
5736
5737 /* Use constant reaction rate... */
5738 double k = NAN;
5739 if (ctl->oh_chem_reaction == 1)
5740 k = ctl->oh_chem[0];
5741
5742 /* Calculate bimolecular reaction rate... */
5743 else if (ctl->oh_chem_reaction == 2)
5744 k = ctl->oh_chem[0] * exp(-ctl->oh_chem[1] / t);
5745
5746 /* Calculate termolecular reaction rate... */
5747 if (ctl->oh_chem_reaction == 3) {
5748
5749 /* Calculate rate coefficient for X + OH + M -> XOH + M
5750 (JPL Publication 19-05) ... */
5751 const double k0 =
5752 ctl->oh_chem[0] * (ctl->oh_chem[1] !=
5753 0 ? pow(298. / t, ctl->oh_chem[1]) : 1.);
5754 const double ki =
5755 ctl->oh_chem[2] * (ctl->oh_chem[3] !=
5756 0 ? pow(298. / t, ctl->oh_chem[3]) : 1.);
5757 const double c = log10(k0 * M / ki);
5758 k = k0 * M / (1. + k0 * M / ki) * pow(0.6, 1. / (1. + c * c));
5759 }
5760
5761 /* Correction factor for high SO2 concentration
5762 (if qnt_Cx is defined, the correction is switched on)... */
5763 double cor = 1;
5764 if (ctl->qnt_Cx >= 0)
5765 cor =
5766 atm->q[ctl->qnt_Cx][ip] >
5767 low ? a * pow(atm->q[ctl->qnt_Cx][ip], b) : 1;
5768
5769 /* Calculate exponential decay... */
5770 const double rate_coef =
5771 k * clim_oh(ctl, clim, atm->time[ip], atm->lon[ip],
5772 atm->lat[ip], atm->p[ip]) * M * cor;
5773 const double aux = exp(-cache->dt[ip] * rate_coef);
5774 if (ctl->qnt_m >= 0) {
5775 if (ctl->qnt_mloss_oh >= 0)
5776 atm->q[ctl->qnt_mloss_oh][ip]
5777 += atm->q[ctl->qnt_m][ip] * (1 - aux);
5778 atm->q[ctl->qnt_m][ip] *= aux;
5779 if (ctl->qnt_loss_rate >= 0)
5780 atm->q[ctl->qnt_loss_rate][ip] += rate_coef;
5781 }
5782 if (ctl->qnt_vmr >= 0)
5783 atm->q[ctl->qnt_vmr][ip] *= aux;
5784 }
5785}
double oh_chem[4]
Coefficients for OH reaction rate (A, E/R or k0, n, kinf, m).
Definition mptrac.h:3260
int oh_chem_reaction
Reaction type for OH chemistry (0=none, 2=bimolecular, 3=termolecular).
Definition mptrac.h:3257
int qnt_mloss_oh
Quantity array index for total mass loss due to OH chemistry.
Definition mptrac.h:2686
Here is the call graph for this function:

◆ module_position()

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.

This function updates the positions and pressure levels of atmospheric particles based on the meteorological data and the specified time step. It loops over each particle in the atmospheric data structure and performs the following operations:

  • Initializes variables required for interpolation.
  • Calculates modulo for longitude and latitude to ensure they remain within valid ranges.
  • Adjusts latitude if it exceeds the range [-90, 90] degrees.
  • Adjusts longitude if it exceeds the range [-180, 180] degrees.
  • Clamps or reflects pressure at the model top and local surface, as selected by reflect.
Parameters
cachePointer to the cache structure for temporary data and random numbers.
met0Pointer to the first meteorological data structure.
met1Pointer to the second meteorological data structure.
atmPointer to the atmospheric data structure containing particle information.
reflectSelect reflection (1) or clamping (0) at vertical boundaries.
Note
The function initializes a timer to measure the execution time of the position update process.
Position and pressure updates are performed for each particle using linear interpolation.
Longitude and latitude are adjusted to ensure they remain within valid ranges.
Author
Lars Hoffmann

Definition at line 5789 of file mptrac.c.

5794 {
5795
5796 /* Set timer... */
5797 SELECT_TIMER("MODULE_POSITION", "PHYSICS");
5798
5799 /* Loop over particles... */
5800 PARTICLE_LOOP(0, atm->np, 1, "acc data present(cache,met0,met1,atm)") {
5801
5802 /* Init... */
5803 double ps;
5805
5806 if (met0->coord_type == 0) {
5807 /* Calculate modulo... */
5808 atm->lon[ip] = FMOD(atm->lon[ip], 360.);
5809 atm->lat[ip] = FMOD(atm->lat[ip], 360.);
5810
5811 /* Check latitude... */
5812 while (atm->lat[ip] < -90 || atm->lat[ip] > 90) {
5813 if (atm->lat[ip] > 90) {
5814 atm->lat[ip] = 180 - atm->lat[ip];
5815 atm->lon[ip] += 180;
5816 }
5817 if (atm->lat[ip] < -90) {
5818 atm->lat[ip] = -180 - atm->lat[ip];
5819 atm->lon[ip] += 180;
5820 }
5821 }
5822
5823 /* Check longitude... */
5824 while (atm->lon[ip] < -180)
5825 atm->lon[ip] += 360;
5826 while (atm->lon[ip] >= 180)
5827 atm->lon[ip] -= 360;
5828 } else {
5829 intpol_check_cartesian(met0->lon, met0->nx, met0->lat, met0->ny,
5830 atm->lon[ip], atm->lat[ip], &atm->lon[ip],
5831 &atm->lat[ip]);
5832 }
5833
5834 /* Check pressure... */
5835 const double ptop = met0->p[met0->np - 1];
5836 if (atm->p[ip] < ptop) {
5837 atm->p[ip] = reflect ? ptop * ptop / atm->p[ip] : ptop;
5838 } else if (atm->p[ip] > 300.) {
5839 INTPOL_2D(ps, 1);
5840 if (atm->p[ip] > ps)
5841 atm->p[ip] = reflect ? ps * ps / atm->p[ip] : ps;
5842 }
5843 }
5844}
Here is the call graph for this function:

◆ module_radio_decay()

void module_radio_decay ( const ctl_t * ctl,
const cache_t * cache,
atm_t * atm )

Apply radioactive decay to atmospheric tracer species.

This routine updates the concentrations of radioactive tracers carried by atmospheric particles by applying exponential decay over the current particle timestep. The decay constants are derived from the half-lives of the respective isotopes.

Implemented isotopes:

  • Rn-222
  • Pb-210
  • Be-7
  • Cs-137
  • I-131
  • Xe-133

For each particle, the tracer mixing ratios are reduced according to \( q(t+\Delta t) = q(t) \exp(-\lambda \Delta t) \), where \(\lambda\) is the decay constant and \(\Delta t\) is the particle timestep.

Additionally, the decay of Rn-222 contributes to the production of Pb-210 via a simplified parent–daughter relationship.

The update is performed only if the corresponding tracer index in the control structure is non-negative.

Parameters
[in]ctlControl structure containing tracer indices.
[in]cacheCache structure providing particle timesteps.
[in,out]atmAtmospheric state containing particle tracer fields that are updated in place.
Author
Lars Hoffmann

Definition at line 5848 of file mptrac.c.

5851 {
5852
5853 /* Set timer... */
5854 SELECT_TIMER("MODULE_RADIO_DECAY", "PHYSICS");
5855
5856 /* Set decay constants of radioactive species [s^-1]... */
5857 const double lambda_rn222 = log(2.0) / RADIO_HALF_LIFE_RN222;
5858 const double lambda_pb210 = log(2.0) / RADIO_HALF_LIFE_PB210;
5859 const double lambda_be7 = log(2.0) / RADIO_HALF_LIFE_BE7;
5860 const double lambda_cs137 = log(2.0) / RADIO_HALF_LIFE_CS137;
5861 const double lambda_i131 = log(2.0) / RADIO_HALF_LIFE_I131;
5862 const double lambda_xe133 = log(2.0) / RADIO_HALF_LIFE_XE133;
5863
5864 /* Loop over particles... */
5865 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,atm)") {
5866
5867 /* Set timestep... */
5868 const double dt = cache->dt[ip];
5869
5870 /* Loss for Pb-210... */
5871 if (ctl->qnt_Apb210 >= 0)
5872 atm->q[ctl->qnt_Apb210][ip] *= exp(-dt * lambda_pb210);
5873
5874 /* Loss for Rn-222... */
5875 if (ctl->qnt_Arn222 >= 0) {
5876 const double old = atm->q[ctl->qnt_Arn222][ip];
5877 const double aux = exp(-dt * lambda_rn222);
5878 const double lost = old * (1.0 - aux);
5879 atm->q[ctl->qnt_Arn222][ip] = old * aux;
5880
5881 /* Parent-daughter process for Pb-210... */
5882 if (ctl->qnt_Apb210 >= 0)
5883 atm->q[ctl->qnt_Apb210][ip] += lost * lambda_pb210 / lambda_rn222;
5884 }
5885
5886 /* Loss for Be-7... */
5887 if (ctl->qnt_Abe7 >= 0)
5888 atm->q[ctl->qnt_Abe7][ip] *= exp(-dt * lambda_be7);
5889
5890 /* Loss for Cs-137... */
5891 if (ctl->qnt_Acs137 >= 0)
5892 atm->q[ctl->qnt_Acs137][ip] *= exp(-dt * lambda_cs137);
5893
5894 /* Loss for I-131... */
5895 if (ctl->qnt_Ai131 >= 0)
5896 atm->q[ctl->qnt_Ai131][ip] *= exp(-dt * lambda_i131);
5897
5898 /* Loss for Xe-133... */
5899 if (ctl->qnt_Axe133 >= 0)
5900 atm->q[ctl->qnt_Axe133][ip] *= exp(-dt * lambda_xe133);
5901 }
5902}
#define RADIO_HALF_LIFE_CS137
Half-life of Cs-137 [s].
Definition mptrac.h:480
#define RADIO_HALF_LIFE_I131
Half-life of I-131 [s].
Definition mptrac.h:485
#define RADIO_HALF_LIFE_BE7
Half-life of Be-7 [s].
Definition mptrac.h:475
#define RADIO_HALF_LIFE_RN222
Half-life of Rn-222 [s].
Definition mptrac.h:465
#define RADIO_HALF_LIFE_PB210
Half-life of Pb-210 [s].
Definition mptrac.h:470
#define RADIO_HALF_LIFE_XE133
Half-life of Xe-133 [s].
Definition mptrac.h:490

◆ module_radio_depo()

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.

Applies fixed dry-deposition velocities in the surface layer and precipitation-dependent wet scavenging to Apb210, Abe7, Acs137, and Ai131. Air-parcel activities are reduced exponentially and the removed activity is accumulated in depo. When RADIO_DECAY is enabled, ground inventories are referenced to the simulation start for decay-correct output. Backward or zero-length particle timesteps are ignored.

Note
This module requires a latitude/longitude meteorological grid and is not currently supported with domain decomposition (DD).
Parameters
[in]ctlControl parameters, quantity indices, and deposition grid.
[in]cachePer-particle model timesteps.
[in]met0Meteorological field at the earlier bracketing time.
[in]met1Meteorological field at the later bracketing time.
[in,out]atmAir-parcel positions and activities [Bq].
[in,out]depoCumulative ground inventories [Bq per grid cell].

Definition at line 5906 of file mptrac.c.

5912 {
5913
5914 /* Set timer... */
5915 SELECT_TIMER("MODULE_RADIO_DEPO", "PHYSICS");
5916
5917 /* Decay constants of deposited radionuclides [s^-1]... */
5918 const double lambda_pb210 = log(2.0) / RADIO_HALF_LIFE_PB210;
5919 const double lambda_be7 = log(2.0) / RADIO_HALF_LIFE_BE7;
5920 const double lambda_cs137 = log(2.0) / RADIO_HALF_LIFE_CS137;
5921 const double lambda_i131 = log(2.0) / RADIO_HALF_LIFE_I131;
5922
5923 /* Set horizontal grid increments... */
5924 const double dlon = (ctl->grid_lon1 - ctl->grid_lon0) / ctl->grid_nx;
5925 const double dlat = (ctl->grid_lat1 - ctl->grid_lat0) / ctl->grid_ny;
5926
5927 /* Loop over particles... */
5928 PARTICLE_LOOP(0, atm->np, 1,
5929 "acc data present(ctl,cache,met0,met1,atm,depo)") {
5930
5931 /* Deposition is only defined for forward integration... */
5932 const double dt = cache->dt[ip];
5933 if (dt <= 0)
5934 continue;
5935
5936 /* Get surface pressure... */
5937 double ps;
5939 INTPOL_2D(ps, 1);
5940
5941 /* Calculate dry deposition rate [s^-1]... */
5942 double dry_pb210 = 0, dry_be7 = 0, dry_cs137 = 0, dry_i131 = 0;
5943 if (atm->p[ip] >= ps - ctl->dry_depo_dp) {
5944 const double dz = 1000. * (Z(ps - ctl->dry_depo_dp) - Z(ps));
5945 if (dz > 0) {
5946 dry_pb210 = RADIO_DRY_VDEP_PB210 / dz;
5947 dry_be7 = RADIO_DRY_VDEP_BE7 / dz;
5948 dry_cs137 = RADIO_DRY_VDEP_CS137 / dz;
5949 dry_i131 = RADIO_DRY_VDEP_I131 / dz;
5950 }
5951 }
5952
5953 /* Calculate wet deposition rate [s^-1]... */
5954 double wet_pb210 = 0, wet_be7 = 0, wet_cs137 = 0, wet_i131 = 0;
5955 double pct;
5956 INTPOL_2D(pct, 1);
5957 if (isfinite(pct) && atm->p[ip] > pct) {
5958 double cl;
5959 INTPOL_2D(cl, 0);
5960 if (cl > 0) {
5961 const double Is =
5962 pow(cl / ctl->wet_depo_pre[0], 1. / ctl->wet_depo_pre[1]);
5963 if (Is >= 0.01) {
5964 double lwc, rwc, iwc, swc, t;
5965 INTPOL_3D(lwc, 1);
5966 INTPOL_3D(rwc, 0);
5967 INTPOL_3D(iwc, 0);
5968 INTPOL_3D(swc, 0);
5969 INTPOL_3D(t, 0);
5970 const int inside = (lwc > 0 || rwc > 0 || iwc > 0 || swc > 0);
5971 double eta;
5972 if (inside) {
5973 if (t > WET_DEPO_T_LIQUID)
5974 eta = 1;
5975 else if (t <= WET_DEPO_T_ICE)
5976 eta = ctl->wet_depo_ic_ret_ratio;
5977 else
5979 ctl->wet_depo_ic_ret_ratio, t);
5980 } else
5981 eta = (t > WET_DEPO_T_LIQUID_BC ? 1 : ctl->wet_depo_bc_ret_ratio);
5982 wet_pb210 = RADIO_WET_COEFF_PB210 * Is * eta;
5983 wet_be7 = RADIO_WET_COEFF_BE7 * Is * eta;
5984 wet_cs137 = RADIO_WET_COEFF_CS137 * Is * eta;
5985 wet_i131 = RADIO_WET_COEFF_I131 * Is * eta;
5986 }
5987 }
5988 }
5989
5990 /* Get deposition grid index... */
5991 const int ingrid =
5992 (atm->lon[ip] >= ctl->grid_lon0
5993 && atm->lon[ip] < ctl->grid_lon1
5994 && atm->lat[ip] >= ctl->grid_lat0 && atm->lat[ip] < ctl->grid_lat1);
5995 const int ix = ingrid
5996 ? (int) ((atm->lon[ip] - ctl->grid_lon0) / dlon) : 0;
5997 const int iy = ingrid
5998 ? (int) ((atm->lat[ip] - ctl->grid_lat0) / dlat) : 0;
5999 const int idx = (ingrid ? ARRAY_2D(ix, iy, ctl->grid_ny) : 0);
6000 const double tref = atm->time[ip] - ctl->t_start;
6001
6002 /* Deposit Pb-210... */
6003 if (ctl->qnt_Apb210 >= 0) {
6004 const double old = atm->q[ctl->qnt_Apb210][ip];
6005 const double aux = exp(-dt * (dry_pb210 + wet_pb210));
6006 const double lost = old * (1. - aux);
6007 const double deposited = lost
6008 * (ctl->radio_decay ? exp(lambda_pb210 * tref) : 1.0);
6009 atm->q[ctl->qnt_Apb210][ip] = old * aux;
6010 if (ingrid && lost > 0) {
6011#ifdef _OPENACC
6012#pragma acc atomic update
6013#else
6014#pragma omp atomic update
6015#endif
6016 depo->Apb210[idx] += deposited;
6017 }
6018 }
6019
6020 /* Deposit Be-7... */
6021 if (ctl->qnt_Abe7 >= 0) {
6022 const double old = atm->q[ctl->qnt_Abe7][ip];
6023 const double aux = exp(-dt * (dry_be7 + wet_be7));
6024 const double lost = old * (1. - aux);
6025 const double deposited = lost
6026 * (ctl->radio_decay ? exp(lambda_be7 * tref) : 1.0);
6027 atm->q[ctl->qnt_Abe7][ip] = old * aux;
6028 if (ingrid && lost > 0) {
6029#ifdef _OPENACC
6030#pragma acc atomic update
6031#else
6032#pragma omp atomic update
6033#endif
6034 depo->Abe7[idx] += deposited;
6035 }
6036 }
6037
6038 /* Deposit Cs-137... */
6039 if (ctl->qnt_Acs137 >= 0) {
6040 const double old = atm->q[ctl->qnt_Acs137][ip];
6041 const double aux = exp(-dt * (dry_cs137 + wet_cs137));
6042 const double lost = old * (1. - aux);
6043 const double deposited = lost
6044 * (ctl->radio_decay ? exp(lambda_cs137 * tref) : 1.0);
6045 atm->q[ctl->qnt_Acs137][ip] = old * aux;
6046 if (ingrid && lost > 0) {
6047#ifdef _OPENACC
6048#pragma acc atomic update
6049#else
6050#pragma omp atomic update
6051#endif
6052 depo->Acs137[idx] += deposited;
6053 }
6054 }
6055
6056 /* Deposit aerosol-bound I-131... */
6057 if (ctl->qnt_Ai131 >= 0) {
6058 const double old = atm->q[ctl->qnt_Ai131][ip];
6059 const double aux = exp(-dt * (dry_i131 + wet_i131));
6060 const double lost = old * (1. - aux);
6061 const double deposited = lost
6062 * (ctl->radio_decay ? exp(lambda_i131 * tref) : 1.0);
6063 atm->q[ctl->qnt_Ai131][ip] = old * aux;
6064 if (ingrid && lost > 0) {
6065#ifdef _OPENACC
6066#pragma acc atomic update
6067#else
6068#pragma omp atomic update
6069#endif
6070 depo->Ai131[idx] += deposited;
6071 }
6072 }
6073 }
6074}
#define RADIO_DRY_VDEP_I131
Dry deposition velocity of aerosol-bound I-131 [m/s].
Definition mptrac.h:510
#define RADIO_DRY_VDEP_CS137
Dry deposition velocity of Cs-137 [m/s].
Definition mptrac.h:505
#define RADIO_DRY_VDEP_PB210
Dry deposition velocity of Pb-210 [m/s].
Definition mptrac.h:495
#define ARRAY_2D(ix, iy, ny)
Macro for computing the linear index of a 2D array element.
Definition mptrac.h:692
#define WET_DEPO_T_ICE
Lower temperature of the ice-cloud retention transition [K].
Definition mptrac.h:455
#define RADIO_WET_COEFF_BE7
Wet deposition coefficient of Be-7 [s^-1].
Definition mptrac.h:520
#define RADIO_WET_COEFF_CS137
Wet deposition coefficient of Cs-137 [s^-1].
Definition mptrac.h:525
#define RADIO_WET_COEFF_I131
Wet deposition coefficient of aerosol-bound I-131 [s^-1].
Definition mptrac.h:530
#define RADIO_DRY_VDEP_BE7
Dry deposition velocity of Be-7 [m/s].
Definition mptrac.h:500
#define WET_DEPO_T_LIQUID
Upper temperature of the ice-cloud retention transition [K].
Definition mptrac.h:450
#define WET_DEPO_T_LIQUID_BC
Temperature threshold for below-cloud retention [K].
Definition mptrac.h:460
#define RADIO_WET_COEFF_PB210
Wet deposition coefficient of Pb-210 [s^-1].
Definition mptrac.h:515
int grid_nx
Number of longitudes of gridded data.
Definition mptrac.h:3465
double grid_lat0
Lower latitude of gridded data [deg].
Definition mptrac.h:3477
double grid_lon0
Lower longitude of gridded data [deg].
Definition mptrac.h:3468
int radio_decay
RADIO_DECAY switch for airborne and deposited activity (0=off, 1=on, default: 0).
Definition mptrac.h:3278
double grid_lon1
Upper longitude of gridded data [deg].
Definition mptrac.h:3471
double wet_depo_pre[2]
Coefficients for precipitation calculation.
Definition mptrac.h:3284
int grid_ny
Number of latitudes of gridded data.
Definition mptrac.h:3474
double wet_depo_bc_ret_ratio
Coefficients for wet deposition below cloud: retention ratio.
Definition mptrac.h:3311
double wet_depo_ic_ret_ratio
Coefficients for wet deposition in cloud: retention ratio.
Definition mptrac.h:3308
double t_start
Start time of simulation [s].
Definition mptrac.h:2851
double grid_lat1
Upper latitude of gridded data [deg].
Definition mptrac.h:3480
double Ai131[EX *EY]
Deposited I-131 activity [Bq].
Definition mptrac.h:3718
double Abe7[EX *EY]
Deposited Be-7 activity [Bq].
Definition mptrac.h:3712
double Acs137[EX *EY]
Deposited Cs-137 activity [Bq].
Definition mptrac.h:3715

◆ module_rng_init()

void module_rng_init ( const int ntask)

Initialize random number generators for parallel tasks.

This function initializes random number generators for parallel tasks using GSL (GNU Scientific Library), Squares, and cuRAND (NVIDIA CUDA Random Number Generation Library) if available. GSL generators are seeded per OpenMP thread, cuRAND is seeded per task, and Squares starts at a task-dependent counter offset.

Parameters
ntaskTask identifier used to initialize the generator streams.
Note
This function must be called before using any random number generation functions to ensure proper initialization of random number generators.
GSL random number generators are initialized for each OpenMP thread, while cuRAND is initialized per task.
If cuRAND is not available (CURAND macro not defined), the cuRAND initialization section is skipped.
Squares spaces initial counters by 2^48 values; task 0 retains the original sequence. Streams can overlap after that many draws, counter wraparound, or reuse of task identifiers.
Author
Lars Hoffmann

Definition at line 6078 of file mptrac.c.

6079 {
6080
6081 /* Separate Squares counter ranges by task (task 0 keeps its sequence)... */
6082 rng_ctr = (uint64_t) (unsigned int) ntask << 48;
6083
6084 /* Initialize GSL random number generators... */
6085 gsl_rng_env_setup();
6086 if (omp_get_max_threads() > NTHREADS)
6087 ERRMSG("Too many threads!");
6088 for (int i = 0; i < NTHREADS; i++) {
6089 rng[i] = gsl_rng_alloc(gsl_rng_default);
6090 gsl_rng_set(rng[i], gsl_rng_default_seed
6091 + (long unsigned) (ntask * NTHREADS + i));
6092 }
6093
6094 /* Initialize cuRAND random number generators... */
6095#ifdef CURAND
6096 if (curandCreateGenerator(&rng_curand, CURAND_RNG_PSEUDO_DEFAULT) !=
6097 CURAND_STATUS_SUCCESS)
6098 ERRMSG("Cannot create random number generator!");
6099 if (curandSetPseudoRandomGeneratorSeed(rng_curand, ntask) !=
6100 CURAND_STATUS_SUCCESS)
6101 ERRMSG("Cannot set seed for random number generator!");
6102 if (curandSetStream
6103 (rng_curand,
6104 (cudaStream_t) acc_get_cuda_stream(acc_async_sync)) !=
6105 CURAND_STATUS_SUCCESS)
6106 ERRMSG("Cannot set stream for random number generator!");
6107#endif
6108}
#define NTHREADS
Maximum number of OpenMP threads.
Definition mptrac.h:594

◆ module_rng()

void module_rng ( const ctl_t * ctl,
double * rs,
const size_t n,
const int method )

Generate random numbers using various methods and distributions.

This function generates random numbers using different methods and distributions based on the specified method and random number generator type. It supports uniform and normal distributions and can utilize GSL, Squares (Widynski, 2022), or cuRAND random number generators.

Parameters
ctlPointer to the control structure containing parameters and settings.
rsPointer to the array where the generated random numbers will be stored.
nThe number of random numbers to generate.
methodThe method for generating random numbers:
  • 0: Uniform distribution
  • 1: Normal distribution
Note
The function selects the appropriate random number generator based on the specified method and the random number generator type defined in the control structure (ctl->rng_type).
For uniform distribution, the generated random numbers are in the range [0, 1).
For normal distribution, the Box-Muller transform is used to generate pairs of random numbers and transform them into a normal distribution.
If cuRAND is not available (CURAND macro not defined), the function returns an error message.
Author
Lars Hoffmann

Definition at line 6112 of file mptrac.c.

6116 {
6117
6118 /* Use GSL random number generators... */
6119 if (ctl->rng_type == 0) {
6120
6121 /* Uniform distribution... */
6122 if (method == 0) {
6123#pragma omp parallel for default(shared)
6124 for (size_t i = 0; i < n; ++i)
6125 rs[i] = gsl_rng_uniform(rng[omp_get_thread_num()]);
6126 }
6127
6128 /* Normal distribution... */
6129 else if (method == 1) {
6130#pragma omp parallel for default(shared)
6131 for (size_t i = 0; i < n; ++i)
6132 rs[i] = gsl_ran_gaussian_ziggurat(rng[omp_get_thread_num()], 1.0);
6133 }
6134
6135 /* Update of random numbers on device... */
6136#ifdef _OPENACC
6137 SELECT_TIMER("UPDATE_DEVICE", "MEMORY");
6138#pragma acc update device(rs[:n])
6139#endif
6140 }
6141
6142 /* Use Squares random number generator (Widynski, 2022)... */
6143 else if (ctl->rng_type == 1) {
6144
6145 /* Set key (don't change this!)... */
6146 const uint64_t key = 0xc8e4fd154ce32f6d;
6147
6148 /* Uniform distribution... */
6149#ifdef _OPENACC
6150#pragma acc data present(rs)
6151#pragma acc parallel loop independent gang vector
6152#else
6153#pragma omp parallel for default(shared)
6154#endif
6155 for (size_t i = 0; i < n + 1; ++i) {
6156 uint64_t r, t, x, y, z;
6157 y = x = (rng_ctr + i) * key;
6158 z = y + key;
6159 x = x * x + y;
6160 x = (x >> 32) | (x << 32);
6161 x = x * x + z;
6162 x = (x >> 32) | (x << 32);
6163 x = x * x + y;
6164 x = (x >> 32) | (x << 32);
6165 t = x = x * x + z;
6166 x = (x >> 32) | (x << 32);
6167 r = t ^ ((x * x + y) >> 32);
6168 rs[i] = (double) r / (double) UINT64_MAX;
6169 }
6170 rng_ctr += n + 1;
6171
6172 /* Normal distribution... */
6173 if (method == 1) {
6174#ifdef _OPENACC
6175#pragma acc parallel loop independent gang vector
6176#else
6177#pragma omp parallel for default(shared)
6178#endif
6179 for (size_t i = 0; i < n; i += 2) {
6180 const double r = sqrt(-2.0 * log(rs[i]));
6181 const double phi = 2.0 * M_PI * rs[i + 1];
6182 rs[i] = r * cosf((float) phi);
6183 rs[i + 1] = r * sinf((float) phi);
6184 }
6185 }
6186 }
6187
6188 /* Use cuRAND random number generators... */
6189 else if (ctl->rng_type == 2) {
6190#ifdef CURAND
6191#pragma acc host_data use_device(rs)
6192 {
6193
6194 /* Uniform distribution... */
6195 if (method == 0) {
6196 if (curandGenerateUniformDouble(rng_curand, rs, (n < 4 ? 4 : n)) !=
6197 CURAND_STATUS_SUCCESS)
6198 ERRMSG("Cannot create random numbers!");
6199 }
6200
6201 /* Normal distribution... */
6202 else if (method == 1 && n > 0) {
6203 if (n > 3 * (size_t) NP)
6204 ERRMSG("Too many random numbers requested!");
6205 const size_t n_generate = (n < 4 ? 4 : n + (n & 1));
6206 if (curandGenerateNormalDouble
6207 (rng_curand, rs, n_generate, 0.0,
6208 1.0) != CURAND_STATUS_SUCCESS)
6209 ERRMSG("Cannot create random numbers!");
6210 }
6211 }
6212#else
6213 ERRMSG("MPTRAC was compiled without cuRAND!");
6214#endif
6215 }
6216}
int rng_type
Random number generator (0=GSL, 1=Squares, 2=cuRAND).
Definition mptrac.h:3059

◆ module_sedi()

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.

This function calculates the sedimentation velocity of particles based on atmospheric pressure, temperature, and particle properties such as radius and density. It then updates the pressure of each particle based on the sedimentation velocity and the specified time step.

Parameters
ctlPointer to the control structure containing parameters and settings.
cachePointer to the cache structure for temporary data and random numbers.
met0Pointer to the meteorological data at the current time step.
met1Pointer to the meteorological data at the next time step.
atmPointer to the atmospheric data containing particle information.
Note
The sedimentation velocity is calculated using the sedi function, which takes atmospheric pressure, temperature, particle radius, and particle density as inputs.
The pressure change for each particle is calculated based on the sedimentation velocity and the specified time step using the DZ2DP function.
Author
Lars Hoffmann

Definition at line 6220 of file mptrac.c.

6225 {
6226
6227 /* Set timer... */
6228 SELECT_TIMER("MODULE_SEDI", "PHYSICS")
6229 /* Loop over particles... */
6230 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
6231
6232 /* Get temperature... */
6233 double t;
6235 INTPOL_3D(t, 1);
6236
6237 /* Sedimentation velocity... */
6238 const double v_s = sedi(atm->p[ip], t, atm->q[ctl->qnt_rp][ip],
6239 atm->q[ctl->qnt_rhop][ip]);
6240
6241 /* Calculate pressure change... */
6242 atm->p[ip] -= DZ2DP(v_s * cache->dt[ip] / 1000., atm->p[ip]);
6243 }
6244}
Here is the call graph for this function:

◆ module_sort()

void module_sort ( const ctl_t * ctl,
const met_t * met0,
cache_t * cache,
atm_t * atm )

Sort particles according to box index.

This function sorts particles within the atmosphere data structure based on their geographical coordinates (longitude and latitude) and pressure level. It allocates temporary arrays to store indices and auxiliary data for sorting, then performs the sorting operation. After sorting, it updates the order of particles and their persistent particle-specific cache values (dt and uvwp). Random values in rs are regenerated before use; shared cache data remain unchanged.

Parameters
ctlPointer to the control structure containing parameters and settings.
met0Pointer to the meteorological data at the current time step.
cachePointer to the particle-specific cache data to reorder.
atmPointer to the atmospheric data containing particle information.
Note
The function utilizes the locate_reg and locate_irr functions to determine the appropriate index for sorting particles based on their longitude, latitude, and pressure level.
With THRUST enabled, particle indices are sorted using the optional Thrust wrapper. Otherwise, GSL (gsl_sort_index) computes the permutation on the CPU; in OpenACC builds, sorting keys are copied to the host and the resulting permutation is copied back to the device.
The calling physics driver invokes this routine when SORT_DT is positive and the time matches the configured interval, independently of DD_EXCHANGE_MODE. SORT_DT controls spatial sorting, whereas DD_SORT_DT controls sorting for the legacy DD exchange path (mode 0).
After sorting, the function updates the order of particle-related data arrays in the atmosphere data structure to maintain consistency.
Author
Lars Hoffmann

Definition at line 6248 of file mptrac.c.

6252 {
6253
6254 /* Set timer... */
6255 SELECT_TIMER("MODULE_SORT", "PHYSICS");
6256
6257 /* Allocate... */
6258 const int np = atm->np;
6259 double *restrict const a = (double *) malloc((size_t) np * sizeof(double));
6260 int *restrict const p = (int *) malloc((size_t) np * sizeof(int));
6261 if (a == NULL || p == NULL)
6262 ERRMSG("Out of memory!");
6263
6264#ifdef _OPENACC
6265#pragma acc enter data create(a[0:np],p[0:np])
6266#pragma acc data present(ctl,met0,cache,atm,a,p)
6267#endif
6268
6269 /* Get box index... */
6270#ifdef _OPENACC
6271#pragma acc parallel loop independent gang vector
6272#else
6273#pragma omp parallel for default(shared)
6274#endif
6275 for (int ip = 0; ip < np; ip++) {
6276 a[ip] =
6277 (double) ((locate_reg(met0->lon, met0->nx, atm->lon[ip]) * met0->ny +
6278 locate_irr(met0->lat, met0->ny, atm->lat[ip]))
6279 * met0->np + locate_irr(met0->p, met0->np, atm->p[ip]));
6280 p[ip] = ip;
6281 }
6282
6283 /* Sorting... */
6284#ifdef THRUST
6285#ifdef _OPENACC
6286#pragma acc host_data use_device(a,p)
6287#endif
6288 thrustSortWrapper(a, np, p);
6289#else
6290 size_t *perm_sz = (size_t *) malloc((size_t) np * sizeof(size_t));
6291 if (perm_sz == NULL)
6292 ERRMSG("Out of memory!");
6293#ifdef _OPENACC
6294#pragma acc update self(a[0:np])
6295#endif
6296 gsl_sort_index(perm_sz, a, 1, (size_t) np);
6297 for (int ip = 0; ip < np; ++ip)
6298 p[ip] = (int) perm_sz[ip];
6299 free(perm_sz);
6300#ifdef _OPENACC
6301#pragma acc update device(p[0:np])
6302#endif
6303#endif
6304
6305 /* Sort data... */
6306 sort_array(atm->time, p, a, np);
6307 sort_array(atm->p, p, a, np);
6308 sort_array(atm->lon, p, a, np);
6309 sort_array(atm->lat, p, a, np);
6310 for (int iq = 0; iq < ctl->nq; iq++)
6311 sort_array(atm->q[iq], p, a, np);
6312 sort_array(cache->dt, p, a, np);
6313 sort_uvwp(cache, p, a, np);
6314
6315 /* Free... */
6316#ifdef _OPENACC
6317#pragma acc exit data delete(a,p)
6318#endif
6319 free(a);
6320 free(p);
6321}
Here is the call graph for this function:

◆ module_timesteps()

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.

This function calculates the time steps for air parcels based on specified conditions, including the direction of simulation, start and stop times, and a given target time. It adjusts the time step for each air parcel accordingly and checks for horizontal boundary conditions of local meteorological data.

Parameters
ctlPointer to the control structure containing simulation parameters.
cachePointer to the cache structure for temporary data and random numbers.
met0Pointer to the initial meteorological data structure.
atmPointer to the atmospheric data structure containing air parcel information.
tThe target time for which time steps are calculated.
Note
The function sets the time step for each air parcel based on its current time relative to the start and stop times of the simulation, as well as the specified target time t.
It also checks for horizontal boundaries of local meteorological data and adjusts the time step accordingly if necessary.
Author
Lars Hoffmann

Definition at line 6325 of file mptrac.c.

6330 {
6331
6332 /* Set timer... */
6333 SELECT_TIMER("MODULE_TIMESTEPS", "PHYSICS");
6334
6335 const double latmin = gsl_stats_min(met0->lat, 1, (size_t) met0->ny),
6336 latmax = gsl_stats_max(met0->lat, 1, (size_t) met0->ny);
6337
6338 const int local =
6339 (fabs(met0->lon[met0->nx - 1] - met0->lon[0] - 360.0) >= 0.01);
6340
6341 /* Loop over particles... */
6342 PARTICLE_LOOP(0, atm->np, 0, "acc data present(ctl,cache,met0,atm)") {
6343
6344 /* Set time step for each air parcel... */
6345 if ((ctl->direction * (atm->time[ip] - ctl->t_start) >= 0
6346 && ctl->direction * (atm->time[ip] - ctl->t_stop) <= 0
6347 && ctl->direction * (atm->time[ip] - t) < 0))
6348 cache->dt[ip] = t - atm->time[ip];
6349 else
6350 cache->dt[ip] = 0.0;
6351
6352 /* Check horizontal boundaries of local meteo data... */
6353#ifdef DD
6354 /* With domain decomposition, the subdomain rather than the meteo grid
6355 defines the boundaries of a trajectory... */
6356 int dd = (ctl->dd) ? 0 : 1;
6357#else
6358 int dd = 1;
6359#endif
6360 if (dd) {
6361 if (local && (atm->lon[ip] <= met0->lon[0]
6362 || atm->lon[ip] >= met0->lon[met0->nx - 1]
6363 || atm->lat[ip] <= latmin || atm->lat[ip] >= latmax))
6364 cache->dt[ip] = 0.0;
6365 } else {
6366 if ((int) atm->q[ctl->qnt_current_subdomain][ip] == -1)
6367 cache->dt[ip] = 0;
6368 }
6369 }
6370}
float * local
Definition mptrac.c:10797
int direction
Direction flag (1=forward calculation, -1=backward calculation).
Definition mptrac.h:2848
double t_stop
Stop time of simulation [s].
Definition mptrac.h:2854

◆ module_timesteps_init()

void module_timesteps_init ( ctl_t * ctl,
const atm_t * atm )

Initialize start time and time interval for time-stepping.

This function initializes the start time and time interval for time-stepping based on the direction of simulation and the provided atmospheric data. It sets the start time according to the minimum or maximum time in the atmospheric data, depending on the simulation direction. Additionally, it checks the time interval and adjusts the start time accordingly for rounding purposes.

Parameters
ctlPointer to the control structure containing simulation parameters.
atmPointer to the atmospheric data structure containing air parcel information.
Note
The function sets the start time based on the direction of simulation and the minimum or maximum time in the atmospheric data.
It checks the time interval to ensure that there is a valid time range for simulation and adjusts the start time for rounding purposes.
Author
Lars Hoffmann

Definition at line 6374 of file mptrac.c.

6376 {
6377
6378 /* Set timer... */
6379 SELECT_TIMER("MODULE_TIMESTEPS_INIT", "PHYSICS");
6380
6381 /* Set start time... */
6382 if (ctl->direction == 1) {
6383 ctl->t_start = gsl_stats_min(atm->time, 1, (size_t) atm->np);
6384 if (ctl->t_stop > 1e99)
6385 ctl->t_stop = gsl_stats_max(atm->time, 1, (size_t) atm->np);
6386 } else {
6387 ctl->t_start = gsl_stats_max(atm->time, 1, (size_t) atm->np);
6388 if (ctl->t_stop > 1e99)
6389 ctl->t_stop = gsl_stats_min(atm->time, 1, (size_t) atm->np);
6390 }
6391
6392 /* Check time interval... */
6393 if (ctl->direction * (ctl->t_stop - ctl->t_start) <= 0)
6394 ERRMSG("Nothing to do! Check T_STOP and DIRECTION!");
6395
6396 /* Round start time... */
6397 if (ctl->direction == 1)
6398 ctl->t_start = floor(ctl->t_start / ctl->dt_mod) * ctl->dt_mod;
6399 else
6400 ctl->t_start = ceil(ctl->t_start / ctl->dt_mod) * ctl->dt_mod;
6401}

◆ module_tracer_chem()

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.

This function simulates chemical reactions involving atmospheric tracers, such as CFC-10, CFC-11, CFC-12, and N2O. It calculates the change in tracer concentrations over time based on reaction rates and environmental factors such as temperature, ozone concentration, solar zenith angle, and O(1D) volume mixing ratio.

Parameters
ctlPointer to the control structure containing simulation parameters.
cachePointer to the cache structure for temporary data and random numbers.
climPointer to the climatological data structure.
met0Pointer to the first meteorological data structure.
met1Pointer to the second meteorological data structure.
atmPointer to the atmospheric data structure containing particle information.
Note
The function assumes that the necessary control structure (ctl), climatological data structure (clim), meteorological data structures (met0, met1), and atmospheric data structure (atm) have been initialized and are accessible.
Chemical reactions involving CFC-10, CFC-11, CFC-12, and N2O are simulated for each particle in the atmospheric data structure.
The function calculates reaction rates based on temperature, solar zenith angle, total column ozone, and the volume mixing ratio of O(1D).
The exponential decay of tracer concentrations due to chemical reactions is calculated using reaction rate coefficients and the time step (dt) for each particle.
If the particle has a quantity flag for the tracer species (e.g., ctl->qnt_Cccl4, ctl->qnt_Cccl3f, ctl->qnt_Cccl2f2, ctl->qnt_Cn2o), the function updates the concentration of the tracer based on the exponential decay.
Author
Mingzhao Liu
Lars Hoffmann

Definition at line 6405 of file mptrac.c.

6411 {
6412
6413 if (ctl->met_coord_type != 0)
6414 ERRMSG("Only lat/lon grid supported");
6415
6416 /* Set timer... */
6417 SELECT_TIMER("MODULE_TRACER_CHEM", "PHYSICS");
6418
6419 /* Loop over particles... */
6420 PARTICLE_LOOP(0, atm->np, 1,
6421 "acc data present(ctl,cache,clim,met0,met1,atm)") {
6422
6423 /* Get temperature... */
6424 double t;
6426 INTPOL_3D(t, 1);
6427
6428 /* Get molecular density... */
6429 const double M = MOLEC_DENS(atm->p[ip], t);
6430
6431 /* Get total column ozone... */
6432 double o3c;
6433 INTPOL_2D(o3c, 1);
6434
6435 /* Get solar zenith angle... */
6436 const double sza =
6437 acos(cos_sza(atm->time[ip], atm->lon[ip], atm->lat[ip]));
6438
6439 /* Get O(1D) volume mixing ratio... */
6440 const double o1d =
6441 clim_zm(&clim->o1d, atm->time[ip], atm->lat[ip], atm->p[ip]);
6442
6443 /* Reactions for CFC-10... */
6444 if (ctl->qnt_Cccl4 >= 0) {
6445 const double K_o1d =
6447 const double K_hv = clim_photo(clim->photo.ccl4, &(clim->photo),
6448 atm->p[ip], sza, o3c);
6449 atm->q[ctl->qnt_Cccl4][ip] *= exp(-cache->dt[ip] * (K_hv + K_o1d));
6450 }
6451
6452 /* Reactions for CFC-11... */
6453 if (ctl->qnt_Cccl3f >= 0) {
6454 const double K_o1d =
6456 const double K_hv = clim_photo(clim->photo.ccl3f, &(clim->photo),
6457 atm->p[ip], sza, o3c);
6458 atm->q[ctl->qnt_Cccl3f][ip] *= exp(-cache->dt[ip] * (K_hv + K_o1d));
6459 }
6460
6461 /* Reactions for CFC-12... */
6462 if (ctl->qnt_Cccl2f2 >= 0) {
6463 const double K_o1d =
6465 const double K_hv = clim_photo(clim->photo.ccl2f2, &(clim->photo),
6466 atm->p[ip], sza, o3c);
6467 atm->q[ctl->qnt_Cccl2f2][ip] *= exp(-cache->dt[ip] * (K_hv + K_o1d));
6468 }
6469
6470 /* Reactions for N2O... */
6471 if (ctl->qnt_Cn2o >= 0) {
6472 const double K_o1d =
6474 const double K_hv = clim_photo(clim->photo.n2o, &(clim->photo),
6475 atm->p[ip], sza, o3c);
6476 atm->q[ctl->qnt_Cn2o][ip] *= exp(-cache->dt[ip] * (K_hv + K_o1d));
6477 }
6478 }
6479}
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
#define O1D_RATE_CFC12_B
O(1D) reaction temperature parameter for CFC-12 [K].
Definition mptrac.h:415
#define O1D_RATE_N2O_A
O(1D) reaction pre-factor for N2O [cm^3/s].
Definition mptrac.h:420
#define O1D_RATE_CFC12_A
O(1D) reaction pre-factor for CFC-12 [cm^3/s].
Definition mptrac.h:410
#define O1D_RATE_N2O_B
O(1D) reaction temperature parameter for N2O [K].
Definition mptrac.h:425
#define O1D_RATE_CCL4_B
O(1D) reaction temperature parameter for CCl4 [K].
Definition mptrac.h:395
#define O1D_RATE_CFC11_A
O(1D) reaction pre-factor for CFC-11 [cm^3/s].
Definition mptrac.h:400
#define O1D_RATE_CCL4_A
O(1D) reaction pre-factor for CCl4 [cm^3/s].
Definition mptrac.h:390
#define ARRHENIUS(a, b, t)
Calculate the Arrhenius rate constant.
Definition mptrac.h:736
#define O1D_RATE_CFC11_B
O(1D) reaction temperature parameter for CFC-11 [K].
Definition mptrac.h:405
double ccl2f2[CP][CSZA][CO3]
CCl2F2 photolysis rate [1/s].
Definition mptrac.h:3759
double ccl3f[CP][CSZA][CO3]
CCl3F photolysis rate [1/s].
Definition mptrac.h:3756
double n2o[CP][CSZA][CO3]
N2O photolysis rate [1/s].
Definition mptrac.h:3750
double ccl4[CP][CSZA][CO3]
CCl4 photolysis rate [1/s].
Definition mptrac.h:3753
clim_photo_t photo
Photolysis rates.
Definition mptrac.h:3855
Here is the call graph for this function:

◆ module_wet_depo()

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.

This function calculates the wet deposition process for each air parcel based on provided atmospheric and meteorological data. It estimates the precipitation rate and scavenging coefficients for particles and gases inside and below cloud layers. The scavenging coefficients are used to calculate the exponential decay of mass or volume mixing ratio over time due to wet deposition.

Parameters
ctlPointer to the control structure containing simulation parameters.
cachePointer to the cache structure for temporary data and random numbers.
met0Pointer to the initial meteorological data structure.
met1Pointer to the updated meteorological data structure.
atmPointer to the atmospheric data structure containing air parcel information.
Note
The function calculates the wet deposition process for particles and gases based on precipitation rate and scavenging coefficients inside and below cloud layers.
It estimates the exponential decay of mass or volume mixing ratio over time due to wet deposition.
For exponential form A and B coefficients see Bakels et al. (2024, Table B2).
For Henry's law constants see Sander (2023) or https://henrys-law.org/.
Some species (e.g. SO2) require an effective Henry constant to account not only for physical solubility in water but also for chemical reactions, such as hydrolysis and ionization, which significantly enhance dissolution in aqueous solutions.
Author
Lars Hoffmann
Mingzhao Liu

Definition at line 6483 of file mptrac.c.

6488 {
6489
6490 /* Set timer... */
6491 SELECT_TIMER("MODULE_WET_DEPO", "PHYSICS");
6492
6493 /* Check quantity flags... */
6494 if (ctl->qnt_m < 0 && ctl->qnt_vmr < 0)
6495 ERRMSG("Module needs quantity mass or volume mixing ratio!");
6496
6497 /* Loop over particles... */
6498 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,cache,met0,met1,atm)") {
6499
6500 /* Check whether particle is below cloud top... */
6501 double pct;
6503 INTPOL_2D(pct, 1);
6504 if (!isfinite(pct) || atm->p[ip] <= pct)
6505 continue;
6506
6507 /* Get cloud bottom pressure... */
6508 double pcb;
6509 INTPOL_2D(pcb, 0);
6510
6511 /* Estimate precipitation rate (Pisso et al., 2019)... */
6512 double cl;
6513 INTPOL_2D(cl, 0);
6514 const double Is =
6515 pow(1. / ctl->wet_depo_pre[0] * cl, 1. / ctl->wet_depo_pre[1]);
6516 if (Is < 0.01)
6517 continue;
6518
6519 /* Check whether particle is inside or below cloud... */
6520 double lwc, rwc, iwc, swc;
6521 INTPOL_3D(lwc, 1);
6522 INTPOL_3D(rwc, 0);
6523 INTPOL_3D(iwc, 0);
6524 INTPOL_3D(swc, 0);
6525 const int inside = (lwc > 0 || rwc > 0 || iwc > 0 || swc > 0);
6526
6527 /* Get temperature... */
6528 double t;
6529 INTPOL_3D(t, 0);
6530
6531 /* Calculate in-cloud scavenging coefficient... */
6532 double lambda = 0;
6533 if (inside) {
6534
6535 /* Calculate retention factor... */
6536 double eta;
6537 if (t > WET_DEPO_T_LIQUID)
6538 eta = 1;
6539 else if (t <= WET_DEPO_T_ICE)
6540 eta = ctl->wet_depo_ic_ret_ratio;
6541 else
6542 eta =
6544 ctl->wet_depo_ic_ret_ratio, t);
6545
6546 /* Use exponential dependency for particles (Bakels et al., 2024)... */
6547 if (ctl->wet_depo_ic_a > 0)
6548 lambda = ctl->wet_depo_ic_a * pow(Is, ctl->wet_depo_ic_b) * eta;
6549
6550 /* Use Henry's law for gases... */
6551 else if (ctl->wet_depo_ic_h[0] > 0) {
6552
6553 /* Get Henry's constant (Burkholder et al., 2019; Sander, 2023)... */
6554 double h = ctl->wet_depo_ic_h[0]
6555 * exp(ctl->wet_depo_ic_h[1] * (1. / t - 1. / CHEM_REF_TEMP));
6556
6557 /* Use effective Henry's constant for SO2
6558 (Berglen, 2004; Simpson, 2012)... */
6559 if (ctl->wet_depo_so2_ph > 0) {
6560 const double H_ion = pow(10., -ctl->wet_depo_so2_ph);
6561 const double K_1 = SO2_DISS_K1_REF
6562 * exp(SO2_DISS_K1_TEMP * (1. / t - 1. / CHEM_REF_TEMP));
6563 const double K_2 = SO2_DISS_K2_REF
6564 * exp(SO2_DISS_K2_TEMP * (1. / t - 1. / CHEM_REF_TEMP));
6565 h *= (1. + K_1 / H_ion + K_1 * K_2 / SQR(H_ion));
6566 }
6567
6568 /* Estimate depth of cloud layer... */
6569 const double dz = 1e3 * (Z(pct) - Z(pcb));
6570
6571 /* Calculate scavenging coefficient... */
6572 lambda = h * RI * t * Is / 3.6e6 / dz * eta;
6573 }
6574 }
6575
6576 /* Calculate below-cloud scavenging coefficient... */
6577 else {
6578
6579 /* Calculate retention factor... */
6580 double eta;
6581 if (t > WET_DEPO_T_LIQUID_BC)
6582 eta = 1;
6583 else
6584 eta = ctl->wet_depo_bc_ret_ratio;
6585
6586 /* Use exponential dependency for particles (Bakels et al., 2024)... */
6587 if (ctl->wet_depo_bc_a > 0)
6588 lambda = ctl->wet_depo_bc_a * pow(Is, ctl->wet_depo_bc_b) * eta;
6589
6590 /* Use Henry's law for gases... */
6591 else if (ctl->wet_depo_bc_h[0] > 0) {
6592
6593 /* Get Henry's constant (Burkholder et al., 2019; Sander, 2023)... */
6594 const double h = ctl->wet_depo_bc_h[0]
6595 * exp(ctl->wet_depo_bc_h[1] * (1. / t - 1. / CHEM_REF_TEMP));
6596
6597 /* Estimate depth of cloud layer... */
6598 const double dz = 1e3 * (Z(pct) - Z(pcb));
6599
6600 /* Calculate scavenging coefficient... */
6601 lambda = h * RI * t * Is / 3.6e6 / dz * eta;
6602 }
6603 }
6604
6605 /* Calculate exponential decay of mass... */
6606 const double aux = exp(-cache->dt[ip] * lambda);
6607 if (ctl->qnt_m >= 0) {
6608 if (ctl->qnt_mloss_wet >= 0)
6609 atm->q[ctl->qnt_mloss_wet][ip]
6610 += atm->q[ctl->qnt_m][ip] * (1 - aux);
6611 atm->q[ctl->qnt_m][ip] *= aux;
6612 if (ctl->qnt_loss_rate >= 0)
6613 atm->q[ctl->qnt_loss_rate][ip] += lambda;
6614 }
6615 if (ctl->qnt_vmr >= 0)
6616 atm->q[ctl->qnt_vmr][ip] *= aux;
6617 }
6618}
#define SO2_DISS_K2_TEMP
Temperature dependence of the second SO2 dissociation constant [K].
Definition mptrac.h:445
#define SO2_DISS_K2_REF
Second SO2 dissociation constant at CHEM_REF_TEMP [mol/L].
Definition mptrac.h:440
double wet_depo_ic_a
Coefficient A for wet deposition in cloud (exponential form).
Definition mptrac.h:3293
double wet_depo_bc_a
Coefficient A for wet deposition below cloud (exponential form).
Definition mptrac.h:3287
int qnt_mloss_wet
Quantity array index for total mass loss due to wet deposition.
Definition mptrac.h:2695
double wet_depo_so2_ph
pH value used to calculate effective Henry constant of SO2.
Definition mptrac.h:3305
double wet_depo_bc_h[2]
Coefficients for wet deposition below cloud (Henry's law: Hb, Cb).
Definition mptrac.h:3302
double wet_depo_ic_h[2]
Coefficients for wet deposition in cloud (Henry's law: Hb, Cb).
Definition mptrac.h:3299
double wet_depo_ic_b
Coefficient B for wet deposition in cloud (exponential form).
Definition mptrac.h:3296
double wet_depo_bc_b
Coefficient B for wet deposition below cloud (exponential form).
Definition mptrac.h:3290

◆ mptrac_alloc()

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.

This function handles memory allocation for various data structures and sets up GPU resources if available. It also creates the necessary data regions on GPUs for OpenACC-enabled execution. Each output parameter is optional; pass NULL for structures that are not needed.

Parameters
[out]ctlPointer to the control structure (ctl_t).
[out]cachePointer to the cache structure (cache_t).
[out]climPointer to the climatology structure (clim_t).
[out]met0Pointer to the first meteorology structure (met_t).
[out]met1Pointer to the second meteorology structure (met_t).
[out]atmPointer to the atmospheric structure (atm_t).
[out]depoPointer to the radionuclide ground-inventory structure (depo_t).
[out]ddpointer to an dd_t structure containing MPI information, including rank and neighbours.
Note
This function uses OpenACC for GPU initialization and memory management. If OpenACC is not enabled, the GPU-specific code is ignored.
Precondition
The function requires an environment supporting OpenACC for GPU operations. MPI support is optional but assumed if compiled with MPI.
Postcondition
Allocated structures are ready for use, and data regions on GPUs are initialized if OpenACC is enabled.
Exceptions
Runtimeerror if no GPU devices are available when OpenACC is enabled.
Author
Lars Hoffmann

Definition at line 6622 of file mptrac.c.

6630 {
6631
6632 /* Initialize GPU... */
6633#ifdef _OPENACC
6634 if (ctl != NULL || cache != NULL || clim != NULL || met0 != NULL
6635 || met1 != NULL || atm != NULL || depo != NULL || dd != NULL) {
6636 SELECT_TIMER("ACC_INIT", "INIT");
6637 if (acc_get_num_devices(acc_device_nvidia) <= 0)
6638 ERRMSG("Not running on a GPU device!");
6639 acc_device_t device_type = acc_get_device_type();
6640 acc_init(device_type);
6641 }
6642#endif
6643
6644 /* Allocate... */
6645 SELECT_TIMER("ALLOC", "MEMORY");
6646 if (ctl != NULL)
6647 ALLOC(*ctl, ctl_t, 1);
6648 if (cache != NULL)
6649 ALLOC(*cache, cache_t, 1);
6650 if (clim != NULL)
6651 ALLOC(*clim, clim_t, 1);
6652 if (met0 != NULL)
6653 ALLOC(*met0, met_t, 1);
6654 if (met1 != NULL)
6655 ALLOC(*met1, met_t, 1);
6656 if (atm != NULL)
6657 ALLOC(*atm, atm_t, 1);
6658 if (depo != NULL)
6659 ALLOC(*depo, depo_t, 1);
6660 if (dd != NULL) {
6661 ALLOC(*dd, dd_t, 1);
6662
6663 /* Initialize the MPI datatype such that mptrac_free() knows whether
6664 dd_init() has created it... */
6665#ifdef DD
6666 (*dd)->MPI_Particle = MPI_DATATYPE_NULL;
6667#endif
6668 }
6669
6670 /* Create data region on GPU... */
6671#ifdef _OPENACC
6672 SELECT_TIMER("CREATE_DATA_REGION", "MEMORY");
6673 if (ctl != NULL) {
6674 ctl_t *ctlup = *ctl;
6675#pragma acc enter data create(ctlup[:1])
6676 }
6677 if (cache != NULL) {
6678 cache_t *cacheup = *cache;
6679#pragma acc enter data create(cacheup[:1])
6680 }
6681 if (clim != NULL) {
6682 clim_t *climup = *clim;
6683#pragma acc enter data create(climup[:1])
6684 }
6685 if (met0 != NULL) {
6686 met_t *met0up = *met0;
6687#pragma acc enter data create(met0up[:1])
6688 }
6689 if (met1 != NULL) {
6690 met_t *met1up = *met1;
6691#pragma acc enter data create(met1up[:1])
6692 }
6693 if (atm != NULL) {
6694 atm_t *atmup = *atm;
6695#pragma acc enter data create(atmup[:1])
6696 }
6697 if (depo != NULL) {
6698 depo_t *depoup = *depo;
6699#pragma acc enter data create(depoup[:1])
6700 }
6701#ifdef DD
6702 if (dd != NULL) {
6703 dd_t *ddup = *dd;
6704#pragma acc enter data create(ddup[:1])
6705 }
6706#endif
6707#endif
6708}
Air parcel data.
Definition mptrac.h:3612
Cache data structure.
Definition mptrac.h:3673
Climatological data.
Definition mptrac.h:3837
Control parameters.
Definition mptrac.h:2514
Domain decomposition data structure.
Definition mptrac.h:4073
Ground inventories of deposited radionuclides.
Definition mptrac.h:3706
Meteo data structure.
Definition mptrac.h:3896

◆ mptrac_free()

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.

This function releases the memory allocated for various data structures and deletes any associated data regions on GPUs if OpenACC is enabled. Every argument may be NULL.

Parameters
[in]ctlPointer to the control structure (ctl_t) to be freed.
[in]cachePointer to the cache structure (cache_t) to be freed.
[in]climPointer to the climatology structure (clim_t) to be freed.
[in]met0Pointer to the first meteorology structure (met_t) to be freed.
[in]met1Pointer to the second meteorology structure (met_t) to be freed.
[in]atmPointer to the atmospheric structure (atm_t) to be freed.
[in]depoPointer to the radionuclide ground-inventory structure (depo_t) to be freed.
[in]ddPointer to an dd_t structure containing MPI information, including rank and neighbours.
Note
This function uses OpenACC for GPU memory management. If OpenACC is not enabled, the GPU-specific code is ignored.
Precondition
Non-NULL input pointers must have been returned through the corresponding argument of mptrac_alloc().
Postcondition
All input pointers are freed, and the associated data regions on GPUs are deleted if OpenACC is enabled.
Warning
Ensure that the input pointers are not used after calling this function as they will be invalidated.
Author
Lars Hoffmann

Definition at line 6712 of file mptrac.c.

6720 {
6721
6722#ifdef DD
6723#if LOGLEV >= 2
6724 if (dd_node != NULL) {
6725 int rank;
6726 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
6727 LOG(2, "DD_COMM rank=%d sent=%llu intra=%llu inter=%llu "
6728 "intra_bytes=%llu inter_bytes=%llu particle_bytes=%d",
6729 rank, dd_sent, dd_intra, dd_inter,
6730 dd_intra * (unsigned long long) dd_particle_bytes,
6731 dd_inter * (unsigned long long) dd_particle_bytes,
6732 dd_particle_bytes);
6733 free(dd_node);
6734 dd_node = NULL;
6735 }
6736#endif
6737#endif
6738
6739 /* Delete data region on GPU... */
6740#ifdef _OPENACC
6741 SELECT_TIMER("DELETE_DATA_REGION", "MEMORY");
6742 if (ctl != NULL) {
6743#pragma acc exit data delete(ctl[:1])
6744 }
6745 if (cache != NULL) {
6746#pragma acc exit data delete(cache[:1])
6747 }
6748 if (clim != NULL) {
6749#pragma acc exit data delete(clim[:1])
6750 }
6751 if (met0 != NULL) {
6752#pragma acc exit data delete(met0[:1])
6753 }
6754 if (met1 != NULL) {
6755#pragma acc exit data delete(met1[:1])
6756 }
6757 if (atm != NULL) {
6758#pragma acc exit data delete(atm[:1])
6759 }
6760 if (depo != NULL) {
6761#pragma acc exit data delete(depo[:1])
6762 }
6763#ifdef DD
6764 if (dd != NULL) {
6765#pragma acc exit data delete(dd[:1])
6766 }
6767#endif
6768#endif
6769
6770 /* Free... */
6771 SELECT_TIMER("FREE", "MEMORY");
6772 free(atm);
6773 free(depo);
6774 free(ctl);
6775 free(cache);
6776 free(clim);
6777 free(met0);
6778 free(met1);
6779
6780 /* Free MPI datatype... */
6781#ifdef DD
6782 if (dd != NULL && dd->MPI_Particle != MPI_DATATYPE_NULL)
6783 MPI_Type_free(&dd->MPI_Particle);
6784#endif
6785 free(dd);
6786}

◆ mptrac_get_met()

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.

This function retrieves meteorological data for the given time t and updates the provided pointers to the met0 and met1 structures accordingly. It handles both the initialization and subsequent updates of the meteorological data based on the direction of time integration.

Parameters
ctlPointer to the control structure containing configuration settings.
climPointer to the climate structure.
tThe current time for which meteorological data is to be retrieved.
met0Pointer to the pointer of the first meteorological data structure.
met1Pointer to the pointer of the second meteorological data structure.
ddA pointer to an dd_t structure containing MPI information, including rank and neighbours.

The function performs the following steps:

  • Initializes meteorological data on the first call or when the simulation restarts.
  • Reads new meteorological data when advancing forward or backward in time.
  • Swaps pointers to manage double buffering of the meteorological data.
  • Performs caching to optimize subsequent data retrieval.
  • Ensures consistency of the meteorological grids.
Note
This function utilizes GPU acceleration with OpenACC directives if enabled.
Ensure that ctl, clim, met0, and met1 are properly initialized before calling this function.
See also
get_met_filename
read_met
SELECT_TIMER
LOG
ERRMSG
WARN
Author
Lars Hoffmann

Definition at line 6790 of file mptrac.c.

6796 {
6797
6798 static int init;
6799
6800 met_t *mets;
6801
6802 char cachefile[LEN], cmd[2 * LEN], filename[LEN];
6803
6804 /* Set timer... */
6805 SELECT_TIMER("GET_MET", "INPUT");
6806
6807 /* Init... */
6808 if (t == ctl->t_start || !init) {
6809 init = 1;
6810
6811 /* Read meteo data... */
6812 get_met_filename(ctl, t + (ctl->direction == -1 ? -1 : 0), -1,
6813 ctl->metbase, ctl->dt_met, filename);
6814 if (!mptrac_read_met(filename, ctl, clim, *met0, dd))
6815 ERRMSG("Cannot open file!");
6816
6817 get_met_filename(ctl, t + (ctl->direction == 1 ? 1 : 0), 1,
6818 ctl->metbase, ctl->dt_met, filename);
6819 if (!mptrac_read_met(filename, ctl, clim, *met1, dd))
6820 ERRMSG("Cannot open file!");
6821
6822 /* Update GPU... */
6823 mptrac_update_device(NULL, NULL, NULL, met0, met1, NULL);
6824 SELECT_TIMER("GET_MET", "INPUT");
6825
6826 /* Caching... */
6827 if (ctl->met_cache && t != ctl->t_stop) {
6828 get_met_filename(ctl, t + 1.1 * ctl->dt_met * ctl->direction,
6829 ctl->direction, ctl->metbase, ctl->dt_met, cachefile);
6830 sprintf(cmd, "cat %s > /dev/null &", cachefile);
6831 LOG(1, "Caching: %s", cachefile);
6832 if (system(cmd) != 0)
6833 WARN("Caching command failed!");
6834 }
6835 }
6836
6837 /* Read new data for forward trajectories... */
6838 if (t > (*met1)->time) {
6839
6840 /* Pointer swap... */
6841 mets = *met1;
6842 *met1 = *met0;
6843 *met0 = mets;
6844
6845 /* Read new meteo data... */
6846 get_met_filename(ctl, t, 1, ctl->metbase, ctl->dt_met, filename);
6847 if (!mptrac_read_met(filename, ctl, clim, *met1, dd))
6848 ERRMSG("Cannot open file!");
6849
6850 /* Update GPU... */
6851 mptrac_update_device(NULL, NULL, NULL, NULL, met1, NULL);
6852 SELECT_TIMER("GET_MET", "INPUT");
6853
6854 /* Caching... */
6855 if (ctl->met_cache && t != ctl->t_stop) {
6856 get_met_filename(ctl, t + ctl->dt_met, 1, ctl->metbase, ctl->dt_met,
6857 cachefile);
6858 sprintf(cmd, "cat %s > /dev/null &", cachefile);
6859 LOG(1, "Caching: %s", cachefile);
6860 if (system(cmd) != 0)
6861 WARN("Caching command failed!");
6862 }
6863 }
6864
6865 /* Read new data for backward trajectories... */
6866 if (t < (*met0)->time) {
6867
6868 /* Pointer swap... */
6869 mets = *met1;
6870 *met1 = *met0;
6871 *met0 = mets;
6872
6873 /* Read new meteo data... */
6874 get_met_filename(ctl, t, -1, ctl->metbase, ctl->dt_met, filename);
6875 if (!mptrac_read_met(filename, ctl, clim, *met0, dd))
6876 ERRMSG("Cannot open file!");
6877
6878 /* Update GPU... */
6879 mptrac_update_device(NULL, NULL, NULL, met0, NULL, NULL);
6880 SELECT_TIMER("GET_MET", "INPUT");
6881
6882 /* Caching... */
6883 if (ctl->met_cache && t != ctl->t_stop) {
6884 get_met_filename(ctl, t - ctl->dt_met, -1, ctl->metbase, ctl->dt_met,
6885 cachefile);
6886 sprintf(cmd, "cat %s > /dev/null &", cachefile);
6887 LOG(1, "Caching: %s", cachefile);
6888 if (system(cmd) != 0)
6889 WARN("Caching command failed!");
6890 }
6891 }
6892
6893 if (!((*met0)->time < (*met1)->time))
6894 ERRMSG("Meteo times must be strictly increasing!");
6895
6896 if ((*met0)->coord_type != (*met1)->coord_type)
6897 ERRMSG("Coordinate types do not match!");
6898
6899 /* Check that grids are consistent... */
6900 if ((*met0)->nx != 0 && (*met1)->nx != 0) {
6901 if ((*met0)->nx != (*met1)->nx
6902 || (*met0)->ny != (*met1)->ny || (*met0)->np != (*met1)->np)
6903 ERRMSG("Meteo grid dimensions do not match!");
6904 if ((ctl->advect_vert_coord == 1
6905 || (ctl->advect > 0 && ctl->advect_vert_coord > 1)
6906 || ctl->qnt_eta_d >= 0
6907 || (ctl->advect_vert_coord == 3 && ctl->qnt_eta_dot >= 0))
6908 && ((*met0)->npl < 2 || (*met0)->npl != (*met1)->npl))
6909 ERRMSG("Meteo native level counts do not match!");
6910 for (int ix = 0; ix < (*met0)->nx; ix++)
6911 if (fabs((*met0)->lon[ix] - (*met1)->lon[ix]) > 0.001)
6912 ERRMSG("Meteo grid longitudes do not match!");
6913 for (int iy = 0; iy < (*met0)->ny; iy++)
6914 if (fabs((*met0)->lat[iy] - (*met1)->lat[iy]) > 0.001)
6915 ERRMSG("Meteo grid latitudes do not match!");
6916 for (int ip = 0; ip < (*met0)->np; ip++)
6917 if (fabs((*met0)->p[ip] - (*met1)->p[ip]) > 0.001)
6918 ERRMSG("Meteo grid pressure levels do not match!");
6919 }
6920}
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:2925
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:8269
int met_cache
Preload meteo data into disk cache (0=no, 1=yes).
Definition mptrac.h:3032
char metbase[LEN]
Basename for meteo data.
Definition mptrac.h:2864
Here is the call graph for this function:

◆ mptrac_init()

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.

This function sets up the necessary components and subsystems for the MPTRAC module, including timesteps, random number generation, and GPU memory updates.

Parameters
ctlPointer to the control structure containing configuration and state information.
cachePointer to the cache structure used for data storage and retrieval.
climPointer to the climatology structure containing climate-related data.
atmPointer to the atmospheric structure containing atmospheric state data.
depoPointer to inventories allocated and zero-initialized by mptrac_alloc().
ntaskNumber of tasks or threads to initialize for the random number generator.

The function performs the following operations:

  • Initializes the timesteps using the module_timesteps_init function.
  • Initializes the random number generator using the module_rng_init function.
  • Updates GPU memory using the mptrac_update_device function.
Author
Lars Hoffmann

Definition at line 6924 of file mptrac.c.

6930 {
6931
6932 if (ctl->advect_vert_coord == 1)
6933 for (int ip = 1; ip < atm->np; ip++)
6934 if (atm->time[ip] != atm->time[0])
6935 ERRMSG("Zeta advection requires identical particle initialization times!");
6936
6937 /* Initialize timesteps... */
6938 module_timesteps_init(ctl, atm);
6939
6940 /* Initialize random number generator... */
6941 module_rng_init(ntask);
6942
6943 /* Update GPU memory... */
6944 mptrac_update_device(ctl, cache, clim, NULL, NULL, atm);
6945#ifdef _OPENACC
6946#pragma acc update device(depo[:1])
6947#else
6948 (void) depo;
6949#endif
6950}
void module_timesteps_init(ctl_t *ctl, const atm_t *atm)
Initialize start time and time interval for time-stepping.
Definition mptrac.c:6374
void module_rng_init(const int ntask)
Initialize random number generators for parallel tasks.
Definition mptrac.c:6078
Here is the call graph for this function:

◆ mptrac_read_atm()

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.

This function reads air parcel data from a file and populates the provided atm_t structure based on the type of data specified in the ctl_t control structure. It supports various data formats including ASCII, binary, netCDF, and CLaMS.

Parameters
filenameThe name of the file containing the atmospheric data.
ctlA pointer to the control structure (ctl_t) that specifies the type of data.
atmA pointer to the atmospheric structure (atm_t) that will be populated with the data.
Returns
Returns 1 on success, and 0 on failure.

This function performs the following steps:

  • Sets a timer for performance measurement.
  • Initializes the atmospheric structure.
  • Logs the file being read.
  • Reads data from the file based on the specified type (ctl->atm_type):
    • 0 for ASCII data
    • 1 for binary data
    • 2 for netCDF data
    • 3 or 4 for CLaMS data
  • Handles errors if the data type is not supported.
  • Checks the result of the data reading function and ensures data was read successfully.
  • Logs information about the number of air parcels and the ranges of various parameters (time, altitude, pressure, longitude, latitude, and other quantities).

The function utilizes several helper functions and macros:

  • SELECT_TIMER for setting the timer.
  • LOG for logging information.
  • ERRMSG for handling error messages.
  • gsl_stats_minmax for calculating minimum and maximum values.
  • Z for converting altitude.
Author
Lars Hoffmann

Definition at line 6954 of file mptrac.c.

6957 {
6958
6959 int result;
6960
6961 /* Set timer... */
6962 SELECT_TIMER("READ_ATM", "INPUT");
6963
6964 /* Init... */
6965 atm->np = 0;
6966
6967 /* Write info... */
6968 LOG(1, "Read atmospheric data: %s", filename);
6969
6970 /* Read ASCII data... */
6971 if (ctl->atm_type == 0)
6972 result = read_atm_asc(filename, ctl, atm);
6973
6974 /* Read binary data... */
6975 else if (ctl->atm_type == 1)
6976 result = read_atm_bin(filename, ctl, atm);
6977
6978 /* Read netCDF data... */
6979 else if (ctl->atm_type == 2)
6980 result = read_atm_nc(filename, ctl, atm);
6981
6982 /* Read CLaMS data... */
6983 else if (ctl->atm_type == 3 || ctl->atm_type == 4)
6984 result = read_atm_clams(filename, ctl, atm);
6985
6986 /* Error... */
6987 else
6988 ERRMSG("Atmospheric data type not supported!");
6989
6990 /* Check result... */
6991 if (result != 1)
6992 return 0;
6993
6994 /* Check number of air parcels... */
6995 if (atm->np < 1)
6996 ERRMSG("Can not read any data!");
6997
6998 /* Write info... */
6999 double mini, maxi;
7000 LOG(2, "Number of particles: %d", atm->np);
7001 gsl_stats_minmax(&mini, &maxi, atm->time, 1, (size_t) atm->np);
7002 LOG(2, "Time range: %.2f ... %.2f s", mini, maxi);
7003 gsl_stats_minmax(&mini, &maxi, atm->p, 1, (size_t) atm->np);
7004 LOG(2, "Altitude range: %g ... %g km", Z(maxi), Z(mini));
7005 LOG(2, "Pressure range: %g ... %g hPa", maxi, mini);
7006 gsl_stats_minmax(&mini, &maxi, atm->lon, 1, (size_t) atm->np);
7007 LOG(2, "%s range: %g ... %g %s",
7008 ctl->met_coord_type == 0 ? "Longitude" : "X coordinate", mini, maxi,
7009 ctl->met_coord_type == 0 ? "deg" : "m");
7010 gsl_stats_minmax(&mini, &maxi, atm->lat, 1, (size_t) atm->np);
7011 LOG(2, "%s range: %g ... %g %s",
7012 ctl->met_coord_type == 0 ? "Latitude" : "Y coordinate", mini, maxi,
7013 ctl->met_coord_type == 0 ? "deg" : "m");
7014 for (int iq = 0; iq < ctl->nq; iq++) {
7015 char msg[5 * LEN];
7016 sprintf(msg, "Quantity %s range: %s ... %s %s",
7017 ctl->qnt_name[iq], ctl->qnt_format[iq],
7018 ctl->qnt_format[iq], ctl->qnt_unit[iq]);
7019 gsl_stats_minmax(&mini, &maxi, atm->q[iq], 1, (size_t) atm->np);
7020 LOG(2, msg, mini, maxi);
7021 }
7022
7023 /* Return success... */
7024 return 1;
7025}
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:9133
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:9014
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:9070
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:8972
char qnt_format[NQ][LEN]
Quantity output format.
Definition mptrac.h:2533
int atm_type
Type of atmospheric data files (0=ASCII, 1=binary, 2=netCDF, 3=CLaMS_traj, 4=CLaMS_pos).
Definition mptrac.h:3355
char qnt_unit[NQ][LEN]
Quantity units.
Definition mptrac.h:2530
char qnt_name[NQ][LEN]
Quantity names.
Definition mptrac.h:2524
Here is the call graph for this function:

◆ mptrac_read_clim()

void mptrac_read_clim ( const ctl_t * ctl,
clim_t * clim )

Reads various climatological data and populates the given climatology structure.

This function reads a range of climatological datasets based on the specified control settings and stores the data in the provided clim_t structure. It handles initialization of tropopause climatology, photolysis rates, and multiple gas species' climatologies and time series.

Parameters
ctlA pointer to the control structure (ctl_t) that specifies file names and parameters for climatology data.
climA pointer to the climatology structure (clim_t) that will be populated with the data.

This function performs the following steps:

  • Sets a timer for reading climatology data.
  • Initializes the tropopause climatology.
  • Reads photolysis rates if specified in ctl.
  • Reads HNO3 climatology if specified in ctl.
  • Reads OH climatology if specified in ctl and applies a diurnal correction if specified.
  • Reads H2O2, HO2, O(1D) climatologies if specified in ctl.
  • Reads time series data for various gases (CFC-10, CFC-11, CFC-12, N2O, SF6) if specified in ctl.

The function utilizes several helper functions:

  • clim_tropo_init for initializing tropopause climatology.
  • read_clim_photo for reading photolysis rates.
  • read_clim_zm for reading zonal mean climatologies.
  • clim_oh_diurnal_correction for applying diurnal correction to OH climatology.
  • read_clim_ts for reading time series data.
Author
Lars Hoffmann
Mingzhao Liu

Definition at line 7029 of file mptrac.c.

7031 {
7032
7033 /* Set timer... */
7034 SELECT_TIMER("READ_CLIM", "INPUT");
7035
7036 /* Init tropopause climatology... */
7037 clim_tropo_init(clim);
7038
7039 /* Read photolysis rates... */
7040 if (ctl->clim_photo[0] != '-')
7041 read_clim_photo(ctl->clim_photo, &clim->photo);
7042
7043 /* Read HNO3 climatology... */
7044 if (ctl->clim_hno3_filename[0] != '-')
7045 read_clim_zm(ctl->clim_hno3_filename, "HNO3", &clim->hno3);
7046
7047 /* Read OH climatology... */
7048 if (ctl->clim_oh_filename[0] != '-') {
7049 read_clim_zm(ctl->clim_oh_filename, "OH", &clim->oh);
7050 if (ctl->oh_chem_beta > 0)
7051 clim_oh_diurnal_correction(ctl, clim);
7052 }
7053
7054 /* Read H2O2 climatology... */
7055 if (ctl->clim_h2o2_filename[0] != '-')
7056 read_clim_zm(ctl->clim_h2o2_filename, "H2O2", &clim->h2o2);
7057
7058 /* Read HO2 climatology... */
7059 if (ctl->clim_ho2_filename[0] != '-')
7060 read_clim_zm(ctl->clim_ho2_filename, "HO2", &clim->ho2);
7061
7062 /* Read O(1D) climatology... */
7063 if (ctl->clim_o1d_filename[0] != '-')
7064 read_clim_zm(ctl->clim_o1d_filename, "O1D", &clim->o1d);
7065
7066 /* Read CFC-10 time series... */
7067 if (ctl->clim_ccl4_timeseries[0] != '-')
7069
7070 /* Read CFC-11 time series... */
7071 if (ctl->clim_ccl3f_timeseries[0] != '-')
7073
7074 /* Read CFC-12 time series... */
7075 if (ctl->clim_ccl2f2_timeseries[0] != '-')
7077
7078 /* Read N2O time series... */
7079 if (ctl->clim_n2o_timeseries[0] != '-')
7080 read_clim_ts(ctl->clim_n2o_timeseries, &clim->n2o);
7081
7082 /* Read SF6 time series... */
7083 if (ctl->clim_sf6_timeseries[0] != '-')
7084 read_clim_ts(ctl->clim_sf6_timeseries, &clim->sf6);
7085}
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:9166
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:9285
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:9339
void clim_tropo_init(clim_t *clim)
Initializes the tropopause data in the climatology structure.
Definition mptrac.c:243
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
char clim_ho2_filename[LEN]
Filename of HO2 climatology.
Definition mptrac.h:3170
char clim_o1d_filename[LEN]
Filename of O(1D) climatology.
Definition mptrac.h:3173
char clim_photo[LEN]
Filename of photolysis rates climatology.
Definition mptrac.h:3158
char clim_h2o2_filename[LEN]
Filename of H2O2 climatology.
Definition mptrac.h:3167
char clim_oh_filename[LEN]
Filename of OH climatology.
Definition mptrac.h:3164
char clim_hno3_filename[LEN]
Filename of HNO3 climatology.
Definition mptrac.h:3161
Here is the call graph for this function:

◆ mptrac_read_ctl()

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.

This function reads control parameters from a specified configuration file and command line arguments, populating the provided ctl_t structure with the parsed data. It handles a wide range of parameters, performing necessary checks and providing default values where applicable.

Parameters
filenameA string containing the path to the configuration file.
argcAn integer representing the number of command line arguments.
argvAn array of strings containing the command line arguments.
ctlA pointer to the structure (ctl_t) that will be populated with the control parameters.

The function performs the following steps:

  • Sets a timer for reading the control file.
  • Logs information about the MPTRAC executable version and compilation details.
  • Initializes quantity indices.
  • Reads and sets various control parameters such as quantities, vertical coordinates, time steps, meteorological data, sorting options, isosurface parameters, random number generator type, advection parameters, diffusion parameters, convection parameters, boundary conditions, species parameters, molar mass, OH chemistry parameters, H2O2 chemistry parameters, KPP chemistry parameters, first order tracer chemistry parameters, wet deposition parameters, dry deposition parameters, climatological data paths, mixing parameters, chemistry grid parameters, exponential decay parameters, PSC analysis parameters, output parameters for atmospheric data, CSI data, ensemble data, grid data, profile data, sample data, station data, and VTK data.
Author
Lars Hoffmann

Definition at line 7089 of file mptrac.c.

7093 {
7094
7095 /* Set timer... */
7096 SELECT_TIMER("READ_CTL", "INPUT");
7097
7098 /* Write info... */
7099 LOG(1, "\nMassive-Parallel Trajectory Calculations (MPTRAC)\n"
7100 "(executable: %s | version: %s | compiled: %s, %s)\n",
7101 argv[0], VERSION, __DATE__, __TIME__);
7102
7103 /* Initialize quantity indices... */
7104 ctl->qnt_idx = -1;
7105 ctl->qnt_ens = -1;
7106 ctl->qnt_stat = -1;
7107 ctl->qnt_m = -1;
7108 ctl->qnt_vmr = -1;
7109 ctl->qnt_rp = -1;
7110 ctl->qnt_rhop = -1;
7111 ctl->qnt_ps = -1;
7112 ctl->qnt_ts = -1;
7113 ctl->qnt_zs = -1;
7114 ctl->qnt_us = -1;
7115 ctl->qnt_vs = -1;
7116 ctl->qnt_ess = -1;
7117 ctl->qnt_nss = -1;
7118 ctl->qnt_shf = -1;
7119 ctl->qnt_lsm = -1;
7120 ctl->qnt_sst = -1;
7121 ctl->qnt_pbl = -1;
7122 ctl->qnt_pt = -1;
7123 ctl->qnt_tt = -1;
7124 ctl->qnt_zt = -1;
7125 ctl->qnt_h2ot = -1;
7126 ctl->qnt_zg = -1;
7127 ctl->qnt_p = -1;
7128 ctl->qnt_t = -1;
7129 ctl->qnt_rho = -1;
7130 ctl->qnt_u = -1;
7131 ctl->qnt_v = -1;
7132 ctl->qnt_w = -1;
7133 ctl->qnt_h2o = -1;
7134 ctl->qnt_o3 = -1;
7135 ctl->qnt_lwc = -1;
7136 ctl->qnt_rwc = -1;
7137 ctl->qnt_iwc = -1;
7138 ctl->qnt_swc = -1;
7139 ctl->qnt_cc = -1;
7140 ctl->qnt_pct = -1;
7141 ctl->qnt_pcb = -1;
7142 ctl->qnt_cl = -1;
7143 ctl->qnt_plcl = -1;
7144 ctl->qnt_plfc = -1;
7145 ctl->qnt_pel = -1;
7146 ctl->qnt_cape = -1;
7147 ctl->qnt_cin = -1;
7148 ctl->qnt_o3c = -1;
7149 ctl->qnt_hno3 = -1;
7150 ctl->qnt_oh = -1;
7151 ctl->qnt_h2o2 = -1;
7152 ctl->qnt_ho2 = -1;
7153 ctl->qnt_o1d = -1;
7154 ctl->qnt_mloss_oh = -1;
7155 ctl->qnt_mloss_h2o2 = -1;
7156 ctl->qnt_mloss_kpp = -1;
7157 ctl->qnt_mloss_wet = -1;
7158 ctl->qnt_mloss_dry = -1;
7159 ctl->qnt_mloss_decay = -1;
7160 ctl->qnt_loss_rate = -1;
7161 ctl->qnt_psat = -1;
7162 ctl->qnt_psice = -1;
7163 ctl->qnt_pw = -1;
7164 ctl->qnt_sh = -1;
7165 ctl->qnt_rh = -1;
7166 ctl->qnt_rhice = -1;
7167 ctl->qnt_theta = -1;
7168 ctl->qnt_iso_var = -1;
7169 ctl->qnt_zeta = -1;
7170 ctl->qnt_zeta_d = -1;
7171 ctl->qnt_zeta_dot = -1;
7172 ctl->qnt_eta = -1;
7173 ctl->qnt_eta_d = -1;
7174 ctl->qnt_eta_dot = -1;
7175 ctl->qnt_tvirt = -1;
7176 ctl->qnt_lapse = -1;
7177 ctl->qnt_vh = -1;
7178 ctl->qnt_vz = -1;
7179 ctl->qnt_pv = -1;
7180 ctl->qnt_tdew = -1;
7181 ctl->qnt_tice = -1;
7182 ctl->qnt_tsts = -1;
7183 ctl->qnt_tnat = -1;
7184 ctl->qnt_Cx = -1;
7185 ctl->qnt_Ch2o = -1;
7186 ctl->qnt_Co3 = -1;
7187 ctl->qnt_Cco = -1;
7188 ctl->qnt_Coh = -1;
7189 ctl->qnt_Ch = -1;
7190 ctl->qnt_Cho2 = -1;
7191 ctl->qnt_Ch2o2 = -1;
7192 ctl->qnt_Co1d = -1;
7193 ctl->qnt_Co3p = -1;
7194 ctl->qnt_Cccl4 = -1;
7195 ctl->qnt_Cccl3f = -1;
7196 ctl->qnt_Cccl2f2 = -1;
7197 ctl->qnt_Cn2o = -1;
7198 ctl->qnt_Csf6 = -1;
7199 ctl->qnt_aoa = -1;
7200 ctl->qnt_Arn222 = -1;
7201 ctl->qnt_Apb210 = -1;
7202 ctl->qnt_Abe7 = -1;
7203 ctl->qnt_Acs137 = -1;
7204 ctl->qnt_Ai131 = -1;
7205 ctl->qnt_Axe133 = -1;
7206 ctl->qnt_current_subdomain = -1;
7207 ctl->qnt_target_subdomain = -1;
7208
7209 /* Read quantities... */
7210 ctl->nq = (int) scan_ctl(filename, argc, argv, "NQ", -1, "0", NULL);
7211 if (ctl->nq > NQ)
7212 ERRMSG("Too many quantities!");
7213 for (int iq = 0; iq < ctl->nq; iq++) {
7214
7215 /* Read quantity name and format... */
7216 scan_ctl(filename, argc, argv, "QNT_NAME", iq, "", ctl->qnt_name[iq]);
7217 scan_ctl(filename, argc, argv, "QNT_LONGNAME", iq, ctl->qnt_name[iq],
7218 ctl->qnt_longname[iq]);
7219 scan_ctl(filename, argc, argv, "QNT_FORMAT", iq, "%g",
7220 ctl->qnt_format[iq]);
7221 if (strcasecmp(ctl->qnt_name[iq], "aoa") == 0)
7222 sprintf(ctl->qnt_format[iq], "%%.2f");
7223
7224 /* Try to identify quantity... */
7225 SET_QNT(qnt_idx, "idx", "particle index", "-")
7226 SET_QNT(qnt_ens, "ens", "ensemble index", "-")
7227 SET_QNT(qnt_stat, "stat", "station flag", "-")
7228 SET_QNT(qnt_m, "m", "mass", "kg")
7229 SET_QNT(qnt_vmr, "vmr", "volume mixing ratio", "ppv")
7230 SET_QNT(qnt_rp, "rp", "particle radius", "microns")
7231 SET_QNT(qnt_rhop, "rhop", "particle density", "kg/m^3")
7232 SET_QNT(qnt_ps, "ps", "surface pressure", "hPa")
7233 SET_QNT(qnt_ts, "ts", "surface temperature", "K")
7234 SET_QNT(qnt_zs, "zs", "surface height", "km")
7235 SET_QNT(qnt_us, "us", "surface zonal wind", "m/s")
7236 SET_QNT(qnt_vs, "vs", "surface meridional wind", "m/s")
7237 SET_QNT(qnt_ess, "ess", "eastward turbulent surface stress", "N/m^2")
7238 SET_QNT(qnt_nss, "nss", "northward turbulent surface stress", "N/m^2")
7239 SET_QNT(qnt_shf, "shf", "surface sensible heat flux", "W/m^2")
7240 SET_QNT(qnt_lsm, "lsm", "land-sea mask", "1")
7241 SET_QNT(qnt_sst, "sst", "sea surface temperature", "K")
7242 SET_QNT(qnt_pbl, "pbl", "planetary boundary layer", "hPa")
7243 SET_QNT(qnt_pt, "pt", "tropopause pressure", "hPa")
7244 SET_QNT(qnt_tt, "tt", "tropopause temperature", "K")
7245 SET_QNT(qnt_zt, "zt", "tropopause geopotential height", "km")
7246 SET_QNT(qnt_h2ot, "h2ot", "tropopause water vapor", "ppv")
7247 SET_QNT(qnt_zg, "zg", "geopotential height", "km")
7248 SET_QNT(qnt_p, "p", "pressure", "hPa")
7249 SET_QNT(qnt_t, "t", "temperature", "K")
7250 SET_QNT(qnt_rho, "rho", "air density", "kg/m^3")
7251 SET_QNT(qnt_u, "u", "zonal wind", "m/s")
7252 SET_QNT(qnt_v, "v", "meridional wind", "m/s")
7253 SET_QNT(qnt_w, "w", "vertical velocity", "hPa/s")
7254 SET_QNT(qnt_h2o, "h2o", "water vapor", "ppv")
7255 SET_QNT(qnt_o3, "o3", "ozone", "ppv")
7256 SET_QNT(qnt_lwc, "lwc", "cloud liquid water content", "kg/kg")
7257 SET_QNT(qnt_rwc, "rwc", "cloud rain water content", "kg/kg")
7258 SET_QNT(qnt_iwc, "iwc", "cloud ice water content", "kg/kg")
7259 SET_QNT(qnt_swc, "swc", "cloud snow water content", "kg/kg")
7260 SET_QNT(qnt_cc, "cc", "cloud cover", "1")
7261 SET_QNT(qnt_pct, "pct", "cloud top pressure", "hPa")
7262 SET_QNT(qnt_pcb, "pcb", "cloud bottom pressure", "hPa")
7263 SET_QNT(qnt_cl, "cl", "total column cloud water", "kg/m^2")
7264 SET_QNT(qnt_plcl, "plcl", "lifted condensation level", "hPa")
7265 SET_QNT(qnt_plfc, "plfc", "level of free convection", "hPa")
7266 SET_QNT(qnt_pel, "pel", "equilibrium level", "hPa")
7267 SET_QNT(qnt_cape, "cape", "convective available potential energy",
7268 "J/kg")
7269 SET_QNT(qnt_cin, "cin", "convective inhibition", "J/kg")
7270 SET_QNT(qnt_o3c, "o3c", "total column ozone", "DU")
7271 SET_QNT(qnt_hno3, "hno3", "nitric acid", "ppv")
7272 SET_QNT(qnt_oh, "oh", "hydroxyl radical", "ppv")
7273 SET_QNT(qnt_h2o2, "h2o2", "hydrogen peroxide", "ppv")
7274 SET_QNT(qnt_ho2, "ho2", "hydroperoxyl radical", "ppv")
7275 SET_QNT(qnt_o1d, "o1d", "atomic oxygen", "ppv")
7276 SET_QNT(qnt_mloss_oh, "mloss_oh", "mass loss due to OH chemistry", "kg")
7277 SET_QNT(qnt_mloss_h2o2, "mloss_h2o2",
7278 "mass loss due to H2O2 chemistry", "kg")
7279 SET_QNT(qnt_mloss_kpp, "mloss_kpp", "mass loss due to kpp chemistry",
7280 "kg")
7281 SET_QNT(qnt_mloss_wet, "mloss_wet", "mass loss due to wet deposition",
7282 "kg")
7283 SET_QNT(qnt_mloss_dry, "mloss_dry", "mass loss due to dry deposition",
7284 "kg")
7285 SET_QNT(qnt_mloss_decay, "mloss_decay",
7286 "mass loss due to exponential decay", "kg")
7287 SET_QNT(qnt_loss_rate, "loss_rate", "total loss rate", "s^-1")
7288 SET_QNT(qnt_psat, "psat", "saturation pressure over water", "hPa")
7289 SET_QNT(qnt_psice, "psice", "saturation pressure over ice", "hPa")
7290 SET_QNT(qnt_pw, "pw", "partial water vapor pressure", "hPa")
7291 SET_QNT(qnt_sh, "sh", "specific humidity", "kg/kg")
7292 SET_QNT(qnt_rh, "rh", "relative humidity", "%%")
7293 SET_QNT(qnt_rhice, "rhice", "relative humidity over ice", "%%")
7294 SET_QNT(qnt_theta, "theta", "potential temperature", "K")
7295 SET_QNT(qnt_iso_var, "iso_var", "isosurface reference value", "varies")
7296 SET_QNT(qnt_zeta, "zeta", "zeta coordinate", "K")
7297 SET_QNT(qnt_zeta_d, "zeta_d", "diagnosed zeta coordinate", "K")
7298 SET_QNT(qnt_zeta_dot, "zeta_dot", "velocity of zeta coordinate",
7299 "K/day")
7300 SET_QNT(qnt_eta, "eta", "eta coordinate", "1")
7301 SET_QNT(qnt_eta_d, "eta_d", "diagnosed eta coordinate", "1")
7302 SET_QNT(qnt_eta_dot, "eta_dot", "velocity of eta coordinate", "1/s")
7303 SET_QNT(qnt_tvirt, "tvirt", "virtual temperature", "K")
7304 SET_QNT(qnt_lapse, "lapse", "temperature lapse rate", "K/km")
7305 SET_QNT(qnt_vh, "vh", "horizontal velocity", "m/s")
7306 SET_QNT(qnt_vz, "vz", "vertical velocity", "m/s")
7307 SET_QNT(qnt_pv, "pv", "potential vorticity", "PVU")
7308 SET_QNT(qnt_tdew, "tdew", "dew point temperature", "K")
7309 SET_QNT(qnt_tice, "tice", "frost point temperature", "K")
7310 SET_QNT(qnt_tsts, "tsts", "STS existence temperature", "K")
7311 SET_QNT(qnt_tnat, "tnat", "NAT existence temperature", "K")
7312 SET_QNT(qnt_Cx, "Cx", "Trace species x volume mixing ratio", "ppv")
7313 SET_QNT(qnt_Ch2o, "Ch2o", "H2O volume mixing ratio", "ppv")
7314 SET_QNT(qnt_Co3, "Co3", "O3 volume mixing ratio", "ppv")
7315 SET_QNT(qnt_Cco, "Cco", "CO volume mixing ratio", "ppv")
7316 SET_QNT(qnt_Coh, "Coh", "HO volume mixing ratio", "ppv")
7317 SET_QNT(qnt_Ch, "Ch", "H radical volume mixing ratio", "ppv")
7318 SET_QNT(qnt_Cho2, "Cho2", "HO2 volume mixing ratio", "ppv")
7319 SET_QNT(qnt_Ch2o2, "Ch2o2", "H2O2 volume mixing ratio", "ppv")
7320 SET_QNT(qnt_Co1d, "Co1d", "O(1D) volume mixing ratio", "ppv")
7321 SET_QNT(qnt_Co3p, "Co3p", "O(3P) radical volume mixing ratio", "ppv")
7322 SET_QNT(qnt_Cccl4, "Cccl4", "CCl4 (CFC-10) volume mixing ratio", "ppv")
7323 SET_QNT(qnt_Cccl3f, "Cccl3f", "CCl3F (CFC-11) volume mixing ratio",
7324 "ppv")
7325 SET_QNT(qnt_Cccl2f2, "Cccl2f2", "CCl2F2 (CFC-12) volume mixing ratio",
7326 "ppv")
7327 SET_QNT(qnt_Cn2o, "Cn2o", "N2O volume mixing ratio", "ppv")
7328 SET_QNT(qnt_Csf6, "Csf6", "SF6 volume mixing ratio", "ppv")
7329 SET_QNT(qnt_aoa, "aoa", "age of air", "s")
7330 SET_QNT(qnt_Arn222, "Arn222", "Rn-222 activity", "Bq")
7331 SET_QNT(qnt_Apb210, "Apb210", "Pb-210 activity", "Bq")
7332 SET_QNT(qnt_Abe7, "Abe7", "Be-7 activity", "Bq")
7333 SET_QNT(qnt_Acs137, "Acs137", "Cs-137 activity", "Bq")
7334 SET_QNT(qnt_Ai131, "Ai131", "I-131 activity", "Bq")
7335 SET_QNT(qnt_Axe133, "Axe133", "Xe-133 activity", "Bq")
7336 SET_QNT(qnt_current_subdomain, "current_subdomain",
7337 "current subdomain rank", "-")
7338 SET_QNT(qnt_target_subdomain, "target_subdomain",
7339 "target subdomain rank", "-")
7340 scan_ctl(filename, argc, argv, "QNT_UNIT", iq, "", ctl->qnt_unit[iq]);
7341 }
7342
7343 ctl->met_coord_type =
7344 (int) scan_ctl(filename, argc, argv, "MET_COORD_TYPE", -1, "0", NULL);
7345 if (ctl->met_coord_type < 0 || ctl->met_coord_type > 1)
7346 ERRMSG("MET_COORD_TYPE must be 0 or 1!");
7347 ctl->met_utm_ref_lat = 0.0;
7348 ctl->met_utm_ref_lon = 0.0;
7349 if (ctl->met_coord_type == 1) {
7350 ctl->met_utm_ref_lat =
7351 scan_ctl(filename, argc, argv, "MET_UTM_REF_LAT", -1, "", NULL);
7352 ctl->met_utm_ref_lon =
7353 scan_ctl(filename, argc, argv, "MET_UTM_REF_LON", -1, "", NULL);
7354 }
7355
7356 /* Vertical coordinate and velocity... */
7357 ctl->advect_vert_coord =
7358 (int) scan_ctl(filename, argc, argv, "ADVECT_VERT_COORD", -1, "0", NULL);
7359 if (ctl->advect_vert_coord < 0 || ctl->advect_vert_coord > 3)
7360 ERRMSG("ADVECT_VERT_COORD must be 0, 1, 2, or 3!");
7361
7362 if (ctl->advect_vert_coord == 1 && ctl->qnt_zeta < 0)
7363 ERRMSG("Add quantity zeta for diabatic advection!");
7364 if (ctl->advect_vert_coord == 3 && ctl->qnt_eta < 0)
7365 ERRMSG("Add quantity eta for etadot avection!");
7366
7367 ctl->met_vert_coord =
7368 (int) scan_ctl(filename, argc, argv, "MET_VERT_COORD", -1, "0", NULL);
7369 if (ctl->met_vert_coord < 0 || ctl->met_vert_coord > 4)
7370 ERRMSG("MET_VERT_COORD must be 0, 1, 2, 3, or 4!");
7371 if (ctl->qnt_eta_d >= 0
7372 && ctl->met_vert_coord != 2 && ctl->met_vert_coord != 3)
7373 ERRMSG("Quantity eta_d requires full-level A and B coefficients!");
7374
7375 if (ctl->advect_vert_coord == 2 && ctl->met_vert_coord == 0)
7376 ERRMSG
7377 ("Using ADVECT_VERT_COORD = 2 requires meteo data on model levels!");
7378 if (ctl->advect_vert_coord == 3 && ctl->met_vert_coord != 3)
7379 ERRMSG
7380 ("Using ADVECT_VERT_COORD = 3 requires A and B model level coefficients!");
7381
7382 ctl->met_gp2z =
7383 (int) scan_ctl(filename, argc, argv, "MET_GP2Z", -1, "0", NULL);
7384 if (ctl->met_gp2z != 0 && ctl->met_gp2z != 1)
7385 ERRMSG("Set MET_GP2Z to 0 or 1!");
7386
7387 /* Time steps of simulation... */
7388 ctl->direction =
7389 (int) scan_ctl(filename, argc, argv, "DIRECTION", -1, "1", NULL);
7390 if (ctl->direction != -1 && ctl->direction != 1)
7391 ERRMSG("Set DIRECTION to -1 or 1!");
7392 ctl->t_stop = scan_ctl(filename, argc, argv, "T_STOP", -1, "1e100", NULL);
7393 ctl->dt_mod = scan_ctl(filename, argc, argv, "DT_MOD", -1, "180", NULL);
7394 if (!isfinite(ctl->dt_mod) || ctl->dt_mod <= 0)
7395 ERRMSG("DT_MOD must be finite and positive!");
7396
7397 /* Meteo data... */
7398 scan_ctl(filename, argc, argv, "METBASE", -1, "-", ctl->metbase);
7399 ctl->dt_met = scan_ctl(filename, argc, argv, "DT_MET", -1, "3600", NULL);
7400 if (ctl->dt_mod > ctl->dt_met)
7401 ERRMSG("DT_MOD must not exceed DT_MET!");
7402 ctl->met_convention =
7403 (int) scan_ctl(filename, argc, argv, "MET_CONVENTION", -1, "0", NULL);
7404 ctl->met_type =
7405 (int) scan_ctl(filename, argc, argv, "MET_TYPE", -1, "0", NULL);
7406 if (ctl->advect_vert_coord == 1 && ctl->met_type != 0)
7407 ERRMSG
7408 ("Please use meteo files in netcdf format for diabatic calculations.");
7409 if (ctl->advect_vert_coord == 3 && ctl->met_type != 0)
7410 ERRMSG
7411 ("Please use meteo files in netcdf format for etadot calculations.");
7412 ctl->met_clams =
7413 (int) scan_ctl(filename, argc, argv, "MET_CLAMS", -1, "0", NULL);
7414 ctl->met_nc_scale =
7415 (int) scan_ctl(filename, argc, argv, "MET_NC_SCALE", -1, "1", NULL);
7416 ctl->met_nc_read_mode =
7417 (int) scan_ctl(filename, argc, argv, "MET_NC_READ_MODE", -1, "0", NULL);
7418 if (ctl->met_nc_read_mode < 0 || ctl->met_nc_read_mode > 1)
7419 ERRMSG("Set MET_NC_READ_MODE to 0 or 1!");
7420 ctl->met_nc_level =
7421 (int) scan_ctl(filename, argc, argv, "MET_NC_LEVEL", -1, "0", NULL);
7422 ctl->met_nc_quant =
7423 (int) scan_ctl(filename, argc, argv, "MET_NC_QUANT", -1, "0", NULL);
7424 ctl->met_zstd_level =
7425 (int) scan_ctl(filename, argc, argv, "MET_ZSTD_LEVEL", -1, "-3", NULL);
7426 ctl->met_zstd_nworkers =
7427 (int) scan_ctl(filename, argc, argv, "MET_ZSTD_NWORKERS", -1, "4", NULL);
7428 ctl->met_lz4_accel =
7429 (int) scan_ctl(filename, argc, argv, "MET_LZ4_ACCEL", -1, "8", NULL);
7430 ctl->met_pck_zstd =
7431 (int) scan_ctl(filename, argc, argv, "MET_PCK_ZSTD", -1, "0", NULL);
7432 if (ctl->met_pck_zstd != 0 && ctl->met_pck_zstd != 1)
7433 ERRMSG("Set MET_PCK_ZSTD to 0 or 1!");
7434#ifndef ZSTD
7435 if (ctl->met_type == 2 && ctl->met_pck_zstd)
7436 ERRMSG("MET_PCK_ZSTD requires MPTRAC to be compiled with ZSTD support!");
7437#endif
7438 const int def_lossy_scale =
7439 (int) scan_ctl(filename, argc, argv, "MET_LOSSY_SCALE", -1, "0", NULL);
7440 for (int i = 0; i < METVAR; i++) {
7441 char defprec_zfp[LEN] = "7", deftol_zfp[LEN] = "0.0";
7442 char defprec_sz3[LEN] = "6", deftol_sz3[LEN] = "0.0";
7443 if (i == 0) { /* geopotential height */
7444 sprintf(defprec_zfp, "12");
7445 sprintf(defprec_sz3, "11");
7446 } else if (i == 1) { /* temperature */
7447 sprintf(defprec_zfp, "11");
7448 sprintf(defprec_sz3, "7");
7449 } else if (i == 2 || i == 3) { /* horizontal wind */
7450 sprintf(defprec_zfp, "7");
7451 sprintf(defprec_sz3, "7");
7452 } else if (i == 4) { /* vertical wind */
7453 sprintf(defprec_zfp, "6");
7454 sprintf(defprec_sz3, "13");
7455 } else if (i == 5) { /* potential vorticity */
7456 sprintf(defprec_zfp, "7");
7457 sprintf(defprec_sz3, "20");
7458 } else if (i == 6) { /* water vapor */
7459 sprintf(defprec_zfp, "10");
7460 sprintf(defprec_sz3, "18");
7461 } else if (i == 7) { /* ozone */
7462 sprintf(defprec_zfp, "9");
7463 sprintf(defprec_sz3, "10");
7464 } else if (i >= 8 && i <= 11) { /* cloud water fields */
7465 sprintf(defprec_zfp, "6");
7466 sprintf(defprec_sz3, "13");
7467 } else if (i == 12) { /* cloud cover */
7468 sprintf(defprec_zfp, "9");
7469 sprintf(defprec_sz3, "6");
7470 }
7471 ctl->met_zfp_prec[i] =
7472 (int) scan_ctl(filename, argc, argv, "MET_ZFP_PREC", i, defprec_zfp,
7473 NULL);
7474 ctl->met_zfp_tol[i] =
7475 scan_ctl(filename, argc, argv, "MET_ZFP_TOL", i, deftol_zfp, NULL);
7476 ctl->met_sz3_prec[i] =
7477 (int) scan_ctl(filename, argc, argv, "MET_SZ3_PREC", i, defprec_sz3,
7478 NULL);
7479 ctl->met_sz3_tol[i] =
7480 scan_ctl(filename, argc, argv, "MET_SZ3_TOL", i, deftol_sz3, NULL);
7481 char defscale[LEN];
7482 snprintf(defscale, LEN, "%d", def_lossy_scale);
7483 ctl->met_lossy_scale[i] =
7484 (int) scan_ctl(filename, argc, argv, "MET_LOSSY_SCALE", i, defscale,
7485 NULL);
7486 if (ctl->met_lossy_scale[i] < 0 || ctl->met_lossy_scale[i] > 1)
7487 ERRMSG("Set MET_LOSSY_SCALE to 0 or 1!");
7488 }
7489
7490 /* Scan compression diagnostics file... */
7491 scan_ctl(filename, argc, argv, "MET_COMP_LOGFILE", -1, "-",
7492 ctl->met_comp_logfile);
7493 ctl->met_cms_batch =
7494 (int) scan_ctl(filename, argc, argv, "MET_CMS_BATCH", -1, "-1", NULL);
7495 ctl->met_cms_zstd =
7496 (int) scan_ctl(filename, argc, argv, "MET_CMS_ZSTD", -1, "1", NULL);
7497 ctl->met_cms_nd0x =
7498 (int) scan_ctl(filename, argc, argv, "MET_CMS_ND0X", -1, "48", NULL);
7499 ctl->met_cms_nd0y =
7500 (int) scan_ctl(filename, argc, argv, "MET_CMS_ND0Y", -1, "24", NULL);
7501 ctl->met_cms_maxlev =
7502 (int) scan_ctl(filename, argc, argv, "MET_CMS_MAXLEV", -1, "6", NULL);
7503 for (int i = 0; i < METVAR; i++) {
7504 char defeps[LEN] = "1.0";
7505 if (i == 1 || i == 2 || i == 3)
7506 sprintf(defeps, "0.05");
7507 ctl->met_cms_eps[i] =
7508 scan_ctl(filename, argc, argv, "MET_CMS_EPS", i, defeps, NULL);
7509 }
7510 ctl->met_dx = (int) scan_ctl(filename, argc, argv, "MET_DX", -1, "1", NULL);
7511 ctl->met_dy = (int) scan_ctl(filename, argc, argv, "MET_DY", -1, "1", NULL);
7512 ctl->met_dp = (int) scan_ctl(filename, argc, argv, "MET_DP", -1, "1", NULL);
7513 if (ctl->met_dx < 1 || ctl->met_dy < 1 || ctl->met_dp < 1)
7514 ERRMSG("MET_DX, MET_DY, and MET_DP need to be greater than zero!");
7515 ctl->met_sx = (int) scan_ctl(filename, argc, argv, "MET_SX", -1, "1", NULL);
7516 ctl->met_sy = (int) scan_ctl(filename, argc, argv, "MET_SY", -1, "1", NULL);
7517 ctl->met_sp = (int) scan_ctl(filename, argc, argv, "MET_SP", -1, "1", NULL);
7518 if (ctl->met_sx < 1 || ctl->met_sy < 1 || ctl->met_sp < 1)
7519 ERRMSG("MET_SX, MET_SY, and MET_SP need to be greater than zero!");
7520 ctl->met_detrend =
7521 scan_ctl(filename, argc, argv, "MET_DETREND", -1, "-999", NULL);
7522 ctl->met_np = (int) scan_ctl(filename, argc, argv, "MET_NP", -1, "0", NULL);
7523 if (ctl->met_np > EP)
7524 ERRMSG("Too many pressure levels!");
7525 ctl->met_press_level_def =
7526 (int) scan_ctl(filename, argc, argv, "MET_PRESS_LEVEL_DEF", -1, "-1",
7527 NULL);
7528 if (ctl->met_press_level_def >= 0) {
7529 level_definitions(ctl);
7530 } else {
7531 if (ctl->met_np > 0) {
7532 for (int ip = 0; ip < ctl->met_np; ip++)
7533 ctl->met_p[ip] =
7534 scan_ctl(filename, argc, argv, "MET_P", ip, "", NULL);
7535 }
7536 }
7537 ctl->met_nlev =
7538 (int) scan_ctl(filename, argc, argv, "MET_NLEV", -1, "0", NULL);
7539 if (ctl->met_nlev > EP)
7540 ERRMSG("Too many model levels!");
7541 for (int ip = 0; ip < ctl->met_nlev; ip++)
7542 ctl->met_lev_hyam[ip] =
7543 scan_ctl(filename, argc, argv, "MET_LEV_HYAM", ip, "", NULL);
7544 for (int ip = 0; ip < ctl->met_nlev; ip++)
7545 ctl->met_lev_hybm[ip] =
7546 scan_ctl(filename, argc, argv, "MET_LEV_HYBM", ip, "", NULL);
7547 ctl->met_geopot_sx =
7548 (int) scan_ctl(filename, argc, argv, "MET_GEOPOT_SX", -1, "-1", NULL);
7549 ctl->met_geopot_sy =
7550 (int) scan_ctl(filename, argc, argv, "MET_GEOPOT_SY", -1, "-1", NULL);
7551 ctl->met_relhum =
7552 (int) scan_ctl(filename, argc, argv, "MET_RELHUM", -1, "0", NULL);
7553 ctl->met_cape =
7554 (int) scan_ctl(filename, argc, argv, "MET_CAPE", -1, "1", NULL);
7555 if (ctl->met_cape < 0 || ctl->met_cape > 1)
7556 ERRMSG("Set MET_CAPE to 0 or 1!");
7557 ctl->met_pbl =
7558 (int) scan_ctl(filename, argc, argv, "MET_PBL", -1, "3", NULL);
7559 if (ctl->met_pbl < 0 || ctl->met_pbl > 3)
7560 ERRMSG("Set MET_PBL to 0 ... 3!");
7561 ctl->met_pbl_min =
7562 scan_ctl(filename, argc, argv, "MET_PBL_MIN", -1, "0.1", NULL);
7563 ctl->met_pbl_max =
7564 scan_ctl(filename, argc, argv, "MET_PBL_MAX", -1, "5.0", NULL);
7565 ctl->met_tropo =
7566 (int) scan_ctl(filename, argc, argv, "MET_TROPO", -1, "3", NULL);
7567 if (ctl->met_tropo < 0 || ctl->met_tropo > 5)
7568 ERRMSG("Set MET_TROPO to 0 ... 5!");
7569 ctl->met_tropo_pv =
7570 scan_ctl(filename, argc, argv, "MET_TROPO_PV", -1, "3.5", NULL);
7571 ctl->met_tropo_theta =
7572 scan_ctl(filename, argc, argv, "MET_TROPO_THETA", -1, "380", NULL);
7573 ctl->met_tropo_spline =
7574 (int) scan_ctl(filename, argc, argv, "MET_TROPO_SPLINE", -1, "1", NULL);
7575 ctl->met_dt_out =
7576 scan_ctl(filename, argc, argv, "MET_DT_OUT", -1, "0.1", NULL);
7577 const int meteo_sampled =
7578 (ctl->qnt_ps >= 0 || ctl->qnt_ts >= 0 || ctl->qnt_zs >= 0
7579 || ctl->qnt_us >= 0 || ctl->qnt_vs >= 0 || ctl->qnt_ess >= 0
7580 || ctl->qnt_nss >= 0 || ctl->qnt_shf >= 0 || ctl->qnt_lsm >= 0
7581 || ctl->qnt_sst >= 0 || ctl->qnt_pbl >= 0 || ctl->qnt_pt >= 0
7582 || ctl->qnt_tt >= 0 || ctl->qnt_zt >= 0 || ctl->qnt_h2ot >= 0
7583 || ctl->qnt_zg >= 0 || ctl->qnt_p >= 0 || ctl->qnt_t >= 0
7584 || ctl->qnt_rho >= 0 || ctl->qnt_u >= 0 || ctl->qnt_v >= 0
7585 || ctl->qnt_w >= 0 || ctl->qnt_h2o >= 0 || ctl->qnt_o3 >= 0
7586 || ctl->qnt_lwc >= 0 || ctl->qnt_rwc >= 0 || ctl->qnt_iwc >= 0
7587 || ctl->qnt_swc >= 0 || ctl->qnt_cc >= 0 || ctl->qnt_pct >= 0
7588 || ctl->qnt_pcb >= 0 || ctl->qnt_cl >= 0 || ctl->qnt_plcl >= 0
7589 || ctl->qnt_plfc >= 0 || ctl->qnt_pel >= 0 || ctl->qnt_cape >= 0
7590 || ctl->qnt_cin >= 0 || ctl->qnt_o3c >= 0 || ctl->qnt_hno3 >= 0
7591 || ctl->qnt_oh >= 0 || ctl->qnt_h2o2 >= 0 || ctl->qnt_ho2 >= 0
7592 || ctl->qnt_o1d >= 0 || ctl->qnt_vh >= 0 || ctl->qnt_vz >= 0
7593 || ctl->qnt_psat >= 0 || ctl->qnt_psice >= 0 || ctl->qnt_pw >= 0
7594 || ctl->qnt_sh >= 0 || ctl->qnt_rh >= 0 || ctl->qnt_rhice >= 0
7595 || ctl->qnt_theta >= 0 || ctl->qnt_zeta >= 0
7596 || ctl->qnt_zeta_d >= 0 || ctl->qnt_eta >= 0
7597 || ctl->qnt_eta_d >= 0 || ctl->qnt_zeta_dot >= 0
7598 || ctl->qnt_eta_dot >= 0 || ctl->qnt_tvirt >= 0
7599 || ctl->qnt_lapse >= 0 || ctl->qnt_pv >= 0
7600 || ctl->qnt_tdew >= 0 || ctl->qnt_tice >= 0
7601 || ctl->qnt_tnat >= 0 || ctl->qnt_tsts >= 0);
7602 if (meteo_sampled && ctl->met_dt_out >= ctl->dt_mod
7603 && !DT_ALIGNED(ctl->met_dt_out, ctl->dt_mod))
7604 WARN("MET_DT_OUT is not an integer multiple of DT_MOD; meteorological sampling may be skipped!");
7605 ctl->met_cache =
7606 (int) scan_ctl(filename, argc, argv, "MET_CACHE", -1, "0", NULL);
7607 ctl->met_mpi_share =
7608 (int) scan_ctl(filename, argc, argv, "MET_MPI_SHARE", -1, "0", NULL);
7609
7610 /* Sorting... */
7611 ctl->sort_dt = scan_ctl(filename, argc, argv, "SORT_DT", -1, "-999", NULL);
7612
7613 /* Isosurface parameters... */
7614 ctl->isosurf =
7615 (int) scan_ctl(filename, argc, argv, "ISOSURF", -1, "0", NULL);
7616 if (ctl->isosurf >= 1 && ctl->isosurf <= 3 && ctl->qnt_iso_var < 0)
7617 ERRMSG("Add quantity iso_var for ISOSURF=1, 2, or 3!");
7618 scan_ctl(filename, argc, argv, "BALLOON", -1, "-", ctl->balloon);
7619
7620 /* Random number generator... */
7621 ctl->rng_type =
7622 (int) scan_ctl(filename, argc, argv, "RNG_TYPE", -1, "1", NULL);
7623 if (ctl->rng_type < 0 || ctl->rng_type > 2)
7624 ERRMSG("Set RNG_TYPE to 0, 1, or 2!");
7625
7626 /* Advection parameters... */
7627 ctl->advect = (int) scan_ctl(filename, argc, argv, "ADVECT", -1, "2", NULL);
7628 if (!(ctl->advect == 1 || ctl->advect == 2))
7629 ERRMSG("Set ADVECT to 1 (Euler) or 2 (midpoint)!");
7630
7631 /* Diffusion parameters... */
7632 ctl->diffusion
7633 = (int) scan_ctl(filename, argc, argv, "DIFFUSION", -1, "0", NULL);
7634 if (ctl->diffusion < 0 || ctl->diffusion > 1)
7635 ERRMSG("Set DIFFUSION to 0 or 1!");
7636 ctl->turb_pbl_scheme =
7637 (int) scan_ctl(filename, argc, argv, "TURB_PBL_SCHEME", -1, "0", NULL);
7638 if (ctl->turb_pbl_scheme < 0 || ctl->turb_pbl_scheme > 1)
7639 ERRMSG("Set TURB_PBL_SCHEME to 0 or 1!");
7640 ctl->turb_dx_pbl =
7641 scan_ctl(filename, argc, argv, "TURB_DX_PBL", -1, "50", NULL);
7642 ctl->turb_dx_trop =
7643 scan_ctl(filename, argc, argv, "TURB_DX_TROP", -1, "50", NULL);
7644 ctl->turb_dx_strat =
7645 scan_ctl(filename, argc, argv, "TURB_DX_STRAT", -1, "0", NULL);
7646 ctl->turb_dz_pbl =
7647 scan_ctl(filename, argc, argv, "TURB_DZ_PBL", -1, "0", NULL);
7648 ctl->turb_dz_trop =
7649 scan_ctl(filename, argc, argv, "TURB_DZ_TROP", -1, "0", NULL);
7650 ctl->turb_dz_strat =
7651 scan_ctl(filename, argc, argv, "TURB_DZ_STRAT", -1, "0.1", NULL);
7652 ctl->turb_mesox =
7653 scan_ctl(filename, argc, argv, "TURB_MESOX", -1, "0.16", NULL);
7654 ctl->turb_mesoz =
7655 scan_ctl(filename, argc, argv, "TURB_MESOZ", -1, "0.16", NULL);
7656 ctl->turb_pbl_trans =
7657 scan_ctl(filename, argc, argv, "TURB_PBL_TRANS", -1, "0", NULL);
7658 if (ctl->turb_pbl_trans < 0 || ctl->turb_pbl_trans > 1)
7659 ERRMSG("TURB_PBL_TRANS must be in the range [0, 1]!");
7660
7661 /* Convection... */
7662 ctl->conv_mix_pbl
7663 = (int) scan_ctl(filename, argc, argv, "CONV_MIX_PBL", -1, "0", NULL);
7664 ctl->conv_pbl_trans
7665 = scan_ctl(filename, argc, argv, "CONV_PBL_TRANS", -1, "0", NULL);
7666 if (ctl->conv_pbl_trans < 0 || ctl->conv_pbl_trans > 1)
7667 ERRMSG("CONV_PBL_TRANS must be in the range [0, 1]!");
7668 ctl->conv_cape
7669 = scan_ctl(filename, argc, argv, "CONV_CAPE", -1, "-999", NULL);
7670 ctl->conv_cin
7671 = scan_ctl(filename, argc, argv, "CONV_CIN", -1, "-999", NULL);
7672 ctl->conv_dt = scan_ctl(filename, argc, argv, "CONV_DT", -1, "-999", NULL);
7673 if (ctl->conv_cape >= 0 || ctl->conv_mix_pbl) {
7674 if (!isfinite(ctl->conv_dt))
7675 ERRMSG("CONV_DT must be finite!");
7676 if (ctl->conv_dt > 0 && !DT_ALIGNED(ctl->conv_dt, ctl->dt_mod))
7677 ERRMSG("CONV_DT must be an integer multiple of DT_MOD!");
7678 }
7679 if (ctl->conv_cape >= 0 && ctl->met_cape == 0
7680 && (ctl->met_type == 0 || ctl->met_type == 6))
7681 ERRMSG("ECP with MET_CAPE=0 requires binary meteo data with EL pressure; netCDF and GRIB readers do not load EL!");
7682
7683 /* Boundary conditions... */
7684 ctl->bound_mass =
7685 scan_ctl(filename, argc, argv, "BOUND_MASS", -1, "-999", NULL);
7686 ctl->bound_mass_trend =
7687 scan_ctl(filename, argc, argv, "BOUND_MASS_TREND", -1, "0", NULL);
7688 ctl->bound_vmr =
7689 scan_ctl(filename, argc, argv, "BOUND_VMR", -1, "-999", NULL);
7690 ctl->bound_vmr_trend =
7691 scan_ctl(filename, argc, argv, "BOUND_VMR_TREND", -1, "0", NULL);
7692 ctl->bound_lat0 =
7693 scan_ctl(filename, argc, argv, "BOUND_LAT0", -1, "-999", NULL);
7694 ctl->bound_lat1 =
7695 scan_ctl(filename, argc, argv, "BOUND_LAT1", -1, "-999", NULL);
7696 ctl->bound_p0 =
7697 scan_ctl(filename, argc, argv, "BOUND_P0", -1, "-999", NULL);
7698 ctl->bound_p1 =
7699 scan_ctl(filename, argc, argv, "BOUND_P1", -1, "-999", NULL);
7700 ctl->bound_dps =
7701 scan_ctl(filename, argc, argv, "BOUND_DPS", -1, "-999", NULL);
7702 ctl->bound_dzs =
7703 scan_ctl(filename, argc, argv, "BOUND_DZS", -1, "-999", NULL);
7704 ctl->bound_zetas =
7705 scan_ctl(filename, argc, argv, "BOUND_ZETAS", -1, "-999", NULL);
7706 ctl->bound_pbl =
7707 (int) scan_ctl(filename, argc, argv, "BOUND_PBL", -1, "0", NULL);
7708
7709 /* Species parameters... */
7710 scan_ctl(filename, argc, argv, "SPECIES", -1, "-", ctl->species);
7711 if (strcasecmp(ctl->species, "CF2Cl2") == 0) {
7712 ctl->molmass = 120.907;
7713 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 3e-5;
7714 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 3500.0;
7715 } else if (strcasecmp(ctl->species, "CFCl3") == 0) {
7716 ctl->molmass = 137.359;
7717 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 1.1e-4;
7718 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 3300.0;
7719 } else if (strcasecmp(ctl->species, "CH4") == 0) {
7720 ctl->molmass = 16.043;
7721 ctl->oh_chem_reaction = 2;
7722 ctl->oh_chem[0] = 2.45e-12;
7723 ctl->oh_chem[1] = 1775;
7724 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 1.4e-5;
7725 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 1600.0;
7726 } else if (strcasecmp(ctl->species, "CO") == 0) {
7727 ctl->molmass = 28.01;
7728 ctl->oh_chem_reaction = 3;
7729 ctl->oh_chem[0] = 6.9e-33;
7730 ctl->oh_chem[1] = 2.1;
7731 ctl->oh_chem[2] = 1.1e-12;
7732 ctl->oh_chem[3] = -1.3;
7733 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 9.7e-6;
7734 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 1300.0;
7735 } else if (strcasecmp(ctl->species, "CO2") == 0) {
7736 ctl->molmass = 44.009;
7737 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 3.3e-4;
7738 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 2400.0;
7739 } else if (strcasecmp(ctl->species, "H2O") == 0) {
7740 ctl->molmass = 18.01528;
7741 } else if (strcasecmp(ctl->species, "N2O") == 0) {
7742 ctl->molmass = 44.013;
7743 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 2.4e-4;
7744 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 2600.;
7745 } else if (strcasecmp(ctl->species, "NH3") == 0) {
7746 ctl->molmass = 17.031;
7747 ctl->oh_chem_reaction = 2;
7748 ctl->oh_chem[0] = 1.7e-12;
7749 ctl->oh_chem[1] = 710;
7750 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 5.9e-1;
7751 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 4200.0;
7752 } else if (strcasecmp(ctl->species, "HNO3") == 0) {
7753 ctl->molmass = 63.012;
7754 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 2.1e3;
7755 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 8700.0;
7756 } else if (strcasecmp(ctl->species, "NO") == 0) {
7757 ctl->molmass = 30.006;
7758 ctl->oh_chem_reaction = 3;
7759 ctl->oh_chem[0] = 7.1e-31;
7760 ctl->oh_chem[1] = 2.6;
7761 ctl->oh_chem[2] = 3.6e-11;
7762 ctl->oh_chem[3] = 0.1;
7763 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 1.9e-5;
7764 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 1600.0;
7765 } else if (strcasecmp(ctl->species, "NO2") == 0) {
7766 ctl->molmass = 46.005;
7767 ctl->oh_chem_reaction = 3;
7768 ctl->oh_chem[0] = 1.8e-30;
7769 ctl->oh_chem[1] = 3.0;
7770 ctl->oh_chem[2] = 2.8e-11;
7771 ctl->oh_chem[3] = 0.0;
7772 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 1.2e-4;
7773 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 2400.0;
7774 } else if (strcasecmp(ctl->species, "O3") == 0) {
7775 ctl->molmass = 47.997;
7776 ctl->oh_chem_reaction = 2;
7777 ctl->oh_chem[0] = 1.7e-12;
7778 ctl->oh_chem[1] = 940;
7779 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 1e-4;
7780 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 2800.0;
7781 } else if (strcasecmp(ctl->species, "SF6") == 0) {
7782 ctl->molmass = 146.048;
7783 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = 2.4e-6;
7784 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = 3100.0;
7785 } else if (strcasecmp(ctl->species, "SO2") == 0) {
7786 ctl->molmass = 64.066;
7787 ctl->oh_chem_reaction = 3;
7788 ctl->oh_chem[0] = 2.9e-31;
7789 ctl->oh_chem[1] = 4.1;
7790 ctl->oh_chem[2] = 1.7e-12;
7791 ctl->oh_chem[3] = -0.2;
7792 ctl->wet_depo_ic_h[0] = ctl->wet_depo_bc_h[0] = SO2_HENRY_REF;
7793 ctl->wet_depo_ic_h[1] = ctl->wet_depo_bc_h[1] = SO2_HENRY_TEMP;
7794 }
7795
7796 /* Molar mass... */
7797 char defstr[LEN];
7798 sprintf(defstr, "%g", ctl->molmass);
7799 ctl->molmass = scan_ctl(filename, argc, argv, "MOLMASS", -1, defstr, NULL);
7800
7801 /* OH chemistry... */
7802 sprintf(defstr, "%d", ctl->oh_chem_reaction);
7803 ctl->oh_chem_reaction =
7804 (int) scan_ctl(filename, argc, argv, "OH_CHEM_REACTION", -1, defstr,
7805 NULL);
7806 for (int ip = 0; ip < 4; ip++) {
7807 sprintf(defstr, "%g", ctl->oh_chem[ip]);
7808 ctl->oh_chem[ip] =
7809 scan_ctl(filename, argc, argv, "OH_CHEM", ip, defstr, NULL);
7810 }
7811 ctl->oh_chem_beta =
7812 scan_ctl(filename, argc, argv, "OH_CHEM_BETA", -1, "0", NULL);
7813
7814 /* H2O2 chemistry... */
7815 ctl->h2o2_chem_reaction =
7816 (int) scan_ctl(filename, argc, argv, "H2O2_CHEM_REACTION", -1, "0", NULL);
7817
7818 /* KPP chemistry... */
7819 ctl->kpp_chem =
7820 (int) scan_ctl(filename, argc, argv, "KPP_CHEM", -1, "0", NULL);
7821 ctl->dt_kpp = scan_ctl(filename, argc, argv, "DT_KPP", -1, "1800", NULL);
7822 if (ctl->kpp_chem && !DT_ALIGNED(ctl->dt_kpp, ctl->dt_mod))
7823 ERRMSG("DT_KPP must be a positive integer multiple of DT_MOD!");
7824
7825 /* First order tracer chemistry... */
7826 ctl->tracer_chem =
7827 (int) scan_ctl(filename, argc, argv, "TRACER_CHEM", -1, "0", NULL);
7828
7829 /* Radioactive decay... */
7830 ctl->radio_decay =
7831 (int) scan_ctl(filename, argc, argv, "RADIO_DECAY", -1, "0", NULL);
7832 ctl->radio_depo =
7833 (int) scan_ctl(filename, argc, argv, "RADIO_DEPO", -1, "0", NULL);
7834 if (ctl->radio_depo && ctl->met_coord_type != 0)
7835 ERRMSG("Radioactive deposition requires a lat/lon meteorological grid!");
7836
7837 /* Wet deposition... */
7838 for (int ip = 0; ip < 2; ip++) {
7839 sprintf(defstr, "%g", ctl->wet_depo_ic_h[ip]);
7840 ctl->wet_depo_ic_h[ip] =
7841 scan_ctl(filename, argc, argv, "WET_DEPO_IC_H", ip, defstr, NULL);
7842 }
7843 for (int ip = 0; ip < 1; ip++) {
7844 sprintf(defstr, "%g", ctl->wet_depo_bc_h[ip]);
7845 ctl->wet_depo_bc_h[ip] =
7846 scan_ctl(filename, argc, argv, "WET_DEPO_BC_H", ip, defstr, NULL);
7847 }
7848 ctl->wet_depo_so2_ph =
7849 scan_ctl(filename, argc, argv, "WET_DEPO_SO2_PH", -1, "0", NULL);
7850 ctl->wet_depo_ic_a =
7851 scan_ctl(filename, argc, argv, "WET_DEPO_IC_A", -1, "0", NULL);
7852 ctl->wet_depo_ic_b =
7853 scan_ctl(filename, argc, argv, "WET_DEPO_IC_B", -1, "0", NULL);
7854 ctl->wet_depo_bc_a =
7855 scan_ctl(filename, argc, argv, "WET_DEPO_BC_A", -1, "0", NULL);
7856 ctl->wet_depo_bc_b =
7857 scan_ctl(filename, argc, argv, "WET_DEPO_BC_B", -1, "0", NULL);
7858 ctl->wet_depo_pre[0] =
7859 scan_ctl(filename, argc, argv, "WET_DEPO_PRE", 0, "0.5", NULL);
7860 ctl->wet_depo_pre[1] =
7861 scan_ctl(filename, argc, argv, "WET_DEPO_PRE", 1, "0.36", NULL);
7863 scan_ctl(filename, argc, argv, "WET_DEPO_IC_RET_RATIO", -1, "1", NULL);
7865 scan_ctl(filename, argc, argv, "WET_DEPO_BC_RET_RATIO", -1, "1", NULL);
7866
7867 /* Dry deposition... */
7868 ctl->dry_depo_vdep =
7869 scan_ctl(filename, argc, argv, "DRY_DEPO_VDEP", -1, "0", NULL);
7870 ctl->dry_depo_dp =
7871 scan_ctl(filename, argc, argv, "DRY_DEPO_DP", -1, "30", NULL);
7872
7873 /* Climatological data... */
7874 scan_ctl(filename, argc, argv, "CLIM_PHOTO", -1,
7875 "../../data/clams_photolysis_rates.nc", ctl->clim_photo);
7876 scan_ctl(filename, argc, argv, "CLIM_HNO3_FILENAME", -1,
7877 "../../data/gozcards_HNO3.nc", ctl->clim_hno3_filename);
7878 scan_ctl(filename, argc, argv, "CLIM_OH_FILENAME", -1,
7879 "../../data/clams_radical_species_vmr.nc", ctl->clim_oh_filename);
7880 scan_ctl(filename, argc, argv, "CLIM_H2O2_FILENAME", -1,
7881 "../../data/cams_H2O2.nc", ctl->clim_h2o2_filename);
7882 scan_ctl(filename, argc, argv, "CLIM_HO2_FILENAME", -1,
7883 "../../data/clams_radical_species_vmr.nc", ctl->clim_ho2_filename);
7884 scan_ctl(filename, argc, argv, "CLIM_O1D_FILENAME", -1,
7885 "../../data/clams_radical_species_vmr.nc", ctl->clim_o1d_filename);
7886 scan_ctl(filename, argc, argv, "CLIM_CCL4_TIMESERIES", -1,
7887 "../../data/noaa_gml_ccl4.tab", ctl->clim_ccl4_timeseries);
7888 scan_ctl(filename, argc, argv, "CLIM_CCL3F_TIMESERIES", -1,
7889 "../../data/noaa_gml_cfc11.tab", ctl->clim_ccl3f_timeseries);
7890 scan_ctl(filename, argc, argv, "CLIM_CCL2F2_TIMESERIES", -1,
7891 "../../data/noaa_gml_cfc12.tab", ctl->clim_ccl2f2_timeseries);
7892 scan_ctl(filename, argc, argv, "CLIM_N2O_TIMESERIES", -1,
7893 "../../data/noaa_gml_n2o.tab", ctl->clim_n2o_timeseries);
7894 scan_ctl(filename, argc, argv, "CLIM_SF6_TIMESERIES", -1,
7895 "../../data/noaa_gml_sf6.tab", ctl->clim_sf6_timeseries);
7896
7897 /* Mixing... */
7898 ctl->mixing_dt =
7899 scan_ctl(filename, argc, argv, "MIXING_DT", -1, "3600.", NULL);
7900 ctl->mixing_tau_trop =
7901 scan_ctl(filename, argc, argv, "MIXING_TAU_TROP", -1, "-999", NULL);
7902 ctl->mixing_tau_strat =
7903 scan_ctl(filename, argc, argv, "MIXING_TAU_STRAT", -1, "-999", NULL);
7904 ctl->mixing_z0 =
7905 scan_ctl(filename, argc, argv, "MIXING_Z0", -1, "-5", NULL);
7906 ctl->mixing_z1 =
7907 scan_ctl(filename, argc, argv, "MIXING_Z1", -1, "85", NULL);
7908 ctl->mixing_nz =
7909 (int) scan_ctl(filename, argc, argv, "MIXING_NZ", -1, "90", NULL);
7910 ctl->mixing_lon0 =
7911 scan_ctl(filename, argc, argv, "MIXING_LON0", -1, "-180", NULL);
7912 ctl->mixing_lon1 =
7913 scan_ctl(filename, argc, argv, "MIXING_LON1", -1, "180", NULL);
7914 ctl->mixing_nx =
7915 (int) scan_ctl(filename, argc, argv, "MIXING_NX", -1, "360", NULL);
7916 ctl->mixing_lat0 =
7917 scan_ctl(filename, argc, argv, "MIXING_LAT0", -1, "-90", NULL);
7918 ctl->mixing_lat1 =
7919 scan_ctl(filename, argc, argv, "MIXING_LAT1", -1, "90", NULL);
7920 ctl->mixing_ny =
7921 (int) scan_ctl(filename, argc, argv, "MIXING_NY", -1, "180", NULL);
7922 if (ctl->mixing_nx < 1 || ctl->mixing_ny < 1 || ctl->mixing_nz < 1
7923 || ctl->mixing_lon0 >= ctl->mixing_lon1
7924 || ctl->mixing_lat0 >= ctl->mixing_lat1
7925 || ctl->mixing_z0 >= ctl->mixing_z1
7926 || ctl->mixing_lat0 < -90 || ctl->mixing_lat1 > 90)
7927 ERRMSG("Invalid mixing grid!");
7928 if ((ctl->mixing_tau_trop > 0) != (ctl->mixing_tau_strat > 0)
7929 || ctl->mixing_tau_trop == 0 || ctl->mixing_tau_strat == 0)
7930 ERRMSG("Mixing times must either both be positive or both be disabled!");
7931 if (ctl->mixing_tau_trop > 0) {
7932 if (!isfinite(ctl->mixing_dt))
7933 ERRMSG("MIXING_DT must be finite!");
7934 if (ctl->mixing_dt > 0 && !DT_ALIGNED(ctl->mixing_dt, ctl->dt_mod))
7935 ERRMSG("MIXING_DT must be an integer multiple of DT_MOD!");
7936 }
7937 if (ctl->mixing_tau_trop > 0 && ctl->met_coord_type == 1)
7938 ERRMSG("Interparcel mixing requires geographic coordinates (MET_COORD_TYPE=0)!");
7939
7940 /* Chemistry grid... */
7941 ctl->chemgrid_z0 =
7942 scan_ctl(filename, argc, argv, "CHEMGRID_Z0", -1, "-5", NULL);
7943 ctl->chemgrid_z1 =
7944 scan_ctl(filename, argc, argv, "CHEMGRID_Z1", -1, "85", NULL);
7945 ctl->chemgrid_nz =
7946 (int) scan_ctl(filename, argc, argv, "CHEMGRID_NZ", -1, "90", NULL);
7947 ctl->chemgrid_lon0 =
7948 scan_ctl(filename, argc, argv, "CHEMGRID_LON0", -1, "-180", NULL);
7949 ctl->chemgrid_lon1 =
7950 scan_ctl(filename, argc, argv, "CHEMGRID_LON1", -1, "180", NULL);
7951 ctl->chemgrid_nx =
7952 (int) scan_ctl(filename, argc, argv, "CHEMGRID_NX", -1, "360", NULL);
7953 ctl->chemgrid_lat0 =
7954 scan_ctl(filename, argc, argv, "CHEMGRID_LAT0", -1, "-90", NULL);
7955 ctl->chemgrid_lat1 =
7956 scan_ctl(filename, argc, argv, "CHEMGRID_LAT1", -1, "90", NULL);
7957 ctl->chemgrid_ny =
7958 (int) scan_ctl(filename, argc, argv, "CHEMGRID_NY", -1, "180", NULL);
7959 if (ctl->chemgrid_nx < 1 || ctl->chemgrid_ny < 1 || ctl->chemgrid_nz < 1
7960 || ctl->chemgrid_lon0 >= ctl->chemgrid_lon1
7961 || ctl->chemgrid_lat0 >= ctl->chemgrid_lat1
7962 || ctl->chemgrid_z0 >= ctl->chemgrid_z1
7963 || ctl->chemgrid_lat0 < -90 || ctl->chemgrid_lat1 > 90)
7964 ERRMSG("Invalid chemistry grid!");
7965
7966 /* Exponential decay... */
7967 ctl->tdec_trop = scan_ctl(filename, argc, argv, "TDEC_TROP", -1, "0", NULL);
7968 ctl->tdec_strat =
7969 scan_ctl(filename, argc, argv, "TDEC_STRAT", -1, "0", NULL);
7970
7971 /* PSC analysis... */
7972 ctl->psc_h2o = scan_ctl(filename, argc, argv, "PSC_H2O", -1, "4e-6", NULL);
7973 ctl->psc_hno3 =
7974 scan_ctl(filename, argc, argv, "PSC_HNO3", -1, "9e-9", NULL);
7975
7976 /* Output of atmospheric data... */
7977 scan_ctl(filename, argc, argv, "ATM_BASENAME", -1, "-", ctl->atm_basename);
7978 scan_ctl(filename, argc, argv, "ATM_GPFILE", -1, "-", ctl->atm_gpfile);
7979 ctl->atm_dt_out =
7980 scan_ctl(filename, argc, argv, "ATM_DT_OUT", -1, "86400", NULL);
7981 if (ctl->atm_basename[0] != '-' && ctl->atm_dt_out > 0
7982 && !DT_ALIGNED(ctl->atm_dt_out, ctl->dt_mod))
7983 WARN("ATM_DT_OUT is not an integer multiple of DT_MOD; output may be skipped!");
7984 ctl->atm_filter =
7985 (int) scan_ctl(filename, argc, argv, "ATM_FILTER", -1, "0", NULL);
7986 ctl->atm_stride =
7987 (int) scan_ctl(filename, argc, argv, "ATM_STRIDE", -1, "1", NULL);
7988 ctl->atm_type =
7989 (int) scan_ctl(filename, argc, argv, "ATM_TYPE", -1, "0", NULL);
7990 ctl->atm_type_out =
7991 (int) scan_ctl(filename, argc, argv, "ATM_TYPE_OUT", -1, "-1", NULL);
7992 if (ctl->atm_type_out == -1)
7993 ctl->atm_type_out = ctl->atm_type;
7994 ctl->atm_nc_level =
7995 (int) scan_ctl(filename, argc, argv, "ATM_NC_LEVEL", -1, "0", NULL);
7996 for (int iq = 0; iq < ctl->nq; iq++)
7997 ctl->atm_nc_quant[iq] =
7998 (int) scan_ctl(filename, argc, argv, "ATM_NC_QUANT", iq, "0", NULL);
7999 ctl->obs_type =
8000 (int) scan_ctl(filename, argc, argv, "OBS_TYPE", -1, "0", NULL);
8001
8002 /* Output of mass budget data... */
8003 scan_ctl(filename, argc, argv, "BUDGET_BASENAME", -1, "-",
8004 ctl->budget_basename);
8005 ctl->budget_dt_out =
8006 scan_ctl(filename, argc, argv, "BUDGET_DT_OUT", -1, "86400", NULL);
8007 if (ctl->budget_basename[0] != '-' && ctl->budget_dt_out > 0
8008 && !DT_ALIGNED(ctl->budget_dt_out, ctl->dt_mod))
8009 WARN("BUDGET_DT_OUT is not an integer multiple of DT_MOD; output may be skipped!");
8010 if (ctl->budget_basename[0] != '-' && ctl->budget_dt_out <= 0)
8011 ERRMSG("Invalid mass budget output settings!");
8012
8013 /* Output of radioactive deposition data... */
8014 scan_ctl(filename, argc, argv, "DEPO_BASENAME", -1, "-",
8015 ctl->depo_basename);
8016 ctl->depo_dt_out =
8017 scan_ctl(filename, argc, argv, "DEPO_DT_OUT", -1, "86400", NULL);
8018 if (ctl->depo_basename[0] != '-' && ctl->depo_dt_out > 0
8019 && !DT_ALIGNED(ctl->depo_dt_out, ctl->dt_mod))
8020 WARN("DEPO_DT_OUT is not an integer multiple of DT_MOD; output may be skipped!");
8021 ctl->depo_type =
8022 (int) scan_ctl(filename, argc, argv, "DEPO_TYPE", -1, "0", NULL);
8023
8024 /* Output of CSI data... */
8025 scan_ctl(filename, argc, argv, "CSI_BASENAME", -1, "-", ctl->csi_basename);
8026 scan_ctl(filename, argc, argv, "CSI_KERNEL", -1, "-", ctl->csi_kernel);
8027 ctl->csi_dt_out =
8028 scan_ctl(filename, argc, argv, "CSI_DT_OUT", -1, "86400", NULL);
8029 if (ctl->csi_basename[0] != '-' && ctl->csi_dt_out > 0
8030 && !DT_ALIGNED(ctl->csi_dt_out, ctl->dt_mod))
8031 WARN("CSI_DT_OUT is not an integer multiple of DT_MOD; output may be skipped!");
8032 scan_ctl(filename, argc, argv, "CSI_OBSFILE", -1, "-", ctl->csi_obsfile);
8033 ctl->csi_obsmin =
8034 scan_ctl(filename, argc, argv, "CSI_OBSMIN", -1, "0", NULL);
8035 ctl->csi_modmin =
8036 scan_ctl(filename, argc, argv, "CSI_MODMIN", -1, "0", NULL);
8037 ctl->csi_z0 = scan_ctl(filename, argc, argv, "CSI_Z0", -1, "-5", NULL);
8038 ctl->csi_z1 = scan_ctl(filename, argc, argv, "CSI_Z1", -1, "85", NULL);
8039 ctl->csi_nz = (int) scan_ctl(filename, argc, argv, "CSI_NZ", -1, "1", NULL);
8040 ctl->csi_lon0 =
8041 scan_ctl(filename, argc, argv, "CSI_LON0", -1, "-180", NULL);
8042 ctl->csi_lon1 = scan_ctl(filename, argc, argv, "CSI_LON1", -1, "180", NULL);
8043 ctl->csi_nx =
8044 (int) scan_ctl(filename, argc, argv, "CSI_NX", -1, "360", NULL);
8045 ctl->csi_lat0 = scan_ctl(filename, argc, argv, "CSI_LAT0", -1, "-90", NULL);
8046 ctl->csi_lat1 = scan_ctl(filename, argc, argv, "CSI_LAT1", -1, "90", NULL);
8047 ctl->csi_ny =
8048 (int) scan_ctl(filename, argc, argv, "CSI_NY", -1, "180", NULL);
8049 if (ctl->csi_nx < 1 || ctl->csi_ny < 1 || ctl->csi_nz < 1
8050 || ctl->csi_lon0 >= ctl->csi_lon1
8051 || ctl->csi_lat0 >= ctl->csi_lat1 || ctl->csi_z0 >= ctl->csi_z1
8052 || ctl->csi_lat0 < -90 || ctl->csi_lat1 > 90)
8053 ERRMSG("Invalid CSI grid!");
8054
8055 /* Output of ensemble data... */
8056 ctl->nens = (int) scan_ctl(filename, argc, argv, "NENS", -1, "0", NULL);
8057 if (ctl->nens < 0)
8058 ERRMSG("NENS must not be negative!");
8059 if (ctl->nens > 0 && ctl->qnt_ens < 0)
8060 ERRMSG("Add quantity ens for ensemble calculations!");
8061 scan_ctl(filename, argc, argv, "ENS_BASENAME", -1, "-", ctl->ens_basename);
8062 ctl->ens_dt_out =
8063 scan_ctl(filename, argc, argv, "ENS_DT_OUT", -1, "86400", NULL);
8064 if (ctl->ens_basename[0] != '-' && ctl->ens_dt_out > 0
8065 && !DT_ALIGNED(ctl->ens_dt_out, ctl->dt_mod))
8066 WARN("ENS_DT_OUT is not an integer multiple of DT_MOD; output may be skipped!");
8067
8068 /* Output of grid data... */
8069 scan_ctl(filename, argc, argv, "GRID_BASENAME", -1, "-",
8070 ctl->grid_basename);
8071 scan_ctl(filename, argc, argv, "GRID_KERNEL", -1, "-", ctl->grid_kernel);
8072 scan_ctl(filename, argc, argv, "GRID_GPFILE", -1, "-", ctl->grid_gpfile);
8073 ctl->grid_dt_out =
8074 scan_ctl(filename, argc, argv, "GRID_DT_OUT", -1, "86400", NULL);
8075 if (ctl->grid_basename[0] != '-' && ctl->grid_dt_out > 0
8076 && !DT_ALIGNED(ctl->grid_dt_out, ctl->dt_mod))
8077 WARN("GRID_DT_OUT is not an integer multiple of DT_MOD; output may be skipped!");
8078 ctl->grid_sparse =
8079 (int) scan_ctl(filename, argc, argv, "GRID_SPARSE", -1, "0", NULL);
8080 ctl->grid_nc_level =
8081 (int) scan_ctl(filename, argc, argv, "GRID_NC_LEVEL", -1, "0", NULL);
8082 for (int iq = 0; iq < ctl->nq; iq++)
8083 ctl->grid_nc_quant[iq] =
8084 (int) scan_ctl(filename, argc, argv, "GRID_NC_QUANT", iq, "0", NULL);
8085 ctl->grid_stddev =
8086 (int) scan_ctl(filename, argc, argv, "GRID_STDDEV", -1, "0", NULL);
8087 ctl->grid_z0 = scan_ctl(filename, argc, argv, "GRID_Z0", -1, "-5", NULL);
8088 ctl->grid_z1 = scan_ctl(filename, argc, argv, "GRID_Z1", -1, "85", NULL);
8089 ctl->grid_nz =
8090 (int) scan_ctl(filename, argc, argv, "GRID_NZ", -1, "1", NULL);
8091 ctl->grid_lon0 =
8092 scan_ctl(filename, argc, argv, "GRID_LON0", -1, "-180", NULL);
8093 ctl->grid_lon1 =
8094 scan_ctl(filename, argc, argv, "GRID_LON1", -1, "180", NULL);
8095 ctl->grid_nx =
8096 (int) scan_ctl(filename, argc, argv, "GRID_NX", -1, "360", NULL);
8097 ctl->grid_lat0 =
8098 scan_ctl(filename, argc, argv, "GRID_LAT0", -1, "-90", NULL);
8099 ctl->grid_lat1 =
8100 scan_ctl(filename, argc, argv, "GRID_LAT1", -1, "90", NULL);
8101 ctl->grid_ny =
8102 (int) scan_ctl(filename, argc, argv, "GRID_NY", -1, "180", NULL);
8103 ctl->grid_type =
8104 (int) scan_ctl(filename, argc, argv, "GRID_TYPE", -1, "0", NULL);
8105 if (ctl->grid_nx < 1 || ctl->grid_nx > EX
8106 || ctl->grid_ny < 1 || ctl->grid_ny > EY || ctl->grid_nz < 1)
8107 ERRMSG("Invalid output grid dimensions!");
8108 if (ctl->grid_lon0 >= ctl->grid_lon1
8109 || ctl->grid_lat0 >= ctl->grid_lat1 || ctl->grid_z0 >= ctl->grid_z1
8110 || ctl->grid_lat0 < -90 || ctl->grid_lat1 > 90)
8111 ERRMSG("Invalid output grid boundaries!");
8112 if (ctl->depo_basename[0] != '-'
8113 && (ctl->depo_dt_out <= 0 || ctl->depo_type < 0 || ctl->depo_type > 1))
8114 ERRMSG("Invalid radioactive deposition output settings!");
8115
8116 /* Output of profile data... */
8117 scan_ctl(filename, argc, argv, "PROF_BASENAME", -1, "-",
8118 ctl->prof_basename);
8119 scan_ctl(filename, argc, argv, "PROF_OBSFILE", -1, "-", ctl->prof_obsfile);
8120 ctl->prof_z0 = scan_ctl(filename, argc, argv, "PROF_Z0", -1, "0", NULL);
8121 ctl->prof_z1 = scan_ctl(filename, argc, argv, "PROF_Z1", -1, "60", NULL);
8122 ctl->prof_nz =
8123 (int) scan_ctl(filename, argc, argv, "PROF_NZ", -1, "60", NULL);
8124 ctl->prof_lon0 =
8125 scan_ctl(filename, argc, argv, "PROF_LON0", -1, "-180", NULL);
8126 ctl->prof_lon1 =
8127 scan_ctl(filename, argc, argv, "PROF_LON1", -1, "180", NULL);
8128 ctl->prof_nx =
8129 (int) scan_ctl(filename, argc, argv, "PROF_NX", -1, "360", NULL);
8130 ctl->prof_lat0 =
8131 scan_ctl(filename, argc, argv, "PROF_LAT0", -1, "-90", NULL);
8132 ctl->prof_lat1 =
8133 scan_ctl(filename, argc, argv, "PROF_LAT1", -1, "90", NULL);
8134 ctl->prof_ny =
8135 (int) scan_ctl(filename, argc, argv, "PROF_NY", -1, "180", NULL);
8136 if (ctl->prof_nx < 1 || ctl->prof_ny < 1 || ctl->prof_nz < 1
8137 || ctl->prof_lon0 >= ctl->prof_lon1
8138 || ctl->prof_lat0 >= ctl->prof_lat1 || ctl->prof_z0 >= ctl->prof_z1
8139 || ctl->prof_lat0 < -90 || ctl->prof_lat1 > 90)
8140 ERRMSG("Invalid profile grid!");
8141
8142 /* Output of sample data... */
8143 scan_ctl(filename, argc, argv, "SAMPLE_BASENAME", -1, "-",
8144 ctl->sample_basename);
8145 scan_ctl(filename, argc, argv, "SAMPLE_KERNEL", -1, "-",
8146 ctl->sample_kernel);
8147 scan_ctl(filename, argc, argv, "SAMPLE_OBSFILE", -1, "-",
8148 ctl->sample_obsfile);
8149 ctl->sample_dx =
8150 scan_ctl(filename, argc, argv, "SAMPLE_DX", -1, "50", NULL);
8151 ctl->sample_dz =
8152 scan_ctl(filename, argc, argv, "SAMPLE_DZ", -1, "-999", NULL);
8153
8154 /* Output of station data... */
8155 scan_ctl(filename, argc, argv, "STAT_BASENAME", -1, "-",
8156 ctl->stat_basename);
8157 ctl->stat_lon = scan_ctl(filename, argc, argv, "STAT_LON", -1, "0", NULL);
8158 ctl->stat_lat = scan_ctl(filename, argc, argv, "STAT_LAT", -1, "0", NULL);
8159 ctl->stat_r = scan_ctl(filename, argc, argv, "STAT_R", -1, "50", NULL);
8160 ctl->stat_t0 =
8161 scan_ctl(filename, argc, argv, "STAT_T0", -1, "-1e100", NULL);
8162 ctl->stat_t1 = scan_ctl(filename, argc, argv, "STAT_T1", -1, "1e100", NULL);
8163
8164 /* Output of VTK data... */
8165 scan_ctl(filename, argc, argv, "VTK_BASENAME", -1, "-", ctl->vtk_basename);
8166 ctl->vtk_dt_out =
8167 scan_ctl(filename, argc, argv, "VTK_DT_OUT", -1, "86400", NULL);
8168 if (ctl->vtk_basename[0] != '-' && ctl->vtk_dt_out > 0
8169 && !DT_ALIGNED(ctl->vtk_dt_out, ctl->dt_mod))
8170 WARN("VTK_DT_OUT is not an integer multiple of DT_MOD; output may be skipped!");
8171 ctl->vtk_stride =
8172 (int) scan_ctl(filename, argc, argv, "VTK_STRIDE", -1, "1", NULL);
8173 ctl->vtk_scale =
8174 scan_ctl(filename, argc, argv, "VTK_SCALE", -1, "1.0", NULL);
8175 ctl->vtk_offset =
8176 scan_ctl(filename, argc, argv, "VTK_OFFSET", -1, "0.0", NULL);
8177 ctl->vtk_sphere =
8178 (int) scan_ctl(filename, argc, argv, "VTK_SPHERE", -1, "0", NULL);
8179
8180 /* Domain decomposition... */
8181
8182 /* Only the trac tool sets this flag after reading the control parameters... */
8183 ctl->dd_supported = 0;
8184#ifdef DD
8185 /* Domain decomposition is only implemented by the trac tool and only works
8186 if the executable was started with one task per subdomain. Therefore, use
8187 it by default only if MPI has been initialized by the calling tool and if
8188 more than one task is running; otherwise, stay in serial mode. */
8189 int mpi_initialized = 0;
8190 int mpi_size = 1;
8191 MPI_Initialized(&mpi_initialized);
8192 if (mpi_initialized)
8193 MPI_Comm_size(MPI_COMM_WORLD, &mpi_size);
8194 ctl->dd =
8195 (int) scan_ctl(filename, argc, argv, "DD", -1,
8196 (mpi_initialized && mpi_size > 1) ? "1" : "0", NULL);
8197#else
8198 ctl->dd = (int) scan_ctl(filename, argc, argv, "DD", -1, "0", NULL);
8199#endif
8200
8202 (int) scan_ctl(filename, argc, argv, "DD_SUBDOMAINS_MERIDIONAL", -1,
8203 (ctl->dd == 1) ? "2" : "1", NULL);
8204 ctl->dd_subdomains_zonal =
8205 (int) scan_ctl(filename, argc, argv, "DD_SUBDOMAINS_ZONAL", -1,
8206 (ctl->dd == 1) ? "2" : "1", NULL);
8207 ctl->dd_halos_size =
8208 (int) scan_ctl(filename, argc, argv, "DD_HALOS_SIZE", -1, "1", NULL);
8209 ctl->dd_sort_dt =
8210 (double) scan_ctl(filename, argc, argv, "DD_SORT_DT", -1, "1800", NULL);
8211 ctl->dd_exchange_mode =
8212 (int) scan_ctl(filename, argc, argv, "DD_EXCHANGE_MODE", -1, "0", NULL);
8213 if (ctl->dd_exchange_mode < 0 || ctl->dd_exchange_mode > 1)
8214 ERRMSG("Set DD_EXCHANGE_MODE to 0 (sort/push) or 1 (no sort)!");
8215 if (ctl->met_nc_read_mode == 1 &&
8216 (!ctl->dd || ctl->met_type != 0 || ctl->met_convention != 0))
8217 ERRMSG("MET_NC_READ_MODE=1 requires MPI DD, NetCDF, and MET_CONVENTION=0!");
8218#ifndef DD
8219 if (ctl->met_nc_read_mode == 1)
8220 ERRMSG("MET_NC_READ_MODE=1 requires a DD build!");
8221#endif
8222
8223#ifdef DD
8224 /* Check the domain decomposition configuration... */
8225 if (ctl->dd) {
8226 if (!mpi_initialized)
8227 ERRMSG
8228 ("Domain decomposition requires to start the executable with one task per subdomain!");
8229 if (ctl->dd_halos_size < 0)
8230 ERRMSG("DD_HALOS_SIZE must be non-negative!");
8231 if (ctl->dd_subdomains_zonal < 1 || ctl->dd_subdomains_meridional < 1)
8232 ERRMSG("The number of subdomains has to be positive!");
8233 if (ctl->dd_subdomains_zonal * ctl->dd_subdomains_meridional == 1)
8234 ERRMSG("Please provide zonal and meridional subdomain numbers!");
8235 if (mpi_size != ctl->dd_subdomains_zonal * ctl->dd_subdomains_meridional)
8236 ERRMSG("Number of tasks and subdomains is not identical!");
8237 if (ctl->qnt_current_subdomain < 0 || ctl->qnt_target_subdomain < 0)
8238 ERRMSG
8239 ("Domain decomposition requires the quantities current_subdomain and target_subdomain!");
8240 if (ctl->met_coord_type != 0)
8241 ERRMSG("Domain decomposition requires a lat/lon meteorological grid!");
8242 if (ctl->radio_depo)
8243 ERRMSG
8244 ("Radioactive deposition is not supported with domain decomposition!");
8245 }
8246#endif
8247
8248 if (fabs(ctl->dt_mod - nearbyint(ctl->dt_mod)) >
8249 8 * DBL_EPSILON * ctl->dt_mod
8250 && ((meteo_sampled && ctl->met_dt_out >= ctl->dt_mod)
8251 || ((ctl->conv_cape >= 0 || ctl->conv_mix_pbl)
8252 && ctl->conv_dt > 0)
8253 || (ctl->mixing_tau_trop > 0 && ctl->mixing_dt > 0)
8254 || (ctl->kpp_chem && ctl->dt_kpp > 0)
8255 || (ctl->atm_basename[0] != '-' && ctl->atm_dt_out > 0)
8256 || (ctl->budget_basename[0] != '-' && ctl->budget_dt_out > 0)
8257 || (ctl->depo_basename[0] != '-' && ctl->depo_dt_out > 0)
8258 || (ctl->csi_basename[0] != '-' && ctl->csi_dt_out > 0)
8259 || (ctl->ens_basename[0] != '-' && ctl->ens_dt_out > 0)
8260 || (ctl->grid_basename[0] != '-' && ctl->grid_dt_out > 0)
8261 || (ctl->vtk_basename[0] != '-' && ctl->vtk_dt_out > 0)
8262 || ctl->sort_dt > 0
8263 || (ctl->dd && ctl->dd_exchange_mode == 0 && ctl->dd_sort_dt > 0)))
8264 WARN("Fractional DT_MOD may cause periodic events to be missed due to floating-point rounding in exact fmod comparisons!");
8265}
void level_definitions(ctl_t *ctl)
Defines pressure levels for meteorological data.
Definition mptrac.c:3648
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:13329
#define METVAR
Number of 3-D meteorological variables.
Definition mptrac.h:564
#define EY
Maximum number of latitudes for meteo data.
Definition mptrac.h:554
#define SET_QNT(qnt, name, longname, unit)
Set atmospheric quantity index.
Definition mptrac.h:2023
#define DT_ALIGNED(dt, dt_mod)
Definition mptrac.h:1127
double grid_z0
Lower altitude of gridded data [km].
Definition mptrac.h:3459
int qnt_o3
Quantity array index for ozone volume mixing ratio.
Definition mptrac.h:2626
double csi_lat1
Upper latitude of gridded CSI data [deg].
Definition mptrac.h:3420
char csi_obsfile[LEN]
Observation data file for CSI analysis.
Definition mptrac.h:3387
int met_nc_scale
Check netCDF scaling factors (0=no, 1=yes).
Definition mptrac.h:2896
int qnt_pel
Quantity array index for pressure at equilibrium level (EL).
Definition mptrac.h:2659
int csi_nz
Number of altitudes of gridded CSI data.
Definition mptrac.h:3396
int qnt_p
Quantity array index for pressure.
Definition mptrac.h:2605
int dd_halos_size
Domain decomposition halo size in grid points.
Definition mptrac.h:3589
char atm_gpfile[LEN]
Gnuplot file for atmospheric data.
Definition mptrac.h:3342
int qnt_swc
Quantity array index for cloud snow water content.
Definition mptrac.h:2638
double csi_obsmin
Minimum observation index to trigger detection.
Definition mptrac.h:3390
int qnt_pcb
Quantity array index for cloud bottom pressure.
Definition mptrac.h:2647
double csi_lon1
Upper longitude of gridded CSI data [deg].
Definition mptrac.h:3411
int qnt_u
Quantity array index for zonal wind.
Definition mptrac.h:2614
double stat_lon
Longitude of station [deg].
Definition mptrac.h:3537
double sort_dt
Time step for sorting of particle data [s].
Definition mptrac.h:3042
double stat_r
Search radius around station [km].
Definition mptrac.h:3543
int csi_ny
Number of latitudes of gridded CSI data.
Definition mptrac.h:3414
int vtk_sphere
Spherical projection for VTK data (0=no, 1=yes).
Definition mptrac.h:3567
double met_pbl_min
Minimum depth of planetary boundary layer [km].
Definition mptrac.h:3010
int qnt_iwc
Quantity array index for cloud ice water content.
Definition mptrac.h:2635
int qnt_pw
Quantity array index for partial water vapor pressure.
Definition mptrac.h:2713
char prof_basename[LEN]
Basename for profile output file.
Definition mptrac.h:3486
double grid_z1
Upper altitude of gridded data [km].
Definition mptrac.h:3462
int met_dp
Stride for pressure levels.
Definition mptrac.h:2962
double met_dt_out
Time step for sampling of meteo data along trajectories [s].
Definition mptrac.h:3029
int qnt_h2o2
Quantity array index for H2O2 volume mixing ratio (climatology).
Definition mptrac.h:2677
int qnt_vh
Quantity array index for horizontal wind.
Definition mptrac.h:2755
char species[LEN]
Species.
Definition mptrac.h:3146
int csi_nx
Number of longitudes of gridded CSI data.
Definition mptrac.h:3405
double csi_lat0
Lower latitude of gridded CSI data [deg].
Definition mptrac.h:3417
int met_pbl
Planetary boundary layer data (0=file, 1=z2p, 2=Richardson, 3=theta).
Definition mptrac.h:3007
int qnt_lwc
Quantity array index for cloud liquid water content.
Definition mptrac.h:2629
int grid_nc_level
zlib compression level of netCDF grid data files (0=off).
Definition mptrac.h:3447
int qnt_ts
Quantity array index for surface temperature.
Definition mptrac.h:2560
int qnt_plfc
Quantity array index for pressure at level of free convection (LCF).
Definition mptrac.h:2656
int qnt_o1d
Quantity array index for O(1D) volume mixing ratio (climatology).
Definition mptrac.h:2683
int met_tropo_spline
Tropopause interpolation method (0=linear, 1=spline).
Definition mptrac.h:3026
char sample_kernel[LEN]
Kernel data file for sample output.
Definition mptrac.h:3522
int qnt_tvirt
Quantity array index for virtual temperature.
Definition mptrac.h:2749
int met_geopot_sy
Latitudinal smoothing of geopotential heights.
Definition mptrac.h:2998
char grid_gpfile[LEN]
Gnuplot file for gridded data.
Definition mptrac.h:3438
int qnt_lsm
Quantity array index for land-sea mask.
Definition mptrac.h:2581
int qnt_theta
Quantity array index for potential temperature.
Definition mptrac.h:2725
double stat_t1
Stop time for station output [s].
Definition mptrac.h:3549
char csi_kernel[LEN]
Kernel data file for CSI output.
Definition mptrac.h:3381
int grid_type
Type of grid data files (0=ASCII, 1=netCDF).
Definition mptrac.h:3483
double csi_lon0
Lower longitude of gridded CSI data [deg].
Definition mptrac.h:3408
int qnt_pbl
Quantity array index for boundary layer pressure.
Definition mptrac.h:2587
int grid_stddev
Include standard deviations in grid output (0=no, 1=yes).
Definition mptrac.h:3453
int met_nc_read_mode
NetCDF DD input (0=local hyperslabs, 1=global 2-D slices via MPI).
Definition mptrac.h:2899
int qnt_psice
Quantity array index for saturation pressure over ice.
Definition mptrac.h:2710
int met_geopot_sx
Longitudinal smoothing of geopotential heights.
Definition mptrac.h:2995
int met_sy
Smoothing for latitudes.
Definition mptrac.h:2968
int qnt_ps
Quantity array index for surface pressure.
Definition mptrac.h:2557
char prof_obsfile[LEN]
Observation data file for profile output.
Definition mptrac.h:3489
int qnt_zs
Quantity array index for surface geopotential height.
Definition mptrac.h:2563
int prof_nz
Number of altitudes of gridded profile data.
Definition mptrac.h:3492
double csi_dt_out
Time step for CSI output [s].
Definition mptrac.h:3384
int met_cape
Convective available potential energy data (0=file, 1=calculate).
Definition mptrac.h:3004
double csi_modmin
Minimum column density to trigger detection [kg/m^2].
Definition mptrac.h:3393
int met_sx
Smoothing for longitudes.
Definition mptrac.h:2965
double depo_dt_out
DEPO_DT_OUT time interval for radioactive deposition output [s] (default: 86400).
Definition mptrac.h:3333
char grid_kernel[LEN]
Kernel data file for grid output.
Definition mptrac.h:3435
double prof_z0
Lower altitude of gridded profile data [km].
Definition mptrac.h:3495
int qnt_w
Quantity array index for vertical velocity.
Definition mptrac.h:2620
double met_tropo_pv
Dynamical tropopause potential vorticity threshold [PVU].
Definition mptrac.h:3020
int prof_nx
Number of longitudes of gridded profile data.
Definition mptrac.h:3501
int qnt_stat
Quantity array index for station flag.
Definition mptrac.h:2542
int dd_supported
Whether the executable implements the domain decomposition.
Definition mptrac.h:3600
int met_mpi_share
Use MPI to share meteo (0=no, 1=yes).
Definition mptrac.h:3035
int qnt_vz
Quantity array index for vertical velocity.
Definition mptrac.h:2758
int qnt_ho2
Quantity array index for HO2 volume mixing ratio (climatology).
Definition mptrac.h:2680
double csi_z1
Upper altitude of gridded CSI data [km].
Definition mptrac.h:3402
double stat_t0
Start time for station output [s].
Definition mptrac.h:3546
int atm_type_out
Type of atmospheric data files for output (-1=same as ATM_TYPE, 0=ASCII, 1=binary,...
Definition mptrac.h:3360
int met_cms_nd0x
cmultiscale number of cells of coarsest grid in x-direction.
Definition mptrac.h:2944
int met_nlev
Number of meteo data model levels.
Definition mptrac.h:2986
double dt_kpp
Time step for KPP chemistry [s].
Definition mptrac.h:3272
char csi_basename[LEN]
Basename of CSI data files.
Definition mptrac.h:3378
int qnt_shf
Quantity array index for surface sensible heat flux.
Definition mptrac.h:2578
int qnt_vs
Quantity array index for surface meridional wind.
Definition mptrac.h:2569
double vtk_dt_out
Time step for VTK data output [s].
Definition mptrac.h:3555
double conv_dt
Time interval for convection module [s].
Definition mptrac.h:3107
char sample_obsfile[LEN]
Observation data file for sample output.
Definition mptrac.h:3525
int qnt_hno3
Quantity array index for HNO3 volume mixing ratio (climatology).
Definition mptrac.h:2671
char grid_basename[LEN]
Basename of grid data files.
Definition mptrac.h:3432
char met_comp_logfile[LEN]
Filename for per-level compression diagnostics ("-" disables output).
Definition mptrac.h:2935
int qnt_h2ot
Quantity array index for tropopause water vapor volume mixing ratio.
Definition mptrac.h:2599
int qnt_rh
Quantity array index for relative humidity over water.
Definition mptrac.h:2719
int met_gp2z
Convert surface geopotential to geopotential height (0=no, 1=yes).
Definition mptrac.h:2886
double met_pbl_max
Maximum depth of planetary boundary layer [km].
Definition mptrac.h:3013
int met_dx
Stride for longitudes.
Definition mptrac.h:2956
int met_convention
Meteo data layout (0=[lev, lat, lon], 1=[lon, lat, lev]).
Definition mptrac.h:2870
char depo_basename[LEN]
DEPO_BASENAME for radioactive deposition files (default: disabled with "-").
Definition mptrac.h:3330
int qnt_zeta_d
Quantity array index for diagnosed zeta vertical coordinate.
Definition mptrac.h:2734
int tracer_chem
Switch for first order tracer chemistry module (0=off, 1=on).
Definition mptrac.h:3275
int diffusion
Diffusion switch (0=off, 1=on).
Definition mptrac.h:3062
double budget_dt_out
Time step for mass budget output [s].
Definition mptrac.h:3375
int qnt_zg
Quantity array index for geopotential height.
Definition mptrac.h:2602
double vtk_offset
Vertical offset for VTK data [km].
Definition mptrac.h:3564
int qnt_v
Quantity array index for meridional wind.
Definition mptrac.h:2617
double met_zfp_tol[METVAR]
ZFP compression tolerance.
Definition mptrac.h:2923
int qnt_oh
Quantity array index for OH volume mixing ratio (climatology).
Definition mptrac.h:2674
int met_sz3_prec[METVAR]
SZ3 compression precision.
Definition mptrac.h:2926
int qnt_h2o
Quantity array index for water vapor volume mixing ratio.
Definition mptrac.h:2623
int prof_ny
Number of latitudes of gridded profile data.
Definition mptrac.h:3510
int qnt_rhice
Quantity array index for relative humidity over ice.
Definition mptrac.h:2722
int qnt_rho
Quantity array index for density of air.
Definition mptrac.h:2611
double sample_dz
Layer depth for sample output [km].
Definition mptrac.h:3531
int obs_type
Type of observation data files (0=ASCII, 1=netCDF).
Definition mptrac.h:3369
int grid_nc_quant[NQ]
Number of digits for quantization of netCDF grid data files (0=off).
Definition mptrac.h:3450
int qnt_us
Quantity array index for surface zonal wind.
Definition mptrac.h:2566
int depo_type
DEPO_TYPE of deposition files (0=ASCII, 1=netCDF, default: 0).
Definition mptrac.h:3336
char budget_basename[LEN]
Basename of mass budget data file.
Definition mptrac.h:3372
char atm_basename[LEN]
Basename of atmospheric data files.
Definition mptrac.h:3339
int qnt_pt
Quantity array index for tropopause pressure.
Definition mptrac.h:2590
int qnt_cl
Quantity array index for total column cloud water.
Definition mptrac.h:2650
double prof_z1
Upper altitude of gridded profile data [km].
Definition mptrac.h:3498
double met_lev_hyam[EP]
Meteo data model level a coefficients.
Definition mptrac.h:2989
int qnt_t
Quantity array index for temperature.
Definition mptrac.h:2608
int atm_filter
Time filter for atmospheric data output (0=none, 1=missval, 2=remove).
Definition mptrac.h:3348
int kpp_chem
Switch for KPP chemistry module (0=off, 1=on).
Definition mptrac.h:3269
int met_lz4_accel
LZ4 acceleration factor (>=1, default=8).
Definition mptrac.h:2914
char ens_basename[LEN]
Basename of ensemble data file.
Definition mptrac.h:3426
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:2883
double csi_z0
Lower altitude of gridded CSI data [km].
Definition mptrac.h:3399
int qnt_lapse
Quantity array index for lapse rate.
Definition mptrac.h:2752
double stat_lat
Latitude of station [deg].
Definition mptrac.h:3540
double met_detrend
FWHM of horizontal Gaussian used for detrending [km].
Definition mptrac.h:2974
double met_cms_eps[METVAR]
cmultiscale compression epsilon.
Definition mptrac.h:2953
int qnt_cape
Quantity array index for convective available potential energy (CAPE).
Definition mptrac.h:2662
int met_cms_nd0y
cmultiscale number of cells of coarsest grid in y-direction.
Definition mptrac.h:2947
int qnt_o3c
Quantity array index for total column ozone.
Definition mptrac.h:2668
int grid_nz
Number of altitudes of gridded data.
Definition mptrac.h:3456
int qnt_nss
Quantity array index for northward turbulent surface stress.
Definition mptrac.h:2575
double ens_dt_out
Time step for ensemble output [s].
Definition mptrac.h:3429
char sample_basename[LEN]
Basename of sample data file.
Definition mptrac.h:3519
int atm_stride
Particle index stride for atmospheric data files.
Definition mptrac.h:3351
int met_relhum
Try to read relative humidity (0=no, 1=yes).
Definition mptrac.h:3001
double atm_dt_out
Time step for atmospheric data output [s].
Definition mptrac.h:3345
int met_lossy_scale[METVAR]
Apply levelwise [0,1] scaling before lossy compression (0=off, 1=on).
Definition mptrac.h:2932
double prof_lat1
Upper latitude of gridded profile data [deg].
Definition mptrac.h:3516
int met_cms_batch
cmultiscale batch size.
Definition mptrac.h:2938
double psc_h2o
H2O volume mixing ratio for PSC analysis.
Definition mptrac.h:3320
int met_sp
Smoothing for pressure levels.
Definition mptrac.h:2971
double prof_lon0
Lower longitude of gridded profile data [deg].
Definition mptrac.h:3504
int qnt_pct
Quantity array index for cloud top pressure.
Definition mptrac.h:2644
int qnt_mloss_kpp
Quantity array index for total mass loss due to KPP chemistry.
Definition mptrac.h:2692
int qnt_psat
Quantity array index for saturation pressure over water.
Definition mptrac.h:2707
double met_lev_hybm[EP]
Meteo data model level b coefficients.
Definition mptrac.h:2992
double prof_lat0
Lower latitude of gridded profile data [deg].
Definition mptrac.h:3513
int qnt_cin
Quantity array index for convective inhibition (CIN).
Definition mptrac.h:2665
double turb_pbl_trans
Depth of turbulent PBL transition layer (fraction of PBL pressure thickness).
Definition mptrac.h:3092
double psc_hno3
HNO3 volume mixing ratio for PSC analysis.
Definition mptrac.h:3323
double prof_lon1
Upper longitude of gridded profile data [deg].
Definition mptrac.h:3507
int met_nc_quant
Number of digits for quantization of netCDF meteo files (0=off).
Definition mptrac.h:2905
int h2o2_chem_reaction
Reaction type for H2O2 chemistry (0=none, 1=SO2).
Definition mptrac.h:3266
int atm_nc_quant[NQ]
Number of digits for quantization of netCDF atmospheric data files (0=off).
Definition mptrac.h:3366
int met_cms_zstd
cmultiscale ZSTD compression (0=off, 1=on).
Definition mptrac.h:2941
int met_cms_maxlev
cmultiscale maximum refinement level.
Definition mptrac.h:2950
int grid_sparse
Sparse output in grid data files (0=no, 1=yes).
Definition mptrac.h:3444
double met_sz3_tol[METVAR]
SZ3 compression tolerance.
Definition mptrac.h:2929
char vtk_basename[LEN]
Basename of VTK data files.
Definition mptrac.h:3552
int qnt_tt
Quantity array index for tropopause temperature.
Definition mptrac.h:2593
int met_nc_level
zlib compression level of netCDF meteo files (0=off).
Definition mptrac.h:2902
double vtk_scale
Vertical scaling factor for VTK data.
Definition mptrac.h:3561
int qnt_pv
Quantity array index for potential vorticity.
Definition mptrac.h:2761
int qnt_sst
Quantity array index for sea surface temperature.
Definition mptrac.h:2584
int atm_nc_level
zlib compression level of netCDF atmospheric data files (0=off).
Definition mptrac.h:3363
int qnt_sh
Quantity array index for specific humidity.
Definition mptrac.h:2716
int qnt_ess
Quantity array index for eastward turbulent surface stress.
Definition mptrac.h:2572
int met_dy
Stride for latitudes.
Definition mptrac.h:2959
int radio_depo
RADIO_DEPO switch for radionuclide deposition (0=off, 1=on, default: 0).
Definition mptrac.h:3281
int qnt_idx
Quantity array index for air parcel IDs.
Definition mptrac.h:2536
double met_tropo_theta
Dynamical tropopause potential temperature threshold [K].
Definition mptrac.h:3023
int qnt_rwc
Quantity array index for cloud rain water content.
Definition mptrac.h:2632
char qnt_longname[NQ][LEN]
Quantity long names.
Definition mptrac.h:2527
int met_zfp_prec[METVAR]
ZFP compression precision.
Definition mptrac.h:2920
double sample_dx
Horizontal radius for sample output [km].
Definition mptrac.h:3528
int vtk_stride
Particle index stride for VTK data.
Definition mptrac.h:3558
char stat_basename[LEN]
Basename of station data file.
Definition mptrac.h:3534
int qnt_zt
Quantity array index for tropopause geopotential height.
Definition mptrac.h:2596
int qnt_cc
Quantity array index for cloud cover.
Definition mptrac.h:2641
int qnt_plcl
Quantity array index for pressure at lifted condensation level (LCL).
Definition mptrac.h:2653
double grid_dt_out
Time step for gridded data output [s].
Definition mptrac.h:3441
int qnt_tdew
Quantity array index for dew point temperature.
Definition mptrac.h:2764
Here is the call graph for this function:

◆ mptrac_read_met()

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.

This function reads meteorological data from a file specified by the filename parameter. It supports both NetCDF and binary formats based on the met_type field in the ctl_t structure. The function can also handle parallel processing with MPI, broadcasting the data across ranks if required by the configuration.

Parameters
filenameA constant character pointer representing the name of the file to read the meteorological data from.
ctlA pointer to a ctl_t structure, which holds control parameters including the type of meteorological data, MPI sharing flags, and configuration details.
climA pointer to a clim_t structure, which contains climatological data to be used in the process, if applicable.
metA pointer to a met_t structure that will store the meteorological data read from the file.
ddA pointer to an dd_t structure containing MPI information, including rank and neighbours.
Returns
Returns an integer, where 1 indicates success.
Note
  • The function logs the action of reading meteorological data, including the file name.
  • It supports MPI parallelization and will share the data across multiple processes if the met_mpi_share flag is set in the control structure.
  • If ctl->met_type is 0, the data is read from a NetCDF file using the read_met_nc function.
  • If ctl->met_type is between 1 and 5, or equals 7 or 8, the data is read from a binary file using the read_met_bin function.
  • If ctl->met_type is 6, the data is read from grib files using the read_met_grib function.
  • If the met_type is not recognized, an error message is generated.
Author
Lars Hoffmann

Definition at line 8269 of file mptrac.c.

8274 {
8275
8276 /* Write info... */
8277 LOG(1, "Read meteo data: %s", filename);
8278
8279#ifdef DD
8280 /* With domain decomposition, each task reads the meteo data of its own
8281 subdomain only, which is not what the tools operating on the full meteo
8282 grid expect... */
8283 if (ctl->dd && !ctl->dd_supported)
8284 ERRMSG("The domain decomposition is only implemented by the trac tool!");
8285#endif
8286
8287 /* Set rank... */
8288 int rank = 0;
8289
8290 /* Check whether the meteo data is shared between the tasks... */
8291 int share = ctl->met_mpi_share;
8292#ifdef MPI
8293 /* Sharing the meteo data between the tasks requires to start the executable
8294 with a parallel launcher. If MPI has not been initialized, the meteo data
8295 is read by the single task itself... */
8296 int mpi_initialized = 0;
8297 MPI_Initialized(&mpi_initialized);
8298 if (share && !mpi_initialized) {
8299 WARN("Cannot share meteo data because MPI has not been initialized!");
8300 share = 0;
8301 }
8302 if (share)
8303 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
8304#endif
8305
8306 /* Check rank... */
8307 if (!share || rank == 0) {
8308
8309 /* Read netCDF data... */
8310 if (ctl->met_type == 0) {
8311 if (read_met_nc(filename, ctl, met, dd) != 1)
8312 return 0;
8313 }
8314
8315 /* Read binary data... */
8316 else if ((ctl->met_type >= 1 && ctl->met_type <= 5)
8317 || ctl->met_type == 7 || ctl->met_type == 8) {
8318 if (read_met_bin(filename, ctl, met) != 1)
8319 return 0;
8320 }
8321#ifdef ECCODES
8322 /* Read grib data... */
8323 else if (ctl->met_type == 6) {
8324 if (read_met_grib(filename, ctl, met) != 1)
8325 return 0;
8326 }
8327#endif
8328
8329 /* Not implemented... */
8330 else
8331 ERRMSG("MET_TYPE not implemented!");
8332
8333 /* Check geographic longitude coordinates... */
8334 if (met->coord_type == 0)
8335 for (int ix = 1; ix < met->nx; ix++)
8336 if (!(met->lon[ix] > met->lon[ix - 1]))
8337 ERRMSG("Geographic longitudes must be strictly increasing!");
8338
8339 /* Check active latitude and pressure coordinates... */
8340 for (int iy = 0; iy < met->ny; iy++) {
8341 if (!isfinite(met->lat[iy]))
8342 ERRMSG("Meteo latitudes must be finite!");
8343 if (met->coord_type == 0 && fabs(met->lat[iy]) > 90.)
8344 ERRMSG("Geographic latitudes must be within [-90, 90] degrees!");
8345 }
8346 if (met->np < 2)
8347 ERRMSG("At least two pressure levels are required!");
8348 for (int ip = 0; ip < met->np; ip++) {
8349 if (!isfinite(met->p[ip]))
8350 ERRMSG("Meteo pressure levels must be finite!");
8351 if (ip > 0 && !(met->p[ip - 1] > met->p[ip]))
8352 ERRMSG("Meteo pressure levels must be strictly descending!");
8353 }
8354
8355 /* Preprocessing for netCDF and grib files... */
8356 if (ctl->met_type == 0 || ctl->met_type == 6) {
8357
8358 /* Extrapolate data for lower boundary... */
8360
8361 /* Fix polar winds... */
8363
8364 /* Create periodic boundary conditions... */
8365#ifdef DD
8366 /* Zonal DD halos provide periodic boundaries only when there is more
8367 than one zonal subdomain. With a single zonal subdomain, append the
8368 periodic endpoint locally as in a run without DD. */
8369 if (!ctl->dd || ctl->dd_subdomains_zonal == 1)
8370 read_met_periodic(met);
8371#else
8372 read_met_periodic(met);
8373#endif
8374
8375 /* Downsampling... */
8376 read_met_sample(ctl, met);
8377
8378 /* Calculate geopotential heights... */
8379 read_met_geopot(ctl, met);
8380
8381 /* Calculate potential vorticity... */
8382 read_met_pv(met);
8383
8384 /* Calculate boundary layer data... */
8385 read_met_pbl(ctl, met);
8386
8387 /* Calculate tropopause data... */
8388 read_met_tropo(ctl, clim, met);
8389
8390 /* Calculate cloud properties... */
8391 read_met_cloud(met);
8392
8393 /* Calculate convective available potential energy... */
8394 read_met_cape(ctl, clim, met);
8395
8396 /* Calculate total column ozone... */
8397 read_met_ozone(met);
8398
8399 /* Detrending... */
8400 read_met_detrend(ctl, met);
8401
8402 /* Check meteo data and smooth zeta profiles ... */
8403 read_met_monotonize(ctl, met);
8404 }
8405 }
8406
8407 /* Broadcast data via MPI... */
8408#ifdef MPI
8409 if (share) {
8410
8411 /* Set timer... */
8412 SELECT_TIMER("READ_MET_MPI_BCAST", "COMM");
8413 LOG(2, "Broadcast data on rank %d...", rank);
8414
8415 /* Broadcast... */
8416 broadcast_large_data(met, sizeof(met_t));
8417 }
8418#endif
8419
8420 /* Return success... */
8421 return 1;
8422}
void read_met_geopot(const ctl_t *ctl, met_t *met)
Calculates geopotential heights from meteorological data.
Definition mptrac.c:10102
void read_met_extrapolate(met_t *met)
Extrapolates meteorological data.
Definition mptrac.c:10062
void read_met_sample(const ctl_t *ctl, met_t *met)
Downsamples meteorological data based on specified parameters.
Definition mptrac.c:12881
void read_met_cloud(met_t *met)
Calculates cloud-related variables for each grid point.
Definition mptrac.c:9898
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:12472
void read_met_detrend(const ctl_t *ctl, met_t *met)
Detrends meteorological data.
Definition mptrac.c:9955
void read_met_monotonize(const ctl_t *ctl, met_t *met)
Makes zeta and pressure profiles monotone.
Definition mptrac.c:12135
void read_met_periodic(met_t *met)
Applies periodic boundary conditions to meteorological data along longitudinal axis.
Definition mptrac.c:12609
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:12220
void read_met_ozone(met_t *met)
Calculates the total column ozone from meteorological ozone data.
Definition mptrac.c:12852
void broadcast_large_data(void *data, size_t N)
State variables of cuRAND random number generator.
Definition mptrac.c:47
void read_met_pv(met_t *met)
Calculates potential vorticity (PV) from meteorological data.
Definition mptrac.c:12732
int read_met_bin(const char *filename, const ctl_t *ctl, met_t *met)
Reads meteorological data from a binary file.
Definition mptrac.c:9479
void read_met_polar_winds(met_t *met)
Applies a fix for polar winds in meteorological data.
Definition mptrac.c:12670
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:9778
int read_met_grib(const char *filename, const ctl_t *ctl, met_t *met)
Reads meteorological data from a grib file and processes it.
Here is the call graph for this function:

◆ mptrac_run_timestep()

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.

This function performs all operations required to advance the model simulation by one timestep. It includes updating air parcel positions, applying advection, diffusion, convection, and other processes such as sedimentation, chemistry, and deposition. Each process is conditionally executed based on the control settings provided in the ctl structure.

Parameters
ctlPointer to the control structure containing model parameters and settings.
cachePointer to the cache structure used for intermediate calculations.
climPointer to the climatology structure containing climatological data.
met0Pointer to the current meteorological data structure.
met1Pointer to the next meteorological data structure.
atmPointer to the atmosphere structure containing air parcel data.
depoPointer to cumulative radionuclide ground inventories.
tCurrent simulation time in seconds.
ddMPI information required for the domain decomposition.
Author
Lars Hoffmann

Definition at line 8426 of file mptrac.c.

8435 {
8436
8437 /* Initialize modules... */
8438 if (t == ctl->t_start) {
8439
8440 /* Initialize isosurface data... */
8441 if (ctl->isosurf >= 1 && ctl->isosurf <= 4)
8442 module_isosurf_init(ctl, cache, *met0, *met1, atm);
8443
8444 /* Initialize advection... */
8445 module_advect_init(ctl, cache, *met0, *met1, atm);
8446
8447 /* Initialize chemistry... */
8448 module_chem_init(ctl, cache, clim, *met0, *met1, atm);
8449 }
8450
8451 /* Set time steps of air parcels... */
8452 module_timesteps(ctl, cache, *met0, atm, t);
8453
8454 /* Sort particles... */
8455 if (ctl->sort_dt > 0 && fmod(t, ctl->sort_dt) == 0)
8456 module_sort(ctl, *met0, cache, atm);
8457
8458 /* Check positions (initial)... */
8459 module_position(cache, *met0, *met1, atm, 0);
8460
8461 /* Advection... */
8462 if (ctl->advect > 0)
8463 module_advect(ctl, cache, *met0, *met1, atm);
8464
8465 /* Turbulent diffusion... */
8466 if (ctl->diffusion
8467 && (ctl->turb_dx_pbl > 0 || ctl->turb_dz_pbl > 0
8468 || ctl->turb_dx_trop > 0 || ctl->turb_dz_trop > 0
8469 || ctl->turb_dx_strat > 0 || ctl->turb_dz_strat > 0))
8470 module_diff_turb(ctl, cache, clim, *met0, *met1, atm);
8471
8472 /* Optional PBL-specific diffusion scheme... */
8473 if (ctl->diffusion && ctl->turb_pbl_scheme == 1)
8474 module_diff_pbl(ctl, cache, *met0, *met1, atm);
8475
8476 /* Mesoscale diffusion... */
8477 if (ctl->diffusion && (ctl->turb_mesox > 0 || ctl->turb_mesoz > 0))
8478 module_diff_meso(ctl, cache, *met0, *met1, atm);
8479
8480 /* Convection... */
8481 if ((ctl->conv_mix_pbl || ctl->conv_cape >= 0)
8482 && (ctl->conv_dt <= 0 || fmod(t, ctl->conv_dt) == 0))
8483 module_convection(ctl, cache, *met0, *met1, atm);
8484
8485 /* Sedimentation... */
8486 if (ctl->qnt_rp >= 0 && ctl->qnt_rhop >= 0)
8487 module_sedi(ctl, cache, *met0, *met1, atm);
8488
8489 /* Isosurface... */
8490 if (ctl->isosurf >= 1 && ctl->isosurf <= 4)
8491 module_isosurf(ctl, cache, *met0, *met1, atm);
8492
8493 /* Check positions (final)... */
8494 module_position(cache, *met0, *met1, atm, 1);
8495
8496 /* Domain decomposition... */
8497#ifdef DD
8498 if (ctl->dd)
8499 module_dd(t, ctl, cache, dd, atm, met0);
8500#else
8501 (void) dd;
8502#endif
8503
8504 /* Interpolate meteo data... */
8505 if (ctl->met_dt_out > 0
8506 && (ctl->met_dt_out < ctl->dt_mod || fmod(t, ctl->met_dt_out) == 0))
8507 module_meteo(ctl, cache, clim, *met0, *met1, atm);
8508
8509 /* Check boundary conditions (initial)... */
8510 if ((ctl->bound_lat0 < ctl->bound_lat1)
8511 && (ctl->bound_p0 > ctl->bound_p1))
8512 module_bound_cond(ctl, cache, clim, *met0, *met1, atm);
8513
8514 /* Initialize quantity of total loss rate... */
8515 if (ctl->qnt_loss_rate >= 0) {
8516 PARTICLE_LOOP(0, atm->np, 1, "acc data present(ctl,atm)") {
8517 atm->q[ctl->qnt_loss_rate][ip] = 0;
8518 }
8519 }
8520
8521 /* Decay of particle mass... */
8522 if (ctl->tdec_trop > 0 && ctl->tdec_strat > 0)
8523 module_decay(ctl, cache, clim, atm);
8524
8525 /* Interparcel mixing... */
8526 if (ctl->mixing_tau_trop > 0 && ctl->mixing_tau_strat > 0
8527 && (ctl->mixing_dt <= 0 || fmod(t, ctl->mixing_dt) == 0))
8528 module_mixing(ctl, cache, clim, atm, t);
8529
8530 /* Calculate the tracer vmr in the chemistry grid... */
8531 if (ctl->oh_chem_reaction != 0 || ctl->h2o2_chem_reaction != 0
8532 || (ctl->kpp_chem && fmod(t, ctl->dt_kpp) == 0))
8533 module_chem_grid(ctl, *met0, *met1, atm, t);
8534
8535 /* OH chemistry... */
8536 if (ctl->oh_chem_reaction != 0)
8537 module_oh_chem(ctl, cache, clim, *met0, *met1, atm);
8538
8539 /* H2O2 chemistry (for SO2 aqueous phase oxidation)... */
8540 if (ctl->h2o2_chem_reaction != 0)
8541 module_h2o2_chem(ctl, cache, clim, *met0, *met1, atm);
8542
8543 /* First-order tracer chemistry... */
8544 if (ctl->tracer_chem)
8545 module_tracer_chem(ctl, cache, clim, *met0, *met1, atm);
8546
8547 /* Radioactive decay... */
8548 if (ctl->radio_decay)
8549 module_radio_decay(ctl, cache, atm);
8550
8551 /* Radioactive deposition... */
8552 if (ctl->radio_depo)
8553 module_radio_depo(ctl, cache, *met0, *met1, atm, depo);
8554
8555 /* KPP chemistry... */
8556 if (ctl->kpp_chem && fmod(t, ctl->dt_kpp) == 0) {
8557#ifdef KPP
8558 module_kpp_chem(ctl, cache, clim, *met0, *met1, atm);
8559#else
8560 ERRMSG("Code was compiled without KPP!");
8561#endif
8562 }
8563
8564 /* Wet deposition... */
8565 if ((ctl->wet_depo_ic_a > 0 || ctl->wet_depo_ic_h[0] > 0)
8566 && (ctl->wet_depo_bc_a > 0 || ctl->wet_depo_bc_h[0] > 0))
8567 module_wet_depo(ctl, cache, *met0, *met1, atm);
8568
8569 /* Dry deposition... */
8570 if (ctl->dry_depo_vdep > 0)
8571 module_dry_depo(ctl, cache, *met0, *met1, atm);
8572
8573 /* Check boundary conditions (final)... */
8574 if ((ctl->bound_lat0 < ctl->bound_lat1)
8575 && (ctl->bound_p0 > ctl->bound_p1))
8576 module_bound_cond(ctl, cache, clim, *met0, *met1, atm);
8577}
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:3904
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:5376
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:4541
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:4363
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:5200
void module_mixing(const ctl_t *ctl, const cache_t *cache, const clim_t *clim, atm_t *atm, const double t)
Update atmospheric properties through interparcel mixing.
Definition mptrac.c:5502
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:6483
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:4183
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:6220
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:4407
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:4087
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:4060
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:5789
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:5848
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:4580
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:6248
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:4902
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:6325
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:6405
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:5115
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:4657
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:5270
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:5705
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:4482
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:5052
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:5906
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.
Here is the call graph for this function:

◆ mptrac_update_device()

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.

This function updates the GPU memory with the data from the provided host data structures (ctl, cache, clim, atm) using OpenACC directives. It ensures that the host data is transferred to the device for further computation.

Parameters
[in]ctlPointer to the ctl_t structure. If not NULL, the corresponding device memory for ctl is updated.
[in]cachePointer to the cache_t structure. If not NULL, the corresponding device memory for cache is updated.
[in]climPointer to the clim_t structure. If not NULL, the corresponding device memory for clim is updated.
[in]met0Pointer to the first met_t structure. If not NULL, the corresponding device memory for met0 is updated.
[in]met1Pointer to the second met_t structure. If not NULL, the corresponding device memory for met1 is updated.
[in]atmPointer to the atm_t structure. If not NULL, the corresponding device memory for atm is updated.
Note
The function assumes that OpenACC is enabled and uses the #pragma acc update directive for device memory synchronization. Each update operation is wrapped with a timer labeled as "UPDATE_DEVICE" for performance tracking.
Warning
Ensure that the pointers passed to this function are valid and properly initialized before calling this function. Passing invalid or uninitialized pointers may lead to undefined behavior.
Author
Lars Hoffmann

Definition at line 8581 of file mptrac.c.

8587 {
8588
8589 /* Update GPU... */
8590 if (ctl != NULL) {
8591#ifdef _OPENACC
8592 SELECT_TIMER("UPDATE_DEVICE", "MEMORY");
8593#pragma acc update device(ctl[:1])
8594#endif
8595 }
8596
8597 if (cache != NULL) {
8598#ifdef _OPENACC
8599 SELECT_TIMER("UPDATE_DEVICE", "MEMORY");
8600#pragma acc update device(cache[:1])
8601#endif
8602 }
8603
8604 if (clim != NULL) {
8605#ifdef _OPENACC
8606 SELECT_TIMER("UPDATE_DEVICE", "MEMORY");
8607#pragma acc update device(clim[:1])
8608#endif
8609 }
8610
8611 if (met0 != NULL) {
8612#ifdef _OPENACC
8613 SELECT_TIMER("UPDATE_DEVICE", "MEMORY");
8614 met_t *met0up = *met0;
8615#pragma acc update device(met0up[:1])
8616#endif
8617 }
8618
8619 if (met1 != NULL) {
8620#ifdef _OPENACC
8621 SELECT_TIMER("UPDATE_DEVICE", "MEMORY");
8622 met_t *met1up = *met1;
8623#pragma acc update device(met1up[:1])
8624#endif
8625 }
8626
8627 if (atm != NULL) {
8628#ifdef _OPENACC
8629 SELECT_TIMER("UPDATE_DEVICE", "MEMORY");
8630#pragma acc update device(atm[:1])
8631#endif
8632 }
8633}

◆ mptrac_update_host()

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.

This function transfers data from the device (GPU) memory back to the host memory for the provided data structures (ctl, cache, clim, atm) using OpenACC directives. It ensures that the latest data from the device is synchronized with the host.

Parameters
[in]ctlPointer to the ctl_t structure. If not NULL, the corresponding host memory for ctl is updated from the device.
[in]cachePointer to the cache_t structure. If not NULL, the corresponding host memory for cache is updated from the device.
[in]climPointer to the clim_t structure. If not NULL, the corresponding host memory for clim is updated from the device.
[in]met0Pointer to the first met_t structure. If not NULL, the corresponding host memory for met0 is updated.
[in]met1Pointer to the second met_t structure. If not NULL, the corresponding host memory for met1 is updated.
[in]atmPointer to the atm_t structure. If not NULL, the corresponding host memory for atm is updated from the device.
Note
The function assumes that OpenACC is enabled and uses the #pragma acc update directive for host memory synchronization. Each update operation is wrapped with a timer labeled as "UPDATE_HOST" for performance tracking.
Warning
Ensure that the pointers passed to this function are valid and properly initialized before calling this function
Author
Lars Hoffmann

Definition at line 8637 of file mptrac.c.

8643 {
8644
8645 /* Update GPU... */
8646 if (ctl != NULL) {
8647#ifdef _OPENACC
8648 SELECT_TIMER("UPDATE_HOST", "MEMORY");
8649#pragma acc update host(ctl[:1])
8650#endif
8651 }
8652
8653 if (cache != NULL) {
8654#ifdef _OPENACC
8655 SELECT_TIMER("UPDATE_HOST", "MEMORY");
8656#pragma acc update host(cache[:1])
8657#endif
8658 }
8659
8660 if (clim != NULL) {
8661#ifdef _OPENACC
8662 SELECT_TIMER("UPDATE_HOST", "MEMORY");
8663#pragma acc update host(clim[:1])
8664#endif
8665 }
8666
8667 if (met0 != NULL) {
8668#ifdef _OPENACC
8669 SELECT_TIMER("UPDATE_HOST", "MEMORY");
8670 met_t *met0up = *met0;
8671#pragma acc update host(met0up[:1])
8672#endif
8673 }
8674
8675 if (met1 != NULL) {
8676#ifdef _OPENACC
8677 SELECT_TIMER("UPDATE_HOST", "MEMORY");
8678 met_t *met1up = *met1;
8679#pragma acc update host(met1up[:1])
8680#endif
8681 }
8682
8683 if (atm != NULL) {
8684#ifdef _OPENACC
8685 SELECT_TIMER("UPDATE_HOST", "MEMORY");
8686#pragma acc update host(atm[:1])
8687#endif
8688 }
8689}

◆ mptrac_write_atm()

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.

The mptrac_write_atm function writes the air parcel data stored in the atm structure to a file specified by filename. The format of the output file is determined by the atm_type_out field in the ctl control structure.

Parameters
filenameA string representing the name of the file to write the data to.
ctlA pointer to a ctl_t structure containing control parameters.
atmA pointer to an atm_t structure containing atmospheric data.
tThe current time, used for certain output formats.

The function performs the following steps:

  • Sets a timer for the write operation using the SELECT_TIMER macro.
  • Logs the beginning of the write operation with the specified filename.
  • Depending on the atm_type_out value in the ctl structure, writes the data in one of the following formats:
    • ASCII (atm_type_out == 0): Calls write_atm_asc.
    • Binary (atm_type_out == 1): Calls write_atm_bin.
    • netCDF (atm_type_out == 2): Calls write_atm_nc.
    • CLaMS trajectory data (atm_type_out == 3): Calls write_atm_clams_traj.
    • CLaMS position data (atm_type_out == 4): Calls write_atm_clams.
  • If the atm_type_out value is not supported, triggers an error message.
  • Logs various statistics about the atmospheric data, including the number of particles, time range, altitude range, pressure range, longitude range, and latitude range.
  • Logs the range for each quantity specified in the ctl structure.
Author
Lars Hoffmann

Definition at line 8693 of file mptrac.c.

8697 {
8698
8699 /* Set timer... */
8700 SELECT_TIMER("WRITE_ATM", "OUTPUT");
8701
8702 /* Write info... */
8703 LOG(1, "Write atmospheric data: %s", filename);
8704
8705 /* Write ASCII data... */
8706 if (ctl->atm_type_out == 0)
8707 write_atm_asc(filename, ctl, atm, t);
8708
8709 /* Write binary data... */
8710 else if (ctl->atm_type_out == 1)
8711 write_atm_bin(filename, ctl, atm);
8712
8713 /* Write netCDF data... */
8714 else if (ctl->atm_type_out == 2)
8715 write_atm_nc(filename, ctl, atm);
8716
8717 /* Write CLaMS trajectory data... */
8718 else if (ctl->atm_type_out == 3)
8719 write_atm_clams_traj(filename, ctl, atm, t);
8720
8721 /* Write CLaMS pos data... */
8722 else if (ctl->atm_type_out == 4)
8723 write_atm_clams(filename, ctl, atm);
8724
8725 /* Error... */
8726 else
8727 ERRMSG("Atmospheric data type not supported!");
8728
8729 /* Write info... */
8730 double mini, maxi;
8731 LOG(2, "Number of particles: %d", atm->np);
8732 gsl_stats_minmax(&mini, &maxi, atm->time, 1, (size_t) atm->np);
8733 LOG(2, "Time range: %.2f ... %.2f s", mini, maxi);
8734 gsl_stats_minmax(&mini, &maxi, atm->p, 1, (size_t) atm->np);
8735 LOG(2, "Altitude range: %g ... %g km", Z(maxi), Z(mini));
8736 LOG(2, "Pressure range: %g ... %g hPa", maxi, mini);
8737 gsl_stats_minmax(&mini, &maxi, atm->lon, 1, (size_t) atm->np);
8738 LOG(2, "%s range: %g ... %g %s",
8739 ctl->met_coord_type == 0 ? "Longitude" : "X coordinate", mini, maxi,
8740 ctl->met_coord_type == 0 ? "deg" : "m");
8741 gsl_stats_minmax(&mini, &maxi, atm->lat, 1, (size_t) atm->np);
8742 LOG(2, "%s range: %g ... %g %s",
8743 ctl->met_coord_type == 0 ? "Latitude" : "Y coordinate", mini, maxi,
8744 ctl->met_coord_type == 0 ? "deg" : "m");
8745 for (int iq = 0; iq < ctl->nq; iq++) {
8746 char msg[5 * LEN];
8747 sprintf(msg, "Quantity %s range: %s ... %s %s",
8748 ctl->qnt_name[iq], ctl->qnt_format[iq],
8749 ctl->qnt_format[iq], ctl->qnt_unit[iq]);
8750 gsl_stats_minmax(&mini, &maxi, atm->q[iq], 1, (size_t) atm->np);
8751 LOG(2, msg, mini, maxi);
8752 }
8753}
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:13876
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:13672
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:13820
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:13770
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:14037
Here is the call graph for this function:

◆ mptrac_write_met()

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.

This function handles writing meteorological data based on the specified control (ctl_t) and meteorological data (met_t) structures. The file format and compression type are determined by the met_type in the control structure. The function supports netCDF, binary output, and various compression methods (ZFP, ZSTD, CMS), while providing error handling for unsupported configurations.

Parameters
filenameA constant character pointer representing the name of the file to write the meteorological data to.
ctlA pointer to a ctl_t structure, which holds the configuration and control parameters for the output, including the type of meteorological data and compression method.
metA pointer to a met_t structure that holds the meteorological data to be written to the file.
Note
  • The function selects a timer for performance profiling or debugging.
  • It logs the action of writing meteorological data, including the file name.
Warning
  • If ctl->met_type is 3, ZFP compression is required, and the function will generate an error if compiled without ZFP support.
  • If ctl->met_type is 4, ZSTD compression is required, and the function will generate an error if compiled without ZSTD support.
  • If ctl->met_type is 5, CMS compression is required, and the function will generate an error if compiled without CMS support.
  • If ctl->met_type is 7, SZ3 compression is required, and the function will generate an error if compiled without SZ3 support.
  • If ctl->met_type is 8, LZ4 compression is required, and the function will generate an error if compiled without LZ4 support.
Note
  • If ctl->met_type is 0, the function writes data in netCDF format via write_met_nc.
  • If ctl->met_type is between 1 and 5, or equals 7 or 8, the function writes data in binary format via write_met_bin.
  • If ctl->met_type is not recognized, an error message is generated.
Author
Lars Hoffmann

Definition at line 8757 of file mptrac.c.

8760 {
8761
8762 /* Set timer... */
8763 SELECT_TIMER("WRITE_MET", "OUTPUT");
8764
8765 /* Write info... */
8766 LOG(1, "Write meteo data: %s", filename);
8767
8768 /* Check compression flags... */
8769#ifndef ZFP
8770 if (ctl->met_type == 3)
8771 ERRMSG("MPTRAC was compiled without ZFP compression!");
8772#endif
8773#ifndef ZSTD
8774 if (ctl->met_type == 4)
8775 ERRMSG("MPTRAC was compiled without ZSTD compression!");
8776#endif
8777#ifndef LZ4
8778 if (ctl->met_type == 8)
8779 ERRMSG("MPTRAC was compiled without LZ4 compression!");
8780#endif
8781#ifndef CMS
8782 if (ctl->met_type == 5)
8783 ERRMSG("MPTRAC was compiled without cmultiscale compression!");
8784#endif
8785#ifndef SZ3
8786 if (ctl->met_type == 7)
8787 ERRMSG("MPTRAC was compiled without SZ3 compression!");
8788#endif
8789
8790 /* Write netCDF data... */
8791 if (ctl->met_type == 0)
8792 write_met_nc(filename, ctl, met);
8793
8794 /* Write binary data... */
8795 else if ((ctl->met_type >= 1 && ctl->met_type <= 5)
8796 || ctl->met_type == 7 || ctl->met_type == 8)
8797 write_met_bin(filename, ctl, met);
8798
8799 /* Not implemented... */
8800 else
8801 ERRMSG("MET_TYPE not implemented!");
8802}
void write_met_nc(const char *filename, const ctl_t *ctl, met_t *met)
Writes meteorological data to a NetCDF file.
Definition mptrac.c:15432
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:15180
Here is the call graph for this function:

◆ mptrac_write_output()

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.

The mptrac_write_output function writes various types of output data to files in the directory specified by the dirname parameter. The function takes control parameters (ctl), two meteorological data structures (met0 and met1), an atmospheric data structure (atm), and a time value (t) as input.

Parameters
dirnameA string representing the directory path where output files will be written.
ctlA pointer to a ctl_t structure containing control parameters.
met0A pointer to a met_t structure representing the first set of meteorological data.
met1A pointer to a met_t structure representing the second set of meteorological data.
atmA pointer to an atm_t structure representing atmospheric data.
depoA pointer to the cumulative radionuclide ground inventories.
tA double value representing the time at which the output is being written.

The function performs the following steps:

  • Parses the input time (t) to extract year, month, day, hour, minute, and second.
  • Updates host memory if necessary based on control parameters.
  • Writes atmospheric data to files if specified by control parameters.
  • Writes gridded data to files if specified by control parameters.
  • Writes cumulative radionuclide deposition if specified by control parameters.
  • Writes CSI (Critical Success Index) data to files if specified by control parameters.
  • Writes ensemble data to files if specified by control parameters.
  • Writes profile data to files if specified by control parameters.
  • Writes sample data to files if specified by control parameters.
  • Writes station data to files if specified by control parameters.
  • Writes VTK (Visualization Toolkit) data to files if specified by control parameters.
Note
This function orchestrates the writing of various types of output data to files based on control parameters and the current simulation time.
Author
Lars Hoffmann

Definition at line 8806 of file mptrac.c.

8813 {
8814
8815 char ext[10], filename[2 * LEN];
8816
8817 double r;
8818
8819 int year, mon, day, hour, min, sec;
8820
8821 /* Get time... */
8822 jsec2time(t, &year, &mon, &day, &hour, &min, &sec, &r);
8823
8824 /* Decide which particle outputs are due, including the final step... */
8825 const int final = (t == ctl->t_stop);
8826 const int output_atm = ctl->atm_basename[0] != '-'
8827 && (fmod(t, ctl->atm_dt_out) == 0 || final);
8828 const int output_grid = ctl->grid_basename[0] != '-'
8829 && (fmod(t, ctl->grid_dt_out) == 0 || final);
8830 const int output_budget = ctl->budget_basename[0] != '-'
8831 && (fmod(t, ctl->budget_dt_out) == 0 || final);
8832 const int output_depo = ctl->depo_basename[0] != '-'
8833 && (fmod(t, ctl->depo_dt_out) == 0 || final);
8834 const int output_ens = ctl->ens_basename[0] != '-'
8835 && (fmod(t, ctl->ens_dt_out) == 0 || final);
8836 const int output_vtk = ctl->vtk_basename[0] != '-'
8837 && (fmod(t, ctl->vtk_dt_out) == 0 || final);
8838
8839 /* Update host... */
8840 if (output_atm || output_grid || output_budget || output_ens || output_vtk
8841 || ctl->csi_basename[0] != '-' || ctl->prof_basename[0] != '-'
8842 || ctl->sample_basename[0] != '-' || ctl->stat_basename[0] != '-')
8843 mptrac_update_host(NULL, NULL, NULL, NULL, NULL, atm);
8844
8845 /* Write atmospheric data... */
8846 if (output_atm) {
8847 if (ctl->atm_type_out == 0)
8848 sprintf(ext, "tab");
8849 else if (ctl->atm_type_out == 1)
8850 sprintf(ext, "bin");
8851 else if (ctl->atm_type_out >= 2)
8852 sprintf(ext, "nc");
8853 sprintf(filename, "%s/%s_%04d_%02d_%02d_%02d_%02d_%02d.%s",
8854 dirname, ctl->atm_basename, year, mon, day, hour, min, sec, ext);
8855 mptrac_write_atm(filename, ctl, atm, t);
8856 }
8857
8858 /* Write gridded data... */
8859 if (output_grid) {
8860 sprintf(filename, "%s/%s_%04d_%02d_%02d_%02d_%02d_%02d.%s",
8861 dirname, ctl->grid_basename, year, mon, day, hour, min, sec,
8862 ctl->grid_type == 0 ? "tab" : "nc");
8863 write_grid(filename, ctl, met0, met1, atm, t);
8864 }
8865
8866 /* Write mass budget data... */
8867 if (output_budget) {
8868 sprintf(filename, "%s/%s.tab", dirname, ctl->budget_basename);
8869 write_budget(filename, ctl, atm, t);
8870 }
8871
8872 /* Write radioactive deposition data... */
8873 if (output_depo) {
8874#ifdef _OPENACC
8875#pragma acc update host(depo[:1])
8876#endif
8877 sprintf(filename, "%s/%s_%04d_%02d_%02d_%02d_%02d_%02d.%s",
8878 dirname, ctl->depo_basename, year, mon, day, hour, min, sec,
8879 ctl->depo_type == 0 ? "tab" : "nc");
8880 write_depo(filename, ctl, depo, t);
8881 }
8882
8883 /* Write CSI data... */
8884 if (ctl->csi_basename[0] != '-') {
8885 sprintf(filename, "%s/%s.tab", dirname, ctl->csi_basename);
8886 write_csi(filename, ctl, atm, t);
8887 }
8888
8889 /* Write ensemble data... */
8890 if (output_ens) {
8891 sprintf(filename, "%s/%s_%04d_%02d_%02d_%02d_%02d_%02d.tab",
8892 dirname, ctl->ens_basename, year, mon, day, hour, min, sec);
8893 write_ens(filename, ctl, atm, t);
8894 }
8895
8896 /* Write profile data... */
8897 if (ctl->prof_basename[0] != '-') {
8898 sprintf(filename, "%s/%s.tab", dirname, ctl->prof_basename);
8899 write_prof(filename, ctl, met0, met1, atm, t);
8900 }
8901
8902 /* Write sample data... */
8903 if (ctl->sample_basename[0] != '-') {
8904 sprintf(filename, "%s/%s.tab", dirname, ctl->sample_basename);
8905 write_sample(filename, ctl, met0, met1, atm, t);
8906 }
8907
8908 /* Write station data... */
8909 if (ctl->stat_basename[0] != '-') {
8910 sprintf(filename, "%s/%s.tab", dirname, ctl->stat_basename);
8911 write_station(filename, ctl, atm, t);
8912 }
8913
8914 /* Write VTK data... */
8915 if (output_vtk) {
8916 static int nvtk;
8917 if (t == ctl->t_start)
8918 nvtk = 0;
8919 sprintf(filename, "%s/%s_%05d.vtk", dirname, ctl->vtk_basename, ++nvtk);
8920 write_vtk(filename, ctl, atm, t);
8921 }
8922}
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:14567
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:8693
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:14467
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:15675
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:8637
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:16075
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:14086
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:16181
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:15910
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:14744
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:14180
Here is the call graph for this function:

◆ nat_temperature()

double nat_temperature ( const double p,
const double h2o,
const double hno3 )

Calculates the nitric acid trihydrate (NAT) temperature.

This function computes the temperature at which nitric acid trihydrate (NAT) can form given the partial pressures of water vapor and nitric acid in the atmosphere.

Parameters
pThe total atmospheric pressure (in hPa).
h2oThe volume mixing ratio of water vapor (H2O).
hno3The volume mixing ratio of nitric acid (HNO3).
Returns
The NAT temperature (in Kelvin).

This function follows these steps:

  • Ensures the water vapor volume mixing ratio is above a minimum threshold.
  • Converts the volume mixing ratios of H2O and HNO3 to partial pressures.
  • Uses these partial pressures to compute coefficients for the quadratic equation that determines the NAT temperature.
  • Solves the quadratic equation to find the NAT temperature.

The calculations are based on empirical relationships involving logarithms of the partial pressures of H2O and HNO3.

Note
The constants and formulae used are specific to the context of atmospheric chemistry and the formation of NAT.
Author
Lars Hoffmann

Definition at line 8926 of file mptrac.c.

8929 {
8930
8931 /* Check water vapor volume mixing ratio... */
8932 const double h2o_help = MAX(h2o, 0.1e-6);
8933
8934 /* Calculate T_NAT... */
8935 const double p_hno3 = hno3 * p / 1.333224;
8936 const double p_h2o = h2o_help * p / 1.333224;
8937 const double a = 0.009179 - 0.00088 * log10(p_h2o);
8938 const double b = (38.9855 - log10(p_hno3) - 2.7836 * log10(p_h2o)) / a;
8939 const double c = -11397.0 / a;
8940 double tnat = (-b + sqrt(b * b - 4. * c)) / 2.;
8941 double x2 = (-b - sqrt(b * b - 4. * c)) / 2.;
8942 if (x2 > 0)
8943 tnat = x2;
8944
8945 return tnat;
8946}

◆ pbl_weight()

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.

This function calculates a weighting factor that determines the contribution of a pressure level to processes within the planetary boundary layer. The factor is based on the relative position of the pressure within a linear transition range defined by pbl, ps, and TURB_PBL_TRANS, expressed as a fraction of the PBL pressure thickness (ps - pbl).

Parameters
ctlPointer to the control structure containing configuration parameters.
atmPointer to the atmospheric data structure containing pressure levels.
ipIndex of the pressure level in the atmospheric data array.
pblPressure at the planetary boundary layer.
psSurface pressure.
Returns
Weighting factor for the specified pressure level:
  • Returns 1.0 if the pressure is above the upper boundary (p0).
  • Returns 0.0 if the pressure is below the lower boundary (p1).
  • Returns a linearly interpolated value between 1.0 and 0.0 for pressures within the transition range.
Author
Lars Hoffmann

Definition at line 8950 of file mptrac.c.

8955 {
8956
8957 /* Get pressure range... */
8958 const double p1 = pbl - ctl->turb_pbl_trans * (ps - pbl);
8959 const double p0 = pbl;
8960
8961 /* Get weighting factor... */
8962 if (atm->p[ip] > p0)
8963 return 1;
8964 else if (atm->p[ip] < p1)
8965 return 0;
8966 else
8967 return LIN(p0, 1.0, p1, 0.0, atm->p[ip]);
8968}

◆ read_atm_asc()

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.

This function reads air parcel data from an ASCII file and stores the data in the provided atm_t structure. It reads each line of the file, extracts the necessary data fields, and converts the altitude to pressure.

Parameters
filenameThe name of the ASCII file containing the atmospheric data.
ctlA pointer to the control structure (ctl_t) that specifies the number of quantities.
atmA pointer to the atmospheric structure (atm_t) that will be populated with the data.
Returns
Returns 1 on success, and 0 on failure.

This function performs the following steps:

  • Attempts to open the specified file for reading.
  • Logs a warning and returns 0 if the file cannot be opened.
  • Reads each line of the file and extracts data values for time, altitude, longitude, latitude, and other specified quantities.
  • Converts the altitude to pressure.
  • Increments the data point counter.
  • Checks if the number of data points exceeds the maximum allowed (NP) and logs an error message if so.
  • Closes the file after reading all data.
  • Returns 1 to indicate successful data reading.

The function utilizes several macros and helper functions:

  • WARN for logging warnings.
  • ERRMSG for handling error messages.
  • TOK for tokenizing and reading values from the line.
  • P for converting altitude to pressure.
Author
Lars Hoffmann

Definition at line 8972 of file mptrac.c.

8975 {
8976
8977 /* Open file... */
8978 FILE *in;
8979 if (!(in = fopen(filename, "r"))) {
8980 WARN("Cannot open file!");
8981 return 0;
8982 }
8983
8984 /* Read line... */
8985 char line[LEN];
8986 while (fgets(line, LEN, in)) {
8987
8988 /* Read data... */
8989 char *tok;
8990 TOK(line, tok, "%lg", atm->time[atm->np]);
8991 TOK(NULL, tok, "%lg", atm->p[atm->np]);
8992 TOK(NULL, tok, "%lg", atm->lon[atm->np]);
8993 TOK(NULL, tok, "%lg", atm->lat[atm->np]);
8994 for (int iq = 0; iq < ctl->nq; iq++)
8995 TOK(NULL, tok, "%lg", atm->q[iq][atm->np]);
8996
8997 /* Convert altitude to pressure... */
8998 atm->p[atm->np] = P(atm->p[atm->np]);
8999
9000 /* Increment data point counter... */
9001 if ((++atm->np) > NP)
9002 ERRMSG("Too many data points!");
9003 }
9004
9005 /* Close file... */
9006 fclose(in);
9007
9008 /* Return success... */
9009 return 1;
9010}
#define TOK(line, tok, format, var)
Get string tokens.
Definition mptrac.h:2194

◆ read_atm_bin()

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.

This function reads air parcel data from a binary file and stores the data in the provided atm_t structure. It checks the version of the binary data, reads the data values, and verifies the integrity of the data read.

Parameters
filenameThe name of the binary file containing the atmospheric data.
ctlA pointer to the control structure (ctl_t) that specifies the number of quantities.
atmA pointer to the atmospheric structure (atm_t) that will be populated with the data.
Returns
Returns 1 on success, and 0 on failure.

This function performs the following steps:

  • Attempts to open the specified file for reading.
  • Returns 0 if the file cannot be opened.
  • Checks the version of the binary data and logs an error message if the version is incorrect.
  • Reads the number of data points (np).
  • Reads the data arrays for time, pressure, longitude, latitude, and other specified quantities.
  • Checks a final flag to ensure the data was read correctly.
  • Logs an error message if the final flag is incorrect.
  • Closes the file after reading all data.
  • Returns 1 to indicate successful data reading.

The function utilizes several macros and helper functions:

  • ERRMSG for handling error messages.
  • FREAD for reading data from the binary file.
Author
Lars Hoffmann

Definition at line 9014 of file mptrac.c.

9017 {
9018
9019 /* Open file... */
9020 FILE *in;
9021 if (!(in = fopen(filename, "r")))
9022 return 0;
9023
9024 /* Check version of binary data... */
9025 int version;
9026 FREAD(&version, int,
9027 1,
9028 in);
9029 if (version != 100)
9030 ERRMSG("Wrong version of binary data!");
9031
9032 /* Read data... */
9033 FREAD(&atm->np, int,
9034 1,
9035 in);
9036 FREAD(atm->time, double,
9037 (size_t) atm->np,
9038 in);
9039 FREAD(atm->p, double,
9040 (size_t) atm->np,
9041 in);
9042 FREAD(atm->lon, double,
9043 (size_t) atm->np,
9044 in);
9045 FREAD(atm->lat, double,
9046 (size_t) atm->np,
9047 in);
9048 for (int iq = 0; iq < ctl->nq; iq++)
9049 FREAD(atm->q[iq], double,
9050 (size_t) atm->np,
9051 in);
9052
9053 /* Read final flag... */
9054 int final;
9055 FREAD(&final, int,
9056 1,
9057 in);
9058 if (final != 999)
9059 ERRMSG("Error while reading binary data!");
9060
9061 /* Close file... */
9062 fclose(in);
9063
9064 /* Return success... */
9065 return 1;
9066}

◆ read_atm_clams()

int read_atm_clams ( const char * filename,
const ctl_t * ctl,
atm_t * atm )

Reads atmospheric data from a CLAMS NetCDF file.

This function opens a NetCDF file, reads various atmospheric parameters, and stores them in the provided atm_t structure. It handles both zeta and pressure coordinate systems depending on the control settings.

Parameters
[in]filenamePath to the NetCDF file containing atmospheric data.
[in]ctlPointer to the control structure containing configuration settings.
[out]atmPointer to the atmospheric data structure where the data will be stored.
Returns
Returns 1 on success, 0 on failure.
  • Opens the NetCDF file in read-only mode.
  • Retrieves the number of particles (NPARTS).
  • Reads initial time (TIME_INIT) or falls back to time if unavailable.
  • Depending on ctl->advect_vert_coord, reads ZETA and optionally PRESS, or reads PRESS_INIT with fallback to PRESS.
  • Reads longitude (LON) and latitude (LAT).
  • Closes the NetCDF file before returning.
Author
Jan Clemens

Definition at line 9070 of file mptrac.c.

9073 {
9074
9075 if (ctl->met_coord_type != 0)
9076 ERRMSG("CLaMS atmospheric files support only lat/lon grids");
9077
9078 int ncid, varid;
9079
9080 /* Open file... */
9081 if (nc_open(filename, NC_NOWRITE, &ncid) != NC_NOERR)
9082 return 0;
9083
9084 /* Get dimensions... */
9085 NC_INQ_DIM("NPARTS", &atm->np, 1, NP, 1);
9086
9087 /* Get time... */
9088 if (nc_inq_varid(ncid, "TIME_INIT", &varid) == NC_NOERR) {
9089 NC(nc_get_var_double(ncid, varid, atm->time));
9090 } else {
9091 WARN("TIME_INIT not found use time instead!");
9092 double time_init;
9093 NC_GET_DOUBLE("time", &time_init, 1);
9094 for (int ip = 0; ip < atm->np; ip++) {
9095 atm->time[ip] = time_init;
9096 }
9097 }
9098
9099 /* Read zeta coordinate, pressure is optional... */
9100 if (ctl->advect_vert_coord == 1) {
9101 NC_GET_DOUBLE("ZETA", atm->q[ctl->qnt_zeta], 1);
9102 NC_GET_DOUBLE("PRESS", atm->p, 0);
9103 }
9104
9105 /* Read pressure, zeta coordinate is optional... */
9106 else {
9107 if (nc_inq_varid(ncid, "PRESS_INIT", &varid) == NC_NOERR) {
9108 NC(nc_get_var_double(ncid, varid, atm->p));
9109 } else {
9110 WARN("PRESS_INIT not found use PRESS instead!");
9111 nc_inq_varid(ncid, "PRESS", &varid);
9112 NC(nc_get_var_double(ncid, varid, atm->p));
9113 }
9114 }
9115
9116 /* Read further quantities if requested... */
9117 for (int iq = 0; iq < ctl->nq; iq++)
9118 NC_GET_DOUBLE(ctl->qnt_name[iq], atm->q[iq], 0);
9119
9120 /* Read longitude and latitude... */
9121 NC_GET_DOUBLE("LON", atm->lon, 1);
9122 NC_GET_DOUBLE("LAT", atm->lat, 1);
9123
9124 /* Close file... */
9125 NC(nc_close(ncid));
9126
9127 /* Return success... */
9128 return 1;
9129}
#define NC(cmd)
Execute a NetCDF command and check for errors.
Definition mptrac.h:1517
#define NC_GET_DOUBLE(varname, ptr, force)
Retrieve a double-precision variable from a NetCDF file.
Definition mptrac.h:1587
#define NC_INQ_DIM(dimname, ptr, min, max, check)
Inquire the length of a dimension in a NetCDF file.
Definition mptrac.h:1617

◆ read_atm_nc()

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.

This function reads air parcel data from a netCDF file and stores the data in the provided atm_t structure. It retrieves the dimensions, geolocations (time, pressure, longitude, latitude), and specified variables from the file.

Parameters
filenameThe name of the netCDF file containing the atmospheric data.
ctlA pointer to the control structure (ctl_t) that specifies the number of quantities and their names.
atmA pointer to the atmospheric structure (atm_t) that will be populated with the data.
Returns
Returns 1 on success, and 0 on failure.

This function performs the following steps:

  • Attempts to open the specified netCDF file for reading.
  • Returns 0 if the file cannot be opened.
  • Retrieves the number of observations (np) from the "obs" dimension.
  • Reads the geolocation data arrays for time, pressure, longitude, and latitude.
  • Reads the specified variables into the corresponding arrays in the atm_t structure.
  • Closes the netCDF file after reading all data.
  • Returns 1 to indicate successful data reading.

The function utilizes several macros and helper functions:

  • NC_INQ_DIM for inquiring about dimensions in the netCDF file.
  • NC_GET_DOUBLE for reading double values from the netCDF file.
  • NC for checking netCDF function return values.
Author
Lars Hoffmann

Definition at line 9133 of file mptrac.c.

9136 {
9137
9138 int ncid, varid;
9139
9140 /* Open file... */
9141 if (nc_open(filename, NC_NOWRITE, &ncid) != NC_NOERR)
9142 return 0;
9143
9144 /* Get dimensions... */
9145 NC_INQ_DIM("obs", &atm->np, 1, NP, 1);
9146
9147 /* Read geolocations... */
9148 NC_GET_DOUBLE("time", atm->time, 1);
9149 NC_GET_DOUBLE("press", atm->p, 1);
9150 NC_GET_DOUBLE("lon", atm->lon, 1);
9151 NC_GET_DOUBLE("lat", atm->lat, 1);
9152
9153 /* Read variables... */
9154 for (int iq = 0; iq < ctl->nq; iq++)
9155 NC_GET_DOUBLE(ctl->qnt_name[iq], atm->q[iq], 0);
9156
9157 /* Close file... */
9158 NC(nc_close(ncid));
9159
9160 /* Return success... */
9161 return 1;
9162}

◆ read_clim_photo()

void read_clim_photo ( const char * filename,
clim_photo_t * photo )

Reads photolysis rates from a NetCDF file and populates the given photolysis structure.

This function opens a NetCDF file specified by the filename, reads various dimensions and data related to photolysis rates, and stores this data in the provided clim_photo_t structure. It includes checks for data consistency and logs detailed information about the loaded data.

Parameters
filenameA string containing the path to the NetCDF file containing photolysis rate data.
photoA pointer to the photolysis structure (clim_photo_t) that will be populated with the data.

The function performs the following steps:

  • Logs the initiation of reading photolysis rates.
  • Opens the NetCDF file in read-only mode.
  • Reads pressure data and checks for descending order.
  • Reads total column ozone data and checks for ascending order.
  • Reads solar zenith angle data and checks for ascending order.
  • Allocates memory for temporary arrays to hold the data.
  • Reads various photolysis rates (e.g., J_N2O, J_CCl4, J_CFC-11, J_CFC-12, J_O2, J_O3b, J_O3a, J_H2O2, J_H2O) and stores them in the clim_photo_t structure.
  • Frees the allocated memory for temporary arrays.
  • Closes the NetCDF file.
  • Logs detailed information about the loaded data, including pressure levels, solar zenith angles, and photolysis rates.
Author
Mingzhao Liu

Definition at line 9166 of file mptrac.c.

9168 {
9169
9170 int ncid, varid;
9171
9172 /* Write info... */
9173 LOG(1, "Read photolysis rates: %s", filename);
9174
9175 /* Open netCDF file... */
9176 if (nc_open(filename, NC_NOWRITE, &ncid) != NC_NOERR) {
9177 WARN("Photolysis rate data are missing!");
9178 return;
9179 }
9180
9181 /* Read pressure data... */
9182 NC_INQ_DIM("press", &photo->np, 2, CP, 1);
9183 NC_GET_DOUBLE("press", photo->p, 1);
9184 if (photo->p[0] < photo->p[1])
9185 ERRMSG("Pressure data are not descending!");
9186
9187 /* Read total column ozone data... */
9188 NC_INQ_DIM("total_o3col", &photo->no3c, 2, CO3, 1);
9189 NC_GET_DOUBLE("total_o3col", photo->o3c, 1);
9190 if (photo->o3c[0] > photo->o3c[1])
9191 ERRMSG("Total column ozone data are not ascending!");
9192
9193 /* Read solar zenith angle data... */
9194 NC_INQ_DIM("sza", &photo->nsza, 2, CSZA, 1);
9195 NC_GET_DOUBLE("sza", photo->sza, 1);
9196 if (photo->sza[0] > photo->sza[1])
9197 ERRMSG("Solar zenith angle data are not ascending!");
9198
9199 /* Read data... */
9200 read_clim_photo_help(ncid, "J_N2O", photo, photo->n2o);
9201 read_clim_photo_help(ncid, "J_CCl4", photo, photo->ccl4);
9202 read_clim_photo_help(ncid, "J_CFC-11", photo, photo->ccl3f);
9203 read_clim_photo_help(ncid, "J_CFC-12", photo, photo->ccl2f2);
9204 read_clim_photo_help(ncid, "J_O2", photo, photo->o2);
9205 read_clim_photo_help(ncid, "J_O3b", photo, photo->o3_1);
9206 read_clim_photo_help(ncid, "J_O3a", photo, photo->o3_2);
9207 read_clim_photo_help(ncid, "J_H2O2", photo, photo->h2o2);
9208 read_clim_photo_help(ncid, "J_H2O", photo, photo->h2o);
9209
9210 /* Close netCDF file... */
9211 NC(nc_close(ncid));
9212
9213 /* Write info... */
9214 LOG(2, "Number of pressure levels: %d", photo->np);
9215 LOG(2, "Altitude levels: %g, %g ... %g km",
9216 Z(photo->p[0]), Z(photo->p[1]), Z(photo->p[photo->np - 1]));
9217 LOG(2, "Pressure levels: %g, %g ... %g hPa",
9218 photo->p[0], photo->p[1], photo->p[photo->np - 1]);
9219 LOG(2, "Number of solar zenith angles: %d", photo->nsza);
9220 LOG(2, "Solar zenith angles: %g, %g ... %g deg",
9221 RAD2DEG(photo->sza[0]), RAD2DEG(photo->sza[1]),
9222 RAD2DEG(photo->sza[photo->nsza - 1]));
9223 LOG(2, "Number of total column ozone values: %d", photo->no3c);
9224 LOG(2, "Total column ozone: %g, %g ... %g DU",
9225 photo->o3c[0], photo->o3c[1], photo->o3c[photo->no3c - 1]);
9226 LOG(2, "N2O photolysis rate: %g, %g ... %g s**-1",
9227 photo->n2o[0][0][0], photo->n2o[1][0][0],
9228 photo->n2o[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9229 LOG(2, "CCl4 photolysis rate: %g, %g ... %g s**-1",
9230 photo->ccl4[0][0][0], photo->ccl4[1][0][0],
9231 photo->ccl4[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9232 LOG(2, "CFC-11 photolysis rate: %g, %g ... %g s**-1",
9233 photo->ccl3f[0][0][0], photo->ccl3f[1][0][0],
9234 photo->ccl3f[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9235 LOG(2, "CFC-12 photolysis rate: %g, %g ... %g s**-1",
9236 photo->ccl2f2[0][0][0], photo->ccl2f2[1][0][0],
9237 photo->ccl2f2[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9238 LOG(2, "O2 photolysis rate: %g, %g ... %g s**-1",
9239 photo->o2[0][0][0], photo->o2[1][0][0],
9240 photo->o2[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9241 LOG(2, "O3 -> O(1D) photolysis rate: %g, %g ... %g s**-1",
9242 photo->o3_1[0][0][0], photo->o3_1[1][0][0],
9243 photo->o3_1[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9244 LOG(2, "O3 -> O(3P) photolysis rate: %g, %g ... %g s**-1",
9245 photo->o3_2[0][0][0], photo->o3_2[1][0][0],
9246 photo->o3_2[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9247 LOG(2, "H2O2 photolysis rate: %g, %g ... %g s**-1",
9248 photo->h2o2[0][0][0], photo->h2o2[1][0][0],
9249 photo->h2o2[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9250 LOG(2, "H2O photolysis rate: %g, %g ... %g s**-1",
9251 photo->h2o[0][0][0], photo->h2o[1][0][0],
9252 photo->h2o[photo->np - 1][photo->nsza - 1][photo->no3c - 1]);
9253}
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:9257
#define CP
Maximum number of pressure levels for climatological data.
Definition mptrac.h:609
#define CO3
Maximum number of total column ozone data for climatological data.
Definition mptrac.h:604
#define CSZA
Maximum number of solar zenith angles for climatological data.
Definition mptrac.h:614
double o3_1[CP][CSZA][CO3]
O3 photolysis rate (O3 + hv = O1d + O2) [1/s].
Definition mptrac.h:3765
double o2[CP][CSZA][CO3]
O2 photolysis rate [1/s].
Definition mptrac.h:3762
double h2o2[CP][CSZA][CO3]
H2O2 photolysis rate [1/s].
Definition mptrac.h:3771
double h2o[CP][CSZA][CO3]
H2O photolysis rate [1/s].
Definition mptrac.h:3774
double o3_2[CP][CSZA][CO3]
O3 photolysis rate (O3 + hv = O3p + O2) [1/s].
Definition mptrac.h:3768
Here is the call graph for this function:

◆ read_clim_photo_help()

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.

This function reads a variable from a NetCDF file into a 3D array based on the dimensions provided by the clim_photo_t structure.

Parameters
[in]ncidNetCDF file ID.
[in]varnameName of the variable to read from the NetCDF file.
[in]photoPointer to a structure defining the data dimensions (np, nsza, no3c).
[out]var3D array to store the read data, with dimensions [CP][CSZA][CO3].
Note
Allocates temporary memory for reading and copies data into the provided array. The memory is freed after the data is copied.
Author
Lars Hoffmann

Definition at line 9257 of file mptrac.c.

9261 {
9262
9263 /* Allocate... */
9264 double *help;
9265 ALLOC(help, double,
9266 photo->np * photo->nsza * photo->no3c);
9267
9268 /* Read variable... */
9269 int varid;
9270 NC_GET_DOUBLE(varname, help, 1);
9271
9272 /* Copy data... */
9273 for (int ip = 0; ip < photo->np; ip++)
9274 for (int is = 0; is < photo->nsza; is++)
9275 for (int io = 0; io < photo->no3c; io++)
9276 var[ip][is][io] =
9277 help[ARRAY_3D(ip, is, photo->nsza, io, photo->no3c)];
9278
9279 /* Free... */
9280 free(help);
9281}

◆ read_clim_ts()

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.

This function reads time and volume mixing ratio (VMR) data from a specified file, processes the data, and stores it in the provided clim_ts_t structure. It also includes checks for data consistency and logs detailed information about the loaded data.

Parameters
filenameA string containing the path to the file containing the climatological time series data.
tsA pointer to the time series structure (clim_ts_t) that will be populated with the data.
Returns
Returns 1 on success, and 0 on failure (e.g., if the file cannot be opened or data is invalid).

The function performs the following steps:

  • Logs the initiation of reading the climatological time series.
  • Opens the file for reading.
  • Reads time and VMR data from the file, converting years to seconds.
  • Checks for ascending order of time data and ensures the number of data points does not exceed the limit.
  • Closes the file after reading.
  • Checks if there are enough data points.
  • Logs detailed information about the loaded data, including the number of time steps and the range of VMR values.
Author
Lars Hoffmann

Definition at line 9285 of file mptrac.c.

9287 {
9288
9289 /* Write info... */
9290 LOG(1, "Read climatological time series: %s", filename);
9291
9292 /* Open file... */
9293 FILE *in;
9294 if (!(in = fopen(filename, "r"))) {
9295 WARN("Cannot open file!");
9296 return 0;
9297 }
9298
9299 /* Read data... */
9300 char line[LEN];
9301 int nh = 0;
9302 while (fgets(line, LEN, in))
9303 if (sscanf(line, "%lg %lg", &ts->time[nh], &ts->vmr[nh]) == 2) {
9304
9305 /* Convert years to seconds... */
9306 ts->time[nh] = (ts->time[nh] - 2000.0) * 365.25 * 86400.;
9307
9308 /* Check data... */
9309 if (nh > 0 && ts->time[nh] <= ts->time[nh - 1])
9310 ERRMSG("Time series must be ascending!");
9311
9312 /* Count time steps... */
9313 if ((++nh) >= CTS)
9314 ERRMSG("Too many data points!");
9315 }
9316
9317 /* Close file... */
9318 fclose(in);
9319
9320 /* Check number of data points... */
9321 ts->ntime = nh;
9322 if (nh < 2)
9323 ERRMSG("Not enough data points!");
9324
9325 /* Write info... */
9326 LOG(2, "Number of time steps: %d", ts->ntime);
9327 LOG(2, "Time steps: %.2f, %.2f ... %.2f s", ts->time[0], ts->time[1],
9328 ts->time[nh - 1]);
9329 LOG(2, "Volume mixing ratio range: %g ... %g ppv",
9330 gsl_stats_min(ts->vmr, 1, (size_t) nh), gsl_stats_max(ts->vmr, 1,
9331 (size_t) nh));
9332
9333 /* Exit success... */
9334 return 1;
9335}
#define CTS
Maximum number of data points of climatological time series.
Definition mptrac.h:624

◆ read_clim_zm()

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.

This function reads data from a specified netCDF file, including pressure levels, latitudes, and volume mixing ratios (VMR) for a specified variable. It performs necessary checks and logs detailed information about the loaded data.

Parameters
filenameA string containing the path to the netCDF file.
varnameA string containing the name of the variable to be read from the netCDF file.
zmA pointer to the structure (clim_zm_t) that will be populated with the data.

The function performs the following steps:

  • Logs the initiation of reading the specified data.
  • Opens the netCDF file for reading.
  • Reads pressure level data and checks for descending order.
  • Reads latitude data and checks for ascending order.
  • Sets the time data for monthly means.
  • Checks the number of time steps.
  • Reads the specified variable data from the file.
  • Fixes any gaps in the data by interpolating from valid values.
  • Logs detailed information about the loaded data, including the number of time steps, pressure levels, latitude values, and the range of the variable's volume mixing ratios.
Author
Lars Hoffmann

Definition at line 9339 of file mptrac.c.

9342 {
9343
9344 int ncid, varid, it, iy, iz, iz2, nt;
9345
9346 double *help, varmin = 1e99, varmax = -1e99;
9347
9348 /* Write info... */
9349 LOG(1, "Read %s data: %s", varname, filename);
9350
9351 /* Open netCDF file... */
9352 if (nc_open(filename, NC_NOWRITE, &ncid) != NC_NOERR) {
9353 WARN("%s climatology data are missing!", varname);
9354 return;
9355 }
9356
9357 /* Read pressure data... */
9358 NC_INQ_DIM("press", &zm->np, 2, CP, 1);
9359 NC_GET_DOUBLE("press", zm->p, 1);
9360 if (zm->p[0] < zm->p[1])
9361 ERRMSG("Pressure data are not descending!");
9362
9363 /* Read latitudes... */
9364 NC_INQ_DIM("lat", &zm->nlat, 2, CY, 1);
9365 NC_GET_DOUBLE("lat", zm->lat, 1);
9366 if (zm->lat[0] > zm->lat[1])
9367 ERRMSG("Latitude data are not ascending!");
9368
9369 /* Set time data (for monthly means)... */
9370 zm->ntime = 12;
9371 zm->time[0] = 1209600.00;
9372 zm->time[1] = 3888000.00;
9373 zm->time[2] = 6393600.00;
9374 zm->time[3] = 9072000.00;
9375 zm->time[4] = 11664000.00;
9376 zm->time[5] = 14342400.00;
9377 zm->time[6] = 16934400.00;
9378 zm->time[7] = 19612800.00;
9379 zm->time[8] = 22291200.00;
9380 zm->time[9] = 24883200.00;
9381 zm->time[10] = 27561600.00;
9382 zm->time[11] = 30153600.00;
9383
9384 /* Check number of timesteps... */
9385 NC_INQ_DIM("time", &nt, 12, 12, 1);
9386
9387 /* Read data... */
9388 ALLOC(help, double,
9389 zm->nlat * zm->np * zm->ntime);
9390 NC_GET_DOUBLE(varname, help, 1);
9391 for (it = 0; it < zm->ntime; it++)
9392 for (iz = 0; iz < zm->np; iz++)
9393 for (iy = 0; iy < zm->nlat; iy++)
9394 zm->vmr[it][iz][iy] = help[ARRAY_3D(it, iz, zm->np, iy, zm->nlat)];
9395 free(help);
9396
9397 /* Fix data gaps... */
9398 for (it = 0; it < zm->ntime; it++)
9399 for (iy = 0; iy < zm->nlat; iy++)
9400 for (iz = 0; iz < zm->np; iz++) {
9401 if (zm->vmr[it][iz][iy] < 0) {
9402 for (iz2 = 0; iz2 < zm->np; iz2++)
9403 if (zm->vmr[it][iz2][iy] >= 0) {
9404 zm->vmr[it][iz][iy] = zm->vmr[it][iz2][iy];
9405 break;
9406 }
9407 for (iz2 = zm->np - 1; iz2 >= 0; iz2--)
9408 if (zm->vmr[it][iz2][iy] >= 0) {
9409 zm->vmr[it][iz][iy] = zm->vmr[it][iz2][iy];
9410 break;
9411 }
9412 }
9413 varmin = MIN(varmin, zm->vmr[it][iz][iy]);
9414 varmax = MAX(varmax, zm->vmr[it][iz][iy]);
9415 }
9416
9417 /* Close netCDF file... */
9418 NC(nc_close(ncid));
9419
9420 /* Write info... */
9421 LOG(2, "Number of time steps: %d", zm->ntime);
9422 LOG(2, "Time steps: %.2f, %.2f ... %.2f s",
9423 zm->time[0], zm->time[1], zm->time[zm->ntime - 1]);
9424 LOG(2, "Number of pressure levels: %d", zm->np);
9425 LOG(2, "Altitude levels: %g, %g ... %g km",
9426 Z(zm->p[0]), Z(zm->p[1]), Z(zm->p[zm->np - 1]));
9427 LOG(2, "Pressure levels: %g, %g ... %g hPa", zm->p[0],
9428 zm->p[1], zm->p[zm->np - 1]);
9429 LOG(2, "Number of latitudes: %d", zm->nlat);
9430 LOG(2, "Latitudes: %g, %g ... %g deg",
9431 zm->lat[0], zm->lat[1], zm->lat[zm->nlat - 1]);
9432 LOG(2, "%s volume mixing ratio range: %g ... %g ppv", varname, varmin,
9433 varmax);
9434}
#define CY
Maximum number of latitudes for climatological data.
Definition mptrac.h:599

◆ read_kernel()

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.

This function reads kernel function data from a specified file, populating the provided arrays kz and kw with the parsed data. It also updates the variable pointed to by nk with the number of data points read. The function ensures that the height levels are in ascending order and performs checks for the number of height levels read.

Parameters
filenameA string containing the path to the file containing kernel function data.
kzA double array to store the height levels of the kernel function.
kwA double array to store the weights corresponding to the height levels.
nkA pointer to an integer variable representing the number of data points read.

The function performs the following steps:

  • Logs information indicating the kernel function file being read.
  • Attempts to open the specified file for reading.
  • Reads data from the file line by line, parsing height levels and weights.
  • Checks that the height levels are in ascending order and that the number of data points does not exceed the defined maximum.
  • Closes the file after reading.
  • Updates the value of nk with the number of data points read.
  • Normalizes the kernel function weights by dividing each weight by the maximum weight.
Author
Lars Hoffmann

Definition at line 9438 of file mptrac.c.

9442 {
9443
9444 /* Write info... */
9445 LOG(1, "Read kernel function: %s", filename);
9446
9447 /* Open file... */
9448 FILE *in;
9449 if (!(in = fopen(filename, "r")))
9450 ERRMSG("Cannot open file!");
9451
9452 /* Read data... */
9453 char line[LEN];
9454 int n = 0;
9455 while (fgets(line, LEN, in))
9456 if (sscanf(line, "%lg %lg", &kz[n], &kw[n]) == 2) {
9457 if (n > 0 && kz[n] < kz[n - 1])
9458 ERRMSG("Height levels must be ascending!");
9459 if ((++n) >= EP)
9460 ERRMSG("Too many height levels!");
9461 }
9462
9463 /* Close file... */
9464 fclose(in);
9465
9466 /* Check number of data points... */
9467 *nk = n;
9468 if (n < 2)
9469 ERRMSG("Not enough height levels!");
9470
9471 /* Normalize kernel function... */
9472 const double kmax = gsl_stats_max(kw, 1, (size_t) n);
9473 for (int iz = 0; iz < n; iz++)
9474 kw[iz] /= kmax;
9475}

◆ read_met_bin()

int read_met_bin ( const char * filename,
const ctl_t * ctl,
met_t * met )

Reads meteorological data from a binary file.

This function reads meteorological data from a binary file and populates the provided met_t structure with the data. It checks the binary file's format version and met_type, ensuring compatibility with the control structure (ctl_t). The function reads time, grid, surface data, and multi-level data, and supports different binary file versions.

Parameters
filenameA constant character pointer representing the name of the binary file to read the meteorological data from.
ctlA pointer to a ctl_t structure that holds control parameters such as the expected met_type and other configuration options.
metA pointer to a met_t structure that will store the meteorological data read from the binary file.
Note
  • The function logs the progress and details of the read operation, such as time, number of longitudes, latitudes, levels, and various meteorological variables.
  • It uses the FREAD macro for safe binary reading operations, which checks the integrity of the read operation.
  • The function reads and verifies the met_type and binary file version to ensure compatibility.
  • Supported binary file versions include 100, 101, and 102, each of which may include additional variables (e.g., LSM, SST, RWC, SWC, and CC).
Warning
  • The function will raise an error if the met_type in the file does not match the ctl->met_type.
  • It will raise an error if the binary file version is not supported.
  • If the dimensions of the data (e.g., number of longitudes, latitudes, or levels) are outside the expected range, an error will be raised.
Author
Lars Hoffmann

Definition at line 9479 of file mptrac.c.

9482 {
9483
9484 FILE *in;
9485
9486 double r;
9487
9488 int year, mon, day, hour, min, sec;
9489
9490 /* Set timer... */
9491 SELECT_TIMER("READ_MET_BIN", "INPUT");
9492
9493 /* Open file... */
9494 if (!(in = fopen(filename, "r"))) {
9495 WARN("Cannot open file!");
9496 return 0;
9497 }
9498
9499 /* Check type of binary data... */
9500 int met_type;
9501 FREAD(&met_type, int,
9502 1,
9503 in);
9504 if (met_type != ctl->met_type)
9505 ERRMSG("Wrong MET_TYPE of binary data!");
9506
9507 /* Check version of binary data... */
9508 int version;
9509 FREAD(&version, int,
9510 1,
9511 in);
9512 if (version != 104)
9513 ERRMSG("Wrong version of binary data!");
9514
9515 /* Read time... */
9516 FREAD(&met->time, double,
9517 1,
9518 in);
9519 jsec2time(met->time, &year, &mon, &day, &hour, &min, &sec, &r);
9520 LOG(2, "Time: %.2f (%d-%02d-%02d, %02d:%02d UTC)",
9521 met->time, year, mon, day, hour, min);
9522 if (year < 1900 || year > 2100 || mon < 1 || mon > 12
9523 || day < 1 || day > 31 || hour < 0 || hour > 23)
9524 ERRMSG("Error while reading time!");
9525
9526 /* Read dimensions... */
9527 met->coord_type = ctl->met_coord_type;
9528
9529 FREAD(&met->nx, int,
9530 1,
9531 in);
9532 LOG(2, "Number of %s: %d",
9533 (met->coord_type == 0) ? "longitudes" : "x coordinates", met->nx);
9534 if (met->nx < 2 || met->nx > EX)
9535 ERRMSG(met->coord_type == 0
9536 ? "Number of longitudes out of range!"
9537 : "Number of x coordinates out of range!");
9538
9539 FREAD(&met->ny, int,
9540 1,
9541 in);
9542 LOG(2, "Number of %s: %d",
9543 (met->coord_type == 0) ? "latitudes" : "y coordinates", met->ny);
9544 if (met->ny < 2 || met->ny > EY)
9545 ERRMSG(met->coord_type == 0
9546 ? "Number of latitudes out of range!"
9547 : "Number of y coordinates out of range!");
9548
9549 FREAD(&met->np, int,
9550 1,
9551 in);
9552 LOG(2, "Number of levels: %d", met->np);
9553 if (met->np < 2 || met->np > EP)
9554 ERRMSG("Number of levels out of range!");
9555
9556 /* Read grid... */
9557 FREAD(met->lon, double,
9558 (size_t) met->nx,
9559 in);
9560 LOG(2, "%s: %g, %g ... %g %s",
9561 met->coord_type == 0 ? "Longitudes" : "X coordinates",
9562 met->lon[0], met->lon[1], met->lon[met->nx - 1],
9563 met->coord_type == 0 ? "deg" : "m");
9564
9565 FREAD(met->lat, double,
9566 (size_t) met->ny,
9567 in);
9568 LOG(2, "%s: %g, %g ... %g %s",
9569 met->coord_type == 0 ? "Latitudes" : "Y coordinates",
9570 met->lat[0], met->lat[1], met->lat[met->ny - 1],
9571 met->coord_type == 0 ? "deg" : "m");
9572
9573 FREAD(met->p, double,
9574 (size_t) met->np,
9575 in);
9576 LOG(2, "Altitude levels: %g, %g ... %g km",
9577 Z(met->p[0]), Z(met->p[1]), Z(met->p[met->np - 1]));
9578 LOG(2, "Pressure levels: %g, %g ... %g hPa",
9579 met->p[0], met->p[1], met->p[met->np - 1]);
9580
9581 /* Read surface data... */
9582 read_met_bin_2d(in, met, met->ps, "PS");
9583 read_met_bin_2d(in, met, met->ts, "TS");
9584 read_met_bin_2d(in, met, met->zs, "ZS");
9585 read_met_bin_2d(in, met, met->us, "US");
9586 read_met_bin_2d(in, met, met->vs, "VS");
9587 read_met_bin_2d(in, met, met->ess, "ESS");
9588 read_met_bin_2d(in, met, met->nss, "NSS");
9589 read_met_bin_2d(in, met, met->shf, "SHF");
9590 read_met_bin_2d(in, met, met->lsm, "LSM");
9591 read_met_bin_2d(in, met, met->sst, "SST");
9592 read_met_bin_2d(in, met, met->pbl, "PBL");
9593 read_met_bin_2d(in, met, met->pt, "PT");
9594 read_met_bin_2d(in, met, met->tt, "TT");
9595 read_met_bin_2d(in, met, met->zt, "ZT");
9596 read_met_bin_2d(in, met, met->h2ot, "H2OT");
9597 read_met_bin_2d(in, met, met->pct, "PCT");
9598 read_met_bin_2d(in, met, met->pcb, "PCB");
9599 read_met_bin_2d(in, met, met->cl, "CL");
9600 read_met_bin_2d(in, met, met->plcl, "PLCL");
9601 read_met_bin_2d(in, met, met->plfc, "PLFC");
9602 read_met_bin_2d(in, met, met->pel, "PEL");
9603 read_met_bin_2d(in, met, met->cape, "CAPE");
9604 read_met_bin_2d(in, met, met->cin, "CIN");
9605 read_met_bin_2d(in, met, met->o3c, "O3C");
9606
9607 /* Read level data... */
9608 read_met_bin_3d(in, ctl, met, met->z, "Z", -1e34f, 1e34f);
9609 read_met_bin_3d(in, ctl, met, met->t, "T", 0, 1e34f);
9610 read_met_bin_3d(in, ctl, met, met->u, "U", -1e34f, 1e34f);
9611 read_met_bin_3d(in, ctl, met, met->v, "V", -1e34f, 1e34f);
9612 read_met_bin_3d(in, ctl, met, met->w, "W", -1e34f, 1e34f);
9613 read_met_bin_3d(in, ctl, met, met->pv, "PV", -1e34f, 1e34f);
9614 read_met_bin_3d(in, ctl, met, met->h2o, "H2O", 0, 1e34f);
9615 read_met_bin_3d(in, ctl, met, met->o3, "O3", 0, 1e34f);
9616 read_met_bin_3d(in, ctl, met, met->lwc, "LWC", 0, 1e34f);
9617 read_met_bin_3d(in, ctl, met, met->rwc, "RWC", 0, 1e34f);
9618 read_met_bin_3d(in, ctl, met, met->iwc, "IWC", 0, 1e34f);
9619 read_met_bin_3d(in, ctl, met, met->swc, "SWC", 0, 1e34f);
9620 read_met_bin_3d(in, ctl, met, met->cc, "CC", 0, 1);
9621
9622 /* Read final flag... */
9623 int final;
9624 FREAD(&final, int,
9625 1,
9626 in);
9627 if (final != 999)
9628 ERRMSG("Error while reading binary data!");
9629
9630 /* Close file... */
9631 fclose(in);
9632
9633 /* Return success... */
9634 return 1;
9635}
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:9639
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:9668
float zt[EX][EY]
Tropopause geopotential height [km].
Definition mptrac.h:3977
float sst[EX][EY]
Sea surface temperature [K].
Definition mptrac.h:3965
float rwc[EX][EY][EP]
Cloud rain water content [kg/kg].
Definition mptrac.h:4037
float o3c[EX][EY]
Total column ozone [DU].
Definition mptrac.h:4007
float cape[EX][EY]
Convective available potential energy [J/kg].
Definition mptrac.h:4001
float pct[EX][EY]
Cloud top pressure [hPa].
Definition mptrac.h:3983
float shf[EX][EY]
Surface sensible heat flux [W/m^2].
Definition mptrac.h:3959
float lwc[EX][EY][EP]
Cloud liquid water content [kg/kg].
Definition mptrac.h:4034
float us[EX][EY]
Surface zonal wind [m/s].
Definition mptrac.h:3947
float cc[EX][EY][EP]
Cloud cover [1].
Definition mptrac.h:4046
float ts[EX][EY]
Surface temperature [K].
Definition mptrac.h:3941
float ess[EX][EY]
Eastward turbulent surface stress [N/m^2].
Definition mptrac.h:3953
float pcb[EX][EY]
Cloud bottom pressure [hPa].
Definition mptrac.h:3986
float pel[EX][EY]
Pressure at equilibrium level (EL) [hPa].
Definition mptrac.h:3998
float cin[EX][EY]
Convective inhibition [J/kg].
Definition mptrac.h:4004
float plcl[EX][EY]
Pressure at lifted condensation level (LCL) [hPa].
Definition mptrac.h:3992
float tt[EX][EY]
Tropopause temperature [K].
Definition mptrac.h:3974
float pbl[EX][EY]
Boundary layer pressure [hPa].
Definition mptrac.h:3968
float vs[EX][EY]
Surface meridional wind [m/s].
Definition mptrac.h:3950
float lsm[EX][EY]
Land-sea mask [1].
Definition mptrac.h:3962
float iwc[EX][EY][EP]
Cloud ice water content [kg/kg].
Definition mptrac.h:4040
float h2ot[EX][EY]
Tropopause water vapor volume mixing ratio [ppv].
Definition mptrac.h:3980
float pv[EX][EY][EP]
Potential vorticity [PVU].
Definition mptrac.h:4025
float cl[EX][EY]
Total column cloud water [kg/m^2].
Definition mptrac.h:3989
float nss[EX][EY]
Northward turbulent surface stress [N/m^2].
Definition mptrac.h:3956
float plfc[EX][EY]
Pressure at level of free convection (LFC) [hPa].
Definition mptrac.h:3995
float swc[EX][EY][EP]
Cloud snow water content [kg/kg].
Definition mptrac.h:4043
Here is the call graph for this function:

◆ read_met_bin_2d()

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.

This function reads a 2-dimensional meteorological variable from a binary file, which is assumed to be uncompressed, and stores it in the provided 2-dimensional array var. The variable name is used for logging purposes to identify the data being read.

Parameters
inA pointer to the FILE structure representing the binary file to read from.
metA pointer to a structure containing meteorological data.
varA 2-dimensional array to store the read variable.
varnameA string containing the name of the variable being read.

The function performs the following steps:

  • Allocates memory for a temporary buffer to hold the uncompressed data.
  • Logs information about the variable being read from the file.
  • Reads the uncompressed data from the file into the temporary buffer.
  • Copies the data from the temporary buffer to the provided 2-dimensional array.
  • Frees the memory allocated for the temporary buffer.
Note
The function assumes that the binary file contains uncompressed data and reads the data directly into the provided array without any additional processing.
Author
Lars Hoffmann

Definition at line 9639 of file mptrac.c.

9643 {
9644
9645 float *help;
9646
9647 /* Allocate... */
9648 ALLOC(help, float,
9649 EX * EY);
9650
9651 /* Read uncompressed... */
9652 LOG(2, "Read 2-D variable: %s (uncompressed)", varname);
9653 FREAD(help, float,
9654 (size_t) (met->nx * met->ny),
9655 in);
9656
9657 /* Copy data... */
9658 for (int ix = 0; ix < met->nx; ix++)
9659 for (int iy = 0; iy < met->ny; iy++)
9660 var[ix][iy] = help[ARRAY_2D(ix, iy, met->ny)];
9661
9662 /* Free... */
9663 free(help);
9664}

◆ read_met_bin_3d()

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.

This function reads 3-dimensional meteorological data from a binary file into a specified variable array. The data can be read in uncompressed form or using one of several supported compression methods. The data is then clamped to specified minimum and maximum bounds.

Parameters
[in]inPointer to the input file from which to read the data.
[in]ctlPointer to the control structure that contains metadata about the type of data and how it is stored.
[in]metPointer to the meteorological structure that contains the dimensions of the data.
[out]var3D array to store the read data, with dimensions [EX][EY][EP].
[in]varnameName of the variable being read, used for logging and debugging.
[in]bound_minMinimum bound to which data values should be clamped.
[in]bound_maxMaximum bound to which data values should be clamped.

The function supports the following types of data:

  • Uncompressed data
  • Packed data
  • ZFP compressed data (if compiled with ZFP support)
  • ZSTD compressed data (if compiled with ZSTD support)
  • cmultiscale compressed data (if compiled with CMS support)

Depending on the compression type specified in the control structure, the appropriate reading and decompression function is used. The data is read into a temporary buffer, then copied into the output array, applying the specified bounds to each value.

Note
The function assumes that the dimensions EX, EY, and EP are correctly defined and match the dimensions specified in the met structure.
If the appropriate compression support is not compiled, an error message is generated.
Author
Lars Hoffmann

Definition at line 9668 of file mptrac.c.

9675 {
9676
9677 float *help;
9678
9679 /* Allocate... */
9680 ALLOC(help, float,
9681 EX * EY * EP);
9682
9683 /* Read uncompressed data... */
9684 if (ctl->met_type == 1) {
9685 LOG(2, "Read 3-D variable: %s (uncompressed)", varname);
9686 FREAD(help, float,
9687 (size_t) (met->nx * met->ny * met->np),
9688 in);
9689 }
9690
9691 /* Read packed data... */
9692 else if (ctl->met_type == 2)
9693 compress_pck(ctl, met, varname, help, 1, NULL, in);
9694
9695 /* Read ZFP data... */
9696 else if (ctl->met_type == 3) {
9697#ifdef ZFP
9698 int precision;
9699 FREAD(&precision, int,
9700 1,
9701 in);
9702
9703 double tolerance;
9704 FREAD(&tolerance, double,
9705 1,
9706 in);
9707
9708 compress_zfp(ctl, met, varname, help, 1, NULL, in);
9709#else
9710 ERRMSG("MPTRAC was compiled without ZFP compression!");
9711#endif
9712 }
9713
9714 /* Read zstd data... */
9715 else if (ctl->met_type == 4) {
9716#ifdef ZSTD
9717 compress_zstd(ctl, met, varname, help, 1, NULL, in);
9718#else
9719 ERRMSG("MPTRAC was compiled without ZSTD compression!");
9720#endif
9721 }
9722
9723 /* Read LZ4 data... */
9724 else if (ctl->met_type == 8) {
9725#ifdef LZ4
9726 compress_lz4(ctl, met, varname, help, 1, NULL, in);
9727#else
9728 ERRMSG("MPTRAC was compiled without LZ4 compression!");
9729#endif
9730 }
9731
9732 /* Read cmultiscale data... */
9733 else if (ctl->met_type == 5) {
9734#ifdef CMS
9735 compress_cms(ctl, met, varname, help, 1, NULL, in);
9736#else
9737 ERRMSG("MPTRAC was compiled without cmultiscale compression!");
9738#endif
9739 }
9740
9741 /* Read SZ3 data... */
9742 else if (ctl->met_type == 7) {
9743#ifdef SZ3
9744 int precision;
9745 FREAD(&precision, int,
9746 1,
9747 in);
9748
9749 double tolerance;
9750 FREAD(&tolerance, double,
9751 1,
9752 in);
9753
9754 compress_sz3(ctl, met, varname, help, 1, NULL, in);
9755#else
9756 ERRMSG("MPTRAC was compiled without sz3 compression!");
9757#endif
9758 }
9759
9760 /* Copy data... */
9761#pragma omp parallel for default(shared) collapse(2)
9762 for (int ix = 0; ix < met->nx; ix++)
9763 for (int iy = 0; iy < met->ny; iy++)
9764 for (int ip = 0; ip < met->np; ip++) {
9765 var[ix][iy][ip] = help[ARRAY_3D(ix, iy, met->ny, ip, met->np)];
9766 if (var[ix][iy][ip] < bound_min)
9767 var[ix][iy][ip] = bound_min;
9768 else if (var[ix][iy][ip] > bound_max)
9769 var[ix][iy][ip] = bound_max;
9770 }
9771
9772 /* Free... */
9773 free(help);
9774}
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 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.
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.
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.
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.
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.
Here is the call graph for this function:

◆ read_met_cape()

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.

This function calculates the Convective Available Potential Energy (CAPE) at each grid point based on the provided meteorological data. CAPE is a measure of the energy available for deep convection, which is essential for severe weather development.

Parameters
ctlPointer to the control structure that contains metadata about the type of data and how it is stored.
climA pointer to a structure containing climatological data.
metA pointer to a structure containing meteorological data.

The function performs the following steps:

  • Sets up a timer to monitor the calculation time.
  • Initializes variables and constants required for the computation, such as vertical spacing and pressure levels.
  • Iterates over each grid point in parallel using OpenMP.
  • Calculates CAPE by integrating the difference in virtual temperatures between the environment and the parcel, up to the level of free convection (LFC).
  • Determines the lifted condensation level (LCL), level of free convection (LFC), equilibrium level (EL), and Convective Inhibition (CIN) for each grid point.
  • Checks the results and updates the corresponding fields in the meteorological data structure.
Note
The function utilizes OpenMP for parallelization to enhance performance by distributing the computation across multiple threads.
Author
Lars Hoffmann

Definition at line 9778 of file mptrac.c.

9781 {
9782
9783 /* Check parameters... */
9784 if (ctl->met_cape != 1)
9785 return;
9786
9787 if (ctl->met_coord_type != 0)
9788 ERRMSG("Only lat/lon grid supported");
9789
9790 /* Set timer... */
9791 SELECT_TIMER("READ_MET_CAPE", "METPROC");
9792 LOG(2, "Calculate CAPE...");
9793
9794 /* Vertical spacing (about 100 m)... */
9795 const double pfac = 1.01439, dz0 = RI / MA / G0 * log(pfac);
9796
9797 /* Loop over columns... */
9798#pragma omp parallel for default(shared) collapse(2)
9799 for (int ix = 0; ix < met->nx; ix++)
9800 for (int iy = 0; iy < met->ny; iy++) {
9801
9802 /* Get potential temperature and water vapor at lowest 50 hPa... */
9803 int n = 0;
9804 double h2o = 0, t, theta = 0;
9805 double pbot = MIN(met->ps[ix][iy], met->p[0]);
9806 double ptop = pbot - 50.;
9807 for (int ip = 0; ip < met->np; ip++) {
9808 if (met->p[ip] <= pbot) {
9809 theta += THETA(met->p[ip], met->t[ix][iy][ip]);
9810 h2o += met->h2o[ix][iy][ip];
9811 n++;
9812 }
9813 if (met->p[ip] < ptop && n > 0)
9814 break;
9815 }
9816 theta /= n;
9817 h2o /= n;
9818
9819 /* Cannot compute anything if water vapor is missing... */
9820 met->plcl[ix][iy] = NAN;
9821 met->plfc[ix][iy] = NAN;
9822 met->pel[ix][iy] = NAN;
9823 met->cape[ix][iy] = NAN;
9824 met->cin[ix][iy] = NAN;
9825 if (h2o <= 0)
9826 continue;
9827
9828 /* Find lifted condensation level (LCL)... */
9829 ptop = P(20.);
9830 pbot = met->ps[ix][iy];
9831 do {
9832 met->plcl[ix][iy] = (float) (0.5 * (pbot + ptop));
9833 t = theta / pow(1000. / met->plcl[ix][iy], KAPPA);
9834 if (RH(met->plcl[ix][iy], t, h2o) > 100.)
9835 ptop = met->plcl[ix][iy];
9836 else
9837 pbot = met->plcl[ix][iy];
9838 } while (pbot - ptop > 0.1);
9839
9840 /* Calculate CIN up to LCL... */
9842 double dcape, dz, h2o_env, t_env;
9843 double p = met->ps[ix][iy];
9844 met->cape[ix][iy] = met->cin[ix][iy] = 0;
9845 do {
9846 dz = dz0 * TVIRT(t, h2o);
9847 p /= pfac;
9848 t = theta / pow(1000. / p, KAPPA);
9849 intpol_met_space_3d(met, met->t, p, met->lon[ix], met->lat[iy],
9850 &t_env, ci, cw, 1);
9851 intpol_met_space_3d(met, met->h2o, p, met->lon[ix], met->lat[iy],
9852 &h2o_env, ci, cw, 0);
9853 dcape = 1e3 * G0 * (TVIRT(t, h2o) - TVIRT(t_env, h2o_env)) /
9854 TVIRT(t_env, h2o_env) * dz;
9855 if (dcape < 0)
9856 met->cin[ix][iy] += fabsf((float) dcape);
9857 } while (p > met->plcl[ix][iy]);
9858
9859 /* Calculate level of free convection (LFC), equilibrium level (EL),
9860 and convective available potential energy (CAPE)... */
9861 dcape = 0;
9862 p = met->plcl[ix][iy];
9863 t = theta / pow(1000. / p, KAPPA);
9864 ptop = 0.75 * clim_tropo(clim, met->time,
9865 ctl->met_coord_type ==
9866 0 ? met->lat[iy] : ctl->met_utm_ref_lat);
9867 do {
9868 dz = dz0 * TVIRT(t, h2o);
9869 p /= pfac;
9870 t -= lapse_rate(t, h2o) * dz;
9871 double psat = PSAT(t);
9872 h2o = psat / (p - (1. - EPS) * psat);
9873 intpol_met_space_3d(met, met->t, p, met->lon[ix], met->lat[iy],
9874 &t_env, ci, cw, 1);
9875 intpol_met_space_3d(met, met->h2o, p, met->lon[ix], met->lat[iy],
9876 &h2o_env, ci, cw, 0);
9877 double dcape_old = dcape;
9878 dcape = 1e3 * G0 * (TVIRT(t, h2o) - TVIRT(t_env, h2o_env)) /
9879 TVIRT(t_env, h2o_env) * dz;
9880 if (dcape > 0) {
9881 met->cape[ix][iy] += (float) dcape;
9882 if (!isfinite(met->plfc[ix][iy]))
9883 met->plfc[ix][iy] = (float) p;
9884 } else if (dcape_old > 0)
9885 met->pel[ix][iy] = (float) p;
9886 if (dcape < 0 && !isfinite(met->plfc[ix][iy]))
9887 met->cin[ix][iy] += fabsf((float) dcape);
9888 } while (p > ptop);
9889
9890 /* Check results... */
9891 if (!isfinite(met->plfc[ix][iy]))
9892 met->cin[ix][iy] = NAN;
9893 }
9894}
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
Here is the call graph for this function:

◆ read_met_cloud()

void read_met_cloud ( met_t * met)

Calculates cloud-related variables for each grid point.

This function calculates cloud-related variables, such as cloud cover, cloud top pressure, cloud bottom pressure, and total cloud water content, based on the provided meteorological data.

Parameters
metA pointer to a structure containing meteorological data.

The function performs the following steps:

  • Sets up a timer to monitor the calculation time.
  • Initializes variables and constants required for the computation.
  • Iterates over each grid point in parallel using OpenMP.
  • Determines cloud-related variables based on thresholds for liquid water content (LWC), rain water content (RWC), ice water content (IWC) and snow water content (SWC).
  • Calculates cloud cover, cloud top pressure, cloud bottom pressure, and total cloud water content for each grid point.
  • Updates the corresponding fields in the meteorological data structure.
Note
The function utilizes OpenMP for parallelization to enhance performance by distributing the computation across multiple threads.
Author
Lars Hoffmann

Definition at line 9898 of file mptrac.c.

9899 {
9900
9901 /* Set timer... */
9902 SELECT_TIMER("READ_MET_CLOUD", "METPROC");
9903 LOG(2, "Calculate cloud data...");
9904
9905 /* Thresholds for cloud detection... */
9906 const double ccmin = 0.01, cwmin = 1e-6;
9907
9908 /* Loop over columns... */
9909#pragma omp parallel for default(shared) collapse(2)
9910 for (int ix = 0; ix < met->nx; ix++)
9911 for (int iy = 0; iy < met->ny; iy++) {
9912
9913 /* Init... */
9914 met->pct[ix][iy] = NAN;
9915 met->pcb[ix][iy] = NAN;
9916 met->cl[ix][iy] = 0;
9917
9918 /* Loop over pressure levels... */
9919 for (int ip = 0; ip < met->np - 1; ip++) {
9920
9921 /* Check pressure... */
9922 if (met->p[ip] > met->ps[ix][iy] || met->p[ip] < P(20.))
9923 continue;
9924
9925 /* Check ice water and liquid water content... */
9926 if (met->cc[ix][iy][ip] > ccmin
9927 && (met->lwc[ix][iy][ip] > cwmin
9928 || met->rwc[ix][iy][ip] > cwmin
9929 || met->iwc[ix][iy][ip] > cwmin
9930 || met->swc[ix][iy][ip] > cwmin)) {
9931
9932 /* Get cloud top pressure ... */
9933 met->pct[ix][iy]
9934 = (float) (0.5 * (met->p[ip] + (float) met->p[ip + 1]));
9935
9936 /* Get cloud bottom pressure ... */
9937 if (!isfinite(met->pcb[ix][iy]))
9938 met->pcb[ix][iy]
9939 = (float) (0.5 * (met->p[ip] + met->p[MAX(ip - 1, 0)]));
9940 }
9941
9942 /* Get cloud water... */
9943 met->cl[ix][iy] += (float)
9944 (0.5 * (met->lwc[ix][iy][ip] + met->lwc[ix][iy][ip + 1]
9945 + met->rwc[ix][iy][ip] + met->rwc[ix][iy][ip + 1]
9946 + met->iwc[ix][iy][ip] + met->iwc[ix][iy][ip + 1]
9947 + met->swc[ix][iy][ip] + met->swc[ix][iy][ip + 1])
9948 * 100. * (met->p[ip] - met->p[ip + 1]) / G0);
9949 }
9950 }
9951}

◆ read_met_detrend()

void read_met_detrend ( const ctl_t * ctl,
met_t * met )

Detrends meteorological data.

This function detrends meteorological data by removing spatially varying backgrounds from each grid point. Detrending helps in removing systematic biases and trends from the data, enabling better analysis and modeling.

Parameters
ctlA pointer to a structure containing control parameters.
metA pointer to a structure containing meteorological data.

The function performs the following steps:

  • Checks if detrending is enabled based on the control parameters.
  • Sets up a timer to monitor the detrending time.
  • Allocates memory for a temporary meteorological data structure.
  • Calculates the standard deviation and box size for detrending.
  • Calculates the detrended data by subtracting spatially varying backgrounds.
  • Updates the original meteorological data with the detrended values.
  • Frees the allocated memory.
Note
Detrending is performed by subtracting spatially varying backgrounds calculated from neighboring grid points.
OpenMP is utilized for parallelization to enhance performance by distributing the computation across multiple threads.
Author
Lars Hoffmann

Definition at line 9955 of file mptrac.c.

9957 {
9958
9959 met_t *help;
9960
9961 /* Check parameters... */
9962 if (ctl->met_detrend <= 0)
9963 return;
9964
9965 if (ctl->met_coord_type != 0)
9966 ERRMSG("Only lat/lon grid supported");
9967
9968 /* Set timer... */
9969 SELECT_TIMER("READ_MET_DETREND", "METPROC");
9970 LOG(2, "Detrend meteo data...");
9971
9972 /* Allocate... */
9973 ALLOC(help, met_t, 1);
9974
9975 /* Calculate standard deviation... */
9976 const double sigma = ctl->met_detrend / 2.355;
9977 const double tssq = 2. * SQR(sigma);
9978
9979 /* Calculate box size in latitude... */
9980 int sy = (int) (3. * DY2DEG(sigma) / fabs(met->lat[1] - met->lat[0]));
9981 sy = MIN(MAX(1, sy), met->ny / 2);
9982
9983 /* Calculate background... */
9984#pragma omp parallel for default(shared) collapse(2)
9985 for (int ix = 0; ix < met->nx; ix++) {
9986 for (int iy = 0; iy < met->ny; iy++) {
9987
9988 /* Calculate Cartesian coordinates... */
9989 double x0[3];
9990 geo2cart(0.0, met->lon[ix], met->lat[iy], x0);
9991
9992 /* Calculate box size in longitude... */
9993 int sx =
9994 (int) (3. * DX2DEG(sigma, met->lat[iy]) /
9995 fabs(met->lon[1] - met->lon[0]));
9996 sx = MIN(MAX(1, sx), met->nx / 2);
9997
9998 /* Init... */
9999 float wsum = 0;
10000 for (int ip = 0; ip < met->np; ip++) {
10001 help->t[ix][iy][ip] = 0;
10002 help->u[ix][iy][ip] = 0;
10003 help->v[ix][iy][ip] = 0;
10004 help->w[ix][iy][ip] = 0;
10005 }
10006
10007 /* Loop over neighboring grid points... */
10008 for (int ix2 = ix - sx; ix2 <= ix + sx; ix2++) {
10009 int ix3 = ix2;
10010 if (ix3 < 0)
10011 ix3 += met->nx;
10012 else if (ix3 >= met->nx)
10013 ix3 -= met->nx;
10014 for (int iy2 = MAX(iy - sy, 0);
10015 iy2 <= MIN(iy + sy, met->ny - 1); iy2++) {
10016
10017 /* Calculate Cartesian coordinates... */
10018 double x1[3];
10019 geo2cart(0.0, met->lon[ix3], met->lat[iy2], x1);
10020
10021 /* Calculate weighting factor... */
10022 const float w = (float) exp(-DIST2(x0, x1) / tssq);
10023
10024 /* Add data... */
10025 wsum += w;
10026 for (int ip = 0; ip < met->np; ip++) {
10027 help->t[ix][iy][ip] += w * met->t[ix3][iy2][ip];
10028 help->u[ix][iy][ip] += w * met->u[ix3][iy2][ip];
10029 help->v[ix][iy][ip] += w * met->v[ix3][iy2][ip];
10030 help->w[ix][iy][ip] += w * met->w[ix3][iy2][ip];
10031 }
10032 }
10033 }
10034
10035 /* Normalize... */
10036 for (int ip = 0; ip < met->np; ip++) {
10037 help->t[ix][iy][ip] /= wsum;
10038 help->u[ix][iy][ip] /= wsum;
10039 help->v[ix][iy][ip] /= wsum;
10040 help->w[ix][iy][ip] /= wsum;
10041 }
10042 }
10043 }
10044
10045 /* Subtract background... */
10046#pragma omp parallel for default(shared) collapse(3)
10047 for (int ix = 0; ix < met->nx; ix++)
10048 for (int iy = 0; iy < met->ny; iy++)
10049 for (int ip = 0; ip < met->np; ip++) {
10050 met->t[ix][iy][ip] -= help->t[ix][iy][ip];
10051 met->u[ix][iy][ip] -= help->u[ix][iy][ip];
10052 met->v[ix][iy][ip] -= help->v[ix][iy][ip];
10053 met->w[ix][iy][ip] -= help->w[ix][iy][ip];
10054 }
10055
10056 /* Free... */
10057 free(help);
10058}
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:2907
#define DX2DEG(dx, lat)
Convert a distance in kilometers to degrees longitude at a given latitude.
Definition mptrac.h:906
#define DIST2(a, b)
Calculate the squared Euclidean distance between two points in Cartesian coordinates.
Definition mptrac.h:1022
#define DY2DEG(dy)
Convert a distance in kilometers to degrees latitude.
Definition mptrac.h:924
Here is the call graph for this function:

◆ read_met_extrapolate()

void read_met_extrapolate ( met_t * met)

Extrapolates meteorological data.

This function extrapolates meteorological data by filling missing or invalid data points with values from the nearest valid point above. Extrapolation is performed column-wise, ensuring that missing data points are replaced with valid values.

Parameters
metA pointer to a structure containing meteorological data.

The function performs the following steps:

  • Sets up a timer to monitor the extrapolation time.
  • Loops over each grid column in parallel.
  • Finds the lowest valid data point within each column.
  • Extrapolates missing or invalid data points by copying values from the nearest valid point above.
  • Updates the meteorological data structure with the extrapolated values.
Note
Extrapolation is performed by copying values from the nearest valid point above to fill missing or invalid data points. OpenMP is utilized for parallelization to enhance performance by distributing the computation across multiple threads.
Author
Lars Hoffmann

Definition at line 10062 of file mptrac.c.

10063 {
10064
10065 /* Set timer... */
10066 SELECT_TIMER("READ_MET_EXTRAPOLATE", "METPROC");
10067 LOG(2, "Extrapolate meteo data...");
10068
10069 /* Loop over columns... */
10070#pragma omp parallel for default(shared) collapse(2)
10071 for (int ix = 0; ix < met->nx; ix++)
10072 for (int iy = 0; iy < met->ny; iy++) {
10073
10074 /* Find lowest valid data point... */
10075 int ip0;
10076 for (ip0 = met->np - 1; ip0 >= 0; ip0--)
10077 if (!isfinite(met->t[ix][iy][ip0])
10078 || !isfinite(met->u[ix][iy][ip0])
10079 || !isfinite(met->v[ix][iy][ip0])
10080 || !isfinite(met->w[ix][iy][ip0]))
10081 break;
10082
10083 /* Extrapolate... */
10084 for (int ip = ip0; ip >= 0; ip--) {
10085 met->t[ix][iy][ip] = met->t[ix][iy][ip + 1];
10086 met->u[ix][iy][ip] = met->u[ix][iy][ip + 1];
10087 met->v[ix][iy][ip] = met->v[ix][iy][ip + 1];
10088 met->w[ix][iy][ip] = met->w[ix][iy][ip + 1];
10089 met->h2o[ix][iy][ip] = met->h2o[ix][iy][ip + 1];
10090 met->o3[ix][iy][ip] = met->o3[ix][iy][ip + 1];
10091 met->lwc[ix][iy][ip] = met->lwc[ix][iy][ip + 1];
10092 met->rwc[ix][iy][ip] = met->rwc[ix][iy][ip + 1];
10093 met->iwc[ix][iy][ip] = met->iwc[ix][iy][ip + 1];
10094 met->swc[ix][iy][ip] = met->swc[ix][iy][ip + 1];
10095 met->cc[ix][iy][ip] = met->cc[ix][iy][ip + 1];
10096 }
10097 }
10098}

◆ read_met_geopot()

void read_met_geopot ( const ctl_t * ctl,
met_t * met )

Calculates geopotential heights from meteorological data.

This function calculates geopotential heights from provided meteorological data using the hydrostatic equation. Geopotential heights are computed column-wise for each grid point based on the temperature, pressure, and surface height information. Optionally, the calculated geopotential heights can be smoothed horizontally using a weighted averaging scheme.

Parameters
ctlA pointer to a structure containing control parameters.
metA pointer to a structure containing meteorological data.

The function performs the following steps:

  • Sets up a timer to monitor the geopotential height calculation time.
  • Calculates the logarithm of pressure levels for efficient computation.
  • Applies the hydrostatic equation to determine geopotential heights based on temperature, pressure, and height information.
  • Optionally, performs horizontal smoothing on the calculated geopotential heights.
  • Updates the meteorological data structure with the computed geopotential heights.
Note
The hydrostatic equation is utilized to calculate geopotential heights, ensuring consistency with atmospheric conditions. Optionally, horizontal smoothing can be applied to the calculated geopotential heights to reduce spatial variability. OpenMP is utilized for parallelization to enhance performance by distributing the computation across multiple threads.
Author
Lars Hoffmann

Definition at line 10102 of file mptrac.c.

10104 {
10105
10106 float *help;
10107
10108 double logp[EP];
10109
10110 int dx = ctl->met_geopot_sx, dy = ctl->met_geopot_sy;
10111
10112 /* Set timer... */
10113 SELECT_TIMER("READ_MET_GEOPOT", "METPROC");
10114 LOG(2, "Calculate geopotential heights...");
10115
10116 /* Allocate... */
10117 ALLOC(help, float,
10118 EX * EY * EP);
10119
10120 /* Calculate log pressure... */
10121#pragma omp parallel for default(shared)
10122 for (int ip = 0; ip < met->np; ip++)
10123 logp[ip] = log(met->p[ip]);
10124
10125 /* Apply hydrostatic equation to calculate geopotential heights... */
10126#pragma omp parallel for default(shared) collapse(2)
10127 for (int ix = 0; ix < met->nx; ix++)
10128 for (int iy = 0; iy < met->ny; iy++) {
10129
10130 /* Get surface height and pressure... */
10131 const double zs = met->zs[ix][iy];
10132 const double lnps = log(met->ps[ix][iy]);
10133
10134 /* Get temperature and water vapor at the surface... */
10135 const int ip0 = locate_irr(met->p, met->np, met->ps[ix][iy]);
10136 const double ts = LIN(met->p[ip0], met->t[ix][iy][ip0], met->p[ip0 + 1],
10137 met->t[ix][iy][ip0 + 1], met->ps[ix][iy]);
10138 const double h2os =
10139 LIN(met->p[ip0], met->h2o[ix][iy][ip0], met->p[ip0 + 1],
10140 met->h2o[ix][iy][ip0 + 1], met->ps[ix][iy]);
10141
10142 /* Upper part of profile... */
10143 met->z[ix][iy][ip0 + 1]
10144 = (float) (zs +
10145 ZDIFF(lnps, ts, h2os, logp[ip0 + 1],
10146 met->t[ix][iy][ip0 + 1], met->h2o[ix][iy][ip0 + 1]));
10147 for (int ip = ip0 + 2; ip < met->np; ip++)
10148 met->z[ix][iy][ip]
10149 = (float) (met->z[ix][iy][ip - 1] +
10150 ZDIFF(logp[ip - 1], met->t[ix][iy][ip - 1],
10151 met->h2o[ix][iy][ip - 1], logp[ip],
10152 met->t[ix][iy][ip], met->h2o[ix][iy][ip]));
10153
10154 /* Lower part of profile... */
10155 met->z[ix][iy][ip0]
10156 = (float) (zs +
10157 ZDIFF(lnps, ts, h2os, logp[ip0],
10158 met->t[ix][iy][ip0], met->h2o[ix][iy][ip0]));
10159 for (int ip = ip0 - 1; ip >= 0; ip--)
10160 met->z[ix][iy][ip]
10161 = (float) (met->z[ix][iy][ip + 1] +
10162 ZDIFF(logp[ip + 1], met->t[ix][iy][ip + 1],
10163 met->h2o[ix][iy][ip + 1], logp[ip],
10164 met->t[ix][iy][ip], met->h2o[ix][iy][ip]));
10165 }
10166
10167 /* Check control parameters... */
10168 if (dx == 0 || dy == 0)
10169 return;
10170
10171 /* Default smoothing parameters... */
10172 if (dx < 0 || dy < 0) {
10173 if (fabs(met->lon[1] - met->lon[0]) < 0.5) {
10174 dx = 3;
10175 dy = 2;
10176 } else {
10177 dx = 6;
10178 dy = 4;
10179 }
10180 }
10181
10182 /* Calculate weights for smoothing... */
10183 float ws[dx + 1][dy + 1];
10184#pragma omp parallel for default(shared) collapse(2)
10185 for (int ix = 0; ix <= dx; ix++)
10186 for (int iy = 0; iy < dy; iy++)
10187 ws[ix][iy] = (1.0f - (float) ix / (float) dx)
10188 * (1.0f - (float) iy / (float) dy);
10189
10190 /* Copy data... */
10191#pragma omp parallel for default(shared) collapse(3)
10192 for (int ix = 0; ix < met->nx; ix++)
10193 for (int iy = 0; iy < met->ny; iy++)
10194 for (int ip = 0; ip < met->np; ip++)
10195 help[ARRAY_3D(ip, ix, met->nx, iy, met->ny)] = met->z[ix][iy][ip];
10196
10197 /* Horizontal smoothing... */
10198#pragma omp parallel for default(shared) collapse(3)
10199 for (int ip = 0; ip < met->np; ip++)
10200 for (int ix = 0; ix < met->nx; ix++)
10201 for (int iy = 0; iy < met->ny; iy++) {
10202 float res = 0, wsum = 0;
10203 int iy0 = MAX(iy - dy + 1, 0);
10204 int iy1 = MIN(iy + dy - 1, met->ny - 1);
10205 for (int ix2 = ix - dx + 1; ix2 <= ix + dx - 1; ++ix2) {
10206 int ix3 = ix2;
10207 if (ix3 < 0)
10208 ix3 += met->nx;
10209 else if (ix3 >= met->nx)
10210 ix3 -= met->nx;
10211 for (int iy2 = iy0; iy2 <= iy1; ++iy2)
10212 if (isfinite(help[ARRAY_3D(ip, ix3, met->nx, iy2, met->ny)])) {
10213 float w = ws[abs(ix - ix2)][abs(iy - iy2)];
10214 res += w * help[ARRAY_3D(ip, ix3, met->nx, iy2, met->ny)];
10215 wsum += w;
10216 }
10217 }
10218 if (wsum > 0)
10219 met->z[ix][iy][ip] = res / wsum;
10220 else
10221 met->z[ix][iy][ip] = NAN;
10222 }
10223
10224 /* Free... */
10225 free(help);
10226}
#define ZDIFF(lnp0, t0, h2o0, lnp1, t1, h2o1)
Calculate geopotential height difference.
Definition mptrac.h:2294
Here is the call graph for this function:

◆ read_met_nc_grid()

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.

The read_met_nc_grid function reads meteorological data from NetCDF files and processes it with domain decomposition for parallel processing. It extracts time information, grid dimensions, and coordinates, and sets up hyperslabs for subdomains and halos. It also reads pressure levels and handles model level and surface data.

Parameters
filenameA string representing the filename of the NetCDF file to read.
ncidA NetCDF file ID.
ctlA pointer to a ctl_t structure containing control parameters.
metA pointer to a met_t structure where meteorological data will be stored.
ddA pointer to an dd_t structure containing MPI information, including rank and neighbours.

The function performs the following steps:

  • Sets filenames for meteorological data files.
  • Extracts time information from the filename or NetCDF file.
  • Validates the time information and logs it.
  • Retrieves global and local grid dimensions and checks for regular grid spacing.
  • Sets up hyperslabs for subdomains and halos, considering edge cases.
  • Adjusts grid dimensions and coordinates for subdomains and halos.
  • Reads pressure levels and computes the 3D pressure field.
  • Handles model level and surface data using GRIB handles.
  • Reads grid data and surface data from the respective files.
  • Computes the 3D pressure field and reads model level data.
Note
This function assumes that the input filename and structures are properly initialized. It uses MPI for parallel processing and handles domain decomposition. The function is designed to work with NetCDF and GRIB file formats. It logs various stages of processing for debugging and validation purposes.
Author
Lars Hoffmann
Jan Clemens

Definition at line 10230 of file mptrac.c.

10235 {
10236
10237 char levname[LEN], tstr[10];
10238
10239 double rtime = 0, r, r2;
10240
10241 int varid, ndims, dimids[NC_MAX_DIMS], year2, mon2, day2, hour2, min2, sec2,
10242 year, mon, day, hour, min, sec;
10243
10244 size_t dimlen;
10245
10246 /* Set timer... */
10247 SELECT_TIMER("READ_MET_NC_GRID", "INPUT");
10248 LOG(2, "Read meteo grid information...");
10249
10250 /* MPTRAC meteo files... */
10251 if (!ctl->met_clams) {
10252
10253 /* Get time from filename... */
10254 met->time = time_from_filename(filename, 16, 0);
10255
10256 /* Check time information from data file... */
10257 jsec2time(met->time, &year, &mon, &day, &hour, &min, &sec, &r);
10258 if (nc_inq_varid(ncid, "time", &varid) == NC_NOERR) {
10259 NC(nc_get_var_double(ncid, varid, &rtime));
10260 if (fabs(year * 10000. + mon * 100. + day + hour / 24. - rtime) > 1.0)
10261 WARN("Time information in meteo file does not match filename!");
10262 } else
10263 WARN("Time information in meteo file is missing!");
10264 }
10265
10266 /* CLaMS meteo files... */
10267 else {
10268
10269 /* Read time from file... */
10270 NC_GET_DOUBLE("time", &rtime, 0);
10271
10272 /* Get time from filename (considering the century)... */
10273 if (rtime < 0)
10274 sprintf(tstr, "19%.2s", &filename[strlen(filename) - 11]);
10275 else
10276 sprintf(tstr, "20%.2s", &filename[strlen(filename) - 11]);
10277 year = atoi(tstr);
10278 sprintf(tstr, "%.2s", &filename[strlen(filename) - 9]);
10279 mon = atoi(tstr);
10280 sprintf(tstr, "%.2s", &filename[strlen(filename) - 7]);
10281 day = atoi(tstr);
10282 sprintf(tstr, "%.2s", &filename[strlen(filename) - 5]);
10283 hour = atoi(tstr);
10284 time2jsec(year, mon, day, hour, 0, 0, 0, &met->time);
10285 }
10286
10287 /* Check time... */
10288 if (year < 1900 || year > 2100 || mon < 1 || mon > 12
10289 || day < 1 || day > 31 || hour < 0 || hour > 23)
10290 ERRMSG("Cannot read time from filename!");
10291 jsec2time(met->time, &year2, &mon2, &day2, &hour2, &min2, &sec2, &r2);
10292 LOG(2, "Time: %.2f (%d-%02d-%02d, %02d:%02d UTC)",
10293 met->time, year2, mon2, day2, hour2, min2);
10294
10295 /* Get vertical dimension... */
10296 if (nc_inq_varid(ncid, "u", &varid) != NC_NOERR)
10297 if (nc_inq_varid(ncid, "U", &varid) != NC_NOERR)
10298 ERRMSG
10299 ("Variable 'u' or 'U' not found, cannot determine vertical dimension!");
10300
10301 NC(nc_inq_varndims(ncid, varid, &ndims));
10302 NC(nc_inq_vardimid(ncid, varid, dimids));
10303
10304 if (ndims == 4) {
10305 NC(nc_inq_dim
10306 (ncid, dimids[ctl->met_convention == 0 ? 1 : 3], levname, &dimlen));
10307 } else if (ndims == 3) {
10308 NC(nc_inq_dim
10309 (ncid, dimids[ctl->met_convention == 0 ? 0 : 2], levname, &dimlen));
10310 } else
10311 ERRMSG("Cannot determine vertical dimension!")
10312 met->np = (int) dimlen;
10313
10314 LOG(2, "Number of levels: %d", met->np);
10315 if (met->np < 2 || met->np > EP)
10316 ERRMSG("Number of levels out of range!");
10317
10318 if (!ctl->dd) {
10319
10320 /* Get grid dimensions and coordinates... */
10321 if (met->coord_type == 0) {
10322 /* Longitude/latitude grid... */
10323 NC_INQ_DIM("lon", &met->nx, 2, EX, 1);
10324 LOG(2, "Number of longitudes: %d", met->nx);
10325
10326 NC_INQ_DIM("lat", &met->ny, 2, EY, 1);
10327 LOG(2, "Number of latitudes: %d", met->ny);
10328
10329 NC_GET_DOUBLE("lon", met->lon, 1);
10330 LOG(2, "Longitudes: %g, %g ... %g deg",
10331 met->lon[0], met->lon[1], met->lon[met->nx - 1]);
10332 NC_GET_DOUBLE("lat", met->lat, 1);
10333 LOG(2, "Latitudes: %g, %g ... %g deg",
10334 met->lat[0], met->lat[1], met->lat[met->ny - 1]);
10335
10336 } else {
10337 /* UTM grid... */
10338 NC_INQ_DIM("x", &met->nx, 2, EX, 1);
10339 LOG(2, "Number of x coordinates: %d", met->nx);
10340
10341 NC_INQ_DIM("y", &met->ny, 2, EY, 1);
10342 LOG(2, "Number of y coordinates: %d", met->ny);
10343
10344 NC_GET_DOUBLE("x", met->lon, 1);
10345 LOG(2, "X coordinates: %g, %g ... %g m",
10346 met->lon[0], met->lon[1], met->lon[met->nx - 1]);
10347 NC_GET_DOUBLE("y", met->lat, 1);
10348 LOG(2, "Y coordinates: %g, %g ... %g m",
10349 met->lat[0], met->lat[1], met->lat[met->ny - 1]);
10350 }
10351
10352 } else {
10353
10354 if (met->coord_type != 0)
10355 ERRMSG("Domain decomposition is only supported for lat/lon grids!");
10356
10357 /* Use equidistant lat-lon domain decomposition... */
10358 dd_read_met_nc_grid(dd, ctl, met, ncid);
10359
10360 }
10361
10362 /* Read pressure levels... */
10363 if (ctl->met_np <= 0) {
10364 NC_GET_DOUBLE(levname, met->p, 1);
10365 for (int ip = 0; ip < met->np; ip++)
10366 met->p[ip] /= 100.;
10367 LOG(2, "Altitude levels: %g, %g ... %g km",
10368 Z(met->p[0]), Z(met->p[1]), Z(met->p[met->np - 1]));
10369 LOG(2, "Pressure levels: %g, %g ... %g hPa",
10370 met->p[0], met->p[1], met->p[met->np - 1]);
10371 }
10372
10373 /* Read hybrid levels... */
10374 if (strcasecmp(levname, "hybrid") == 0)
10375 NC_GET_DOUBLE("hybrid", met->hybrid, 1);
10376
10377 /* Read model level coefficients from file... */
10378 if (ctl->met_vert_coord == 2) {
10379 NC_GET_DOUBLE("hyam", met->hyam, 1);
10380 NC_GET_DOUBLE("hybm", met->hybm, 1);
10381 }
10382
10383 /* Copy model level coefficients from control parameters... */
10384 else if (ctl->met_vert_coord == 3 || ctl->met_vert_coord == 4) {
10385 if (ctl->met_nlev <= 0)
10386 ERRMSG("You need to specify MET_NLEV, MET_LEV_HYAM, and MET_LEV_HYBM!");
10387 for (int ip = 0; ip < ctl->met_nlev; ip++) {
10388 met->hyam[ip] = ctl->met_lev_hyam[ip];
10389 met->hybm[ip] = ctl->met_lev_hybm[ip];
10390 }
10391 }
10392
10393 /* Calculate eta levels... */
10394 for (int k = 0; k < MAX(met->np, ctl->met_nlev); ++k) {
10395 met->eta[k] = met->hyam[k] / (100.0 * P0) + met->hybm[k];
10396 if (ctl->met_vert_coord >= 2 && k > 0 && met->eta[k] <= met->eta[k - 1])
10397 ERRMSG("Eta levels must be ascending!");
10398 }
10399
10400 /* Check horizontal grid spacing... */
10401 for (int ix = 2; ix < met->nx; ix++)
10402 if (fabs
10403 (fabs(met->lon[ix] - met->lon[ix - 1]) -
10404 fabs(met->lon[1] - met->lon[0])) > 0.001)
10405 ERRMSG("No regular grid spacing in longitudes!");
10406 for (int iy = 2; iy < met->ny; iy++)
10407 if (fabs
10408 (fabs(met->lat[iy] - met->lat[iy - 1]) -
10409 fabs(met->lat[1] - met->lat[0])) > 0.001) {
10410 WARN("No regular grid spacing in latitudes!");
10411 break;
10412 }
10413}
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:12282
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:13505
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:13604
#define P0
Standard pressure [hPa].
Definition mptrac.h:305
double hybrid[EP]
Model hybrid levels.
Definition mptrac.h:3926
double eta[EP]
Model level eta values.
Definition mptrac.h:3935
double hyam[EP]
Model level a coefficients [Pa].
Definition mptrac.h:3929
double hybm[EP]
Model level b coefficients.
Definition mptrac.h:3932
Here is the call graph for this function:

◆ read_met_nc_surface()

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.

The read_met_nc_surface function reads surface meteorological data from a NetCDF file and processes it for use in a domain decomposition context. It handles various surface parameters such as pressure, geopotential height, temperature, wind components, and other relevant meteorological data. The function is designed to work with different meteorological data formats and configurations.

Parameters
ncidAn integer representing the NetCDF file ID.
ctlA pointer to a ctl_t structure containing control parameters and settings.
metA pointer to a met_t structure where surface meteorological data will be stored.
ddA pointer to an dd_t structure containing MPI information, including rank and neighbours.

The function performs the following steps:

  • Reads surface pressure data and converts it if necessary.
  • Handles different data formats for MPTRAC and CLaMS meteorological data.
  • Reads geopotential height at the surface and processes it based on the data format.
  • Optionally converts zm surface geopotential data from m^2 s^-2 to km when MET_GP2Z = 1.
  • Retrieves surface temperature, zonal and meridional wind, and other surface parameters.
  • Logs warnings if specific data fields cannot be read.
  • Uses helper functions to read 2D and 3D data fields from the NetCDF file.
  • Processes and stores the read data into the provided meteorological data structure.
Note
This function assumes that the NetCDF file ID and structures are properly initialized. It is designed to work with NetCDF files and uses MPI for parallel processing. The function logs warnings for missing or unreadable data fields and handles different data formats.
Author
Lars Hoffmann
Jan Clemens

Definition at line 10417 of file mptrac.c.

10421 {
10422
10423 /* Set timer... */
10424 SELECT_TIMER("READ_MET_SURFACE", "INPUT");
10425 LOG(2, "Read surface data...");
10426
10427 /* Read surface pressure... */
10428 if (read_met_nc_2d
10429 (ncid, (const char *const[]) { "lnsp", "LNSP" }, 2, ctl, met, dd,
10430 met->ps,
10431 1.0f, 1)) {
10432 for (int ix = 0; ix < met->nx; ix++)
10433 for (int iy = 0; iy < met->ny; iy++)
10434 met->ps[ix][iy] = (float) (exp(met->ps[ix][iy]) / 100.);
10435 } else
10436 if (!read_met_nc_2d
10437 (ncid, (const char *const[]) { "ps", "PS", "sp", "SP" }, 4, ctl, met,
10438 dd, met->ps,
10439 0.01f, 1)) {
10440 WARN("Cannot not read surface pressure data (use lowest level)!");
10441 for (int ix = 0; ix < met->nx; ix++)
10442 for (int iy = 0; iy < met->ny; iy++)
10443 met->ps[ix][iy]
10444 = (ctl->met_np > 0 ? (float) ctl->met_p[0] : (float) met->p[0]);
10445 }
10446
10447 /* MPTRAC meteo data... */
10448 if (ctl->met_clams == 0) {
10449
10450 /* Read geopotential height at the surface... */
10451 if (!read_met_nc_2d
10452 (ncid, (const char *const[]) { "z", "Z" }, 2, ctl, met, dd, met->zs,
10453 (float) (1. / (1000. * G0)), 1))
10454 if (!read_met_nc_2d
10455 (ncid, (const char *const[]) { "zm", "ZM" }, 2, ctl, met, dd,
10456 met->zs,
10457 (ctl->met_gp2z ? (float) (1e-3 / G0) : (float) (1. / 1000.)), 1))
10458 WARN("Cannot read surface geopotential height!");
10459 }
10460
10461 /* CLaMS meteo data... */
10462 else {
10463
10464 /* Read geopotential height at the surface
10465 (use lowermost level of 3-D data field)... */
10466 float *help;
10467 ALLOC(help, float,
10468 EX * EY * EP);
10469 memcpy(help, met->pl, sizeof(met->pl));
10470 if (!read_met_nc_3d
10471 (ncid, (const char *const[]) { "gph", "GPH" }, 2, ctl, met, dd,
10472 met->pl,
10473 (float) (1e-3 / G0)))
10474 ERRMSG("Cannot read geopotential height!");
10475 /* read_met_nc_3d hands back the levels timer... */
10476 SELECT_TIMER("READ_MET_SURFACE", "INPUT");
10477 for (int ix = 0; ix < met->nx; ix++)
10478 for (int iy = 0; iy < met->ny; iy++)
10479 met->zs[ix][iy] = met->pl[ix][iy][0];
10480 memcpy(met->pl, help, sizeof(met->pl));
10481 free(help);
10482 }
10483
10484 /* Read temperature at the surface... */
10485 if (!read_met_nc_2d
10486 (ncid, (const char *const[]) { "t2m", "T2M", "2t", "2T", "t2", "T2" },
10487 6, ctl, met, dd, met->ts, 1.0,
10488 1))
10489 WARN("Cannot read surface temperature!");
10490
10491 /* Read zonal wind at the surface... */
10492 if (!read_met_nc_2d
10493 (ncid, (const char *const[]) { "u10m", "U10M", "10u", "10U", "u10",
10494 "U10"
10495 }, 6, ctl, met, dd,
10496 met->us, 1.0, 1))
10497 WARN("Cannot read surface zonal wind!");
10498
10499 /* Read meridional wind at the surface... */
10500 if (!read_met_nc_2d
10501 (ncid, (const char *const[]) { "v10m", "V10M", "10v", "10V", "v10",
10502 "V10"
10503 }, 6, ctl, met, dd,
10504 met->vs, 1.0, 1))
10505 WARN("Cannot read surface meridional wind!");
10506
10507 /* Read eastward turbulent surface stress... */
10508 if (!read_met_nc_2d
10509 (ncid, (const char *const[]) { "iews", "IEWS" }, 2, ctl, met, dd,
10510 met->ess,
10511 1.0, 1))
10512 WARN("Cannot read eastward turbulent surface stress!");
10513
10514 /* Read northward turbulent surface stress... */
10515 if (!read_met_nc_2d
10516 (ncid, (const char *const[]) { "inss", "INSS" }, 2, ctl, met, dd,
10517 met->nss,
10518 1.0, 1))
10519 WARN("Cannot read northward turbulent surface stress!");
10520
10521 /* Read surface sensible heat flux... */
10522 if (!read_met_nc_2d
10523 (ncid, (const char *const[]) { "ishf", "ISHF" }, 2, ctl, met, dd,
10524 met->shf,
10525 1.0, 1))
10526 WARN("Cannot read surface sensible heat flux!");
10527
10528 /* Read land-sea mask... */
10529 if (!read_met_nc_2d
10530 (ncid, (const char *const[]) { "lsm", "LSM" }, 2, ctl, met, dd,
10531 met->lsm,
10532 1.0, 1))
10533 WARN("Cannot read land-sea mask!");
10534
10535 /* Read sea surface temperature... */
10536 if (!read_met_nc_2d
10537 (ncid, (const char *const[]) { "sstk", "SSTK", "sst", "SST" }, 4, ctl,
10538 met, dd, met->sst,
10539 1.0, 1))
10540 WARN("Cannot read sea surface temperature!");
10541
10542 /* Read PBL... */
10543 if (ctl->met_pbl == 0)
10544 if (!read_met_nc_2d
10545 (ncid, (const char *const[]) { "blp", "BLP" }, 2, ctl, met, dd,
10546 met->pbl,
10547 0.01f, 1))
10548 WARN("Cannot read planetary boundary layer pressure!");
10549 if (ctl->met_pbl == 1)
10550 if (!read_met_nc_2d
10551 (ncid, (const char *const[]) { "blh", "BLH" }, 2, ctl, met, dd,
10552 met->pbl,
10553 0.001f, 1))
10554 WARN("Cannot read planetary boundary layer height!");
10555
10556 /* Read CAPE... */
10557 if (ctl->met_cape == 0)
10558 if (!read_met_nc_2d
10559 (ncid, (const char *const[]) { "cape", "CAPE" }, 2, ctl, met, dd,
10560 met->cape, 1.0, 1))
10561 WARN("Cannot read CAPE!");
10562
10563 /* Read CIN... */
10564 if (ctl->met_cape == 0)
10565 if (!read_met_nc_2d
10566 (ncid, (const char *const[]) { "cin", "CIN" }, 2, ctl, met, dd,
10567 met->cin,
10568 1.0, 1))
10569 WARN("Cannot read convective inhibition!");
10570}
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:10981
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:11319
Here is the call graph for this function:

◆ read_met_nc_levels()

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.

The read_met_nc_levels function reads meteorological level data from a NetCDF file and processes it for use in a domain decomposition context. It handles various meteorological parameters such as temperature, wind components, humidity, ozone, cloud data, and vertical velocity. The function also processes pressure levels and interpolates data between model and pressure levels as needed.

Parameters
ncidAn integer representing the NetCDF file ID.
ctlA pointer to a ctl_t structure containing control parameters and settings.
metA pointer to a met_t structure where meteorological level data will be stored.
ddA pointer to an dd_t structure containing MPI information, including rank and neighbours.

The function performs the following steps:

  • Reads temperature, horizontal wind components, and vertical velocity data.
  • Processes water vapor data, handling both specific and relative humidity.
  • Reads ozone and various cloud-related data such as liquid water content, ice water content, and cloud cover.
  • Processes zeta and zeta_dot data.
  • Stores velocities on model levels and saves the number of model levels.
  • Computes pressure on model levels using different methods based on control parameters.
  • Checks the ordering of pressure levels to ensure they are monotonic.
  • Interpolates meteorological variables from model levels to pressure levels if specified.
  • Validates the ordering of pressure levels to ensure they are in descending order.
Note
This function assumes that the NetCDF file ID and structures are properly initialized. It is designed to work with NetCDF files and uses OpenMP for parallel processing. The function logs errors and warnings for missing or unreadable data fields and handles different data formats.
Author
Lars Hoffmann
Jan Clemens

Definition at line 10574 of file mptrac.c.

10578 {
10579
10580 /* Set timer... */
10581 SELECT_TIMER("READ_MET_NC_LEVELS", "INPUT");
10582 LOG(2, "Read level data...");
10583
10584 /* Read temperature... */
10585 if (!read_met_nc_3d
10586 (ncid, (const char *const[]) { "t", "T", "temp", "TEMP" }, 4, ctl, met,
10587 dd, met->t, 1.0))
10588 ERRMSG("Cannot read temperature!");
10589
10590 /* Read horizontal wind and vertical velocity... */
10591 if (!read_met_nc_3d
10592 (ncid, (const char *const[]) { "u", "U" }, 2, ctl, met, dd, met->u,
10593 1.0))
10594 ERRMSG("Cannot read zonal wind!");
10595 if (!read_met_nc_3d
10596 (ncid, (const char *const[]) { "v", "V" }, 2, ctl, met, dd, met->v,
10597 1.0))
10598 ERRMSG("Cannot read meridional wind!");
10599 if (!read_met_nc_3d
10600 (ncid, (const char *const[]) { "w", "W", "omega", "OMEGA" }, 4, ctl,
10601 met, dd, met->w, 0.01f))
10602 WARN("Cannot read vertical velocity!");
10603
10604 /* Read water vapor... */
10605 if (!ctl->met_relhum) {
10606 if (!read_met_nc_3d
10607 (ncid, (const char *const[]) { "q", "Q", "sh", "SH" }, 4, ctl, met,
10608 dd, met->h2o,
10609 (float) (MA / MH2O)))
10610 WARN("Cannot read specific humidity!");
10611 } else {
10612 if (!read_met_nc_3d
10613 (ncid, (const char *const[]) { "rh", "RH" }, 2, ctl, met, dd,
10614 met->h2o, 0.01f))
10615 WARN("Cannot read relative humidity!");
10616#pragma omp parallel for default(shared) collapse(2)
10617 for (int ix = 0; ix < met->nx; ix++)
10618 for (int iy = 0; iy < met->ny; iy++)
10619 for (int ip = 0; ip < met->np; ip++) {
10620 double pw = met->h2o[ix][iy][ip] * PSAT(met->t[ix][iy][ip]);
10621 met->h2o[ix][iy][ip] =
10622 (float) (pw / (met->p[ip] - (1.0 - EPS) * pw));
10623 }
10624 }
10625
10626 /* Read ozone... */
10627 if (!read_met_nc_3d
10628 (ncid, (const char *const[]) { "o3", "O3" }, 2, ctl, met, dd, met->o3,
10629 (float) (MA / MO3)))
10630 WARN("Cannot read ozone data!");
10631
10632 /* Read cloud data... */
10633 if (!read_met_nc_3d
10634 (ncid, (const char *const[]) { "clwc", "CLWC" }, 2, ctl, met, dd,
10635 met->lwc, 1.0))
10636 WARN("Cannot read cloud liquid water content!");
10637 if (!read_met_nc_3d
10638 (ncid, (const char *const[]) { "crwc", "CRWC" }, 2, ctl, met, dd,
10639 met->rwc, 1.0))
10640 WARN("Cannot read cloud rain water content!");
10641 if (!read_met_nc_3d
10642 (ncid, (const char *const[]) { "ciwc", "CIWC" }, 2, ctl, met, dd,
10643 met->iwc, 1.0))
10644 WARN("Cannot read cloud ice water content!");
10645 if (!read_met_nc_3d
10646 (ncid, (const char *const[]) { "cswc", "CSWC" }, 2, ctl, met, dd,
10647 met->swc, 1.0))
10648 WARN("Cannot read cloud snow water content!");
10649 if (!read_met_nc_3d
10650 (ncid, (const char *const[]) { "cc", "CC" }, 2, ctl, met, dd, met->cc,
10651 1.0))
10652 WARN("Cannot read cloud cover!");
10653
10654 /* Read zeta and zeta_dot... */
10655 if (ctl->advect_vert_coord == 1) {
10656 if (!read_met_nc_3d
10657 (ncid, (const char *const[]) { "ZETA", "zeta" }, 2, ctl, met, dd,
10658 met->zetal, 1.0))
10659 WARN("Cannot read ZETA!");
10660 if (!read_met_nc_3d
10661 (ncid, (const char *const[]) { "ZETA_DOT_TOT", "ZETA_DOT_clr",
10662 "zeta_dot_clr"
10663 }, 3, ctl, met, dd, met->zeta_dotl, 0.00001157407f))
10664 ERRMSG("Cannot read ZETA_DOT!");
10665 }
10666
10667 /* Set eta coordinate on native model levels... */
10668 if (ctl->advect_vert_coord == 3 || ctl->qnt_eta_d >= 0) {
10669#pragma omp parallel for default(shared)
10670 for (int ix = 0; ix < met->nx; ix++)
10671 for (int iy = 0; iy < met->ny; iy++)
10672 for (int ip = 0; ip < met->np; ip++)
10673 met->zetal[ix][iy][ip] =
10674 (float) (met->hyam[ip] / (100.0 * P0) + met->hybm[ip]);
10675 }
10676
10677 /* Read eta_dot... */
10678 if (ctl->advect_vert_coord == 3)
10679 if (!read_met_nc_3d
10680 (ncid, (const char *const[]) { "etadot", "ETADOT" }, 2, ctl, met, dd,
10681 met->zeta_dotl,
10682 1.0))
10683 ERRMSG("Cannot read eta vertical velocity!");
10684
10685 /* Store velocities on model levels... */
10686 if (ctl->met_vert_coord != 0) {
10687#pragma omp parallel for default(shared)
10688 for (int ix = 0; ix < met->nx; ix++)
10689 for (int iy = 0; iy < met->ny; iy++)
10690 for (int ip = 0; ip < met->np; ip++) {
10691 met->ul[ix][iy][ip] = met->u[ix][iy][ip];
10692 met->vl[ix][iy][ip] = met->v[ix][iy][ip];
10693 met->wl[ix][iy][ip] = met->w[ix][iy][ip];
10694 }
10695
10696 /* Save number of model levels... */
10697 met->npl = met->np;
10698 }
10699
10700 /* Get pressure on model levels... */
10701 if (ctl->met_np > 0 || ctl->met_vert_coord != 0) {
10702
10703 /* Read 3-D pressure field... */
10704 if (ctl->met_vert_coord == 1) {
10705 if (!read_met_nc_3d
10706 (ncid, (const char *const[]) { "pl", "PL", "pressure", "PRESSURE" },
10707 4, ctl, met, dd, met->pl,
10708 0.01f))
10709 if (!read_met_nc_3d
10710 (ncid, (const char *const[]) { "press", "PRESS" }, 2, ctl, met,
10711 dd, met->pl, 1.0))
10712 ERRMSG("Cannot read pressure on model levels!");
10713 }
10714
10715 /* Use a and b coefficients for full levels (at layer midpoints)... */
10716 else if (ctl->met_vert_coord == 2 || ctl->met_vert_coord == 3) {
10717
10718 /* Check number of levels... */
10719 if (ctl->met_vert_coord == 3 && met->np != ctl->met_nlev)
10720 ERRMSG("Mismatch in number of model levels!");
10721
10722 /* Calculate pressure... */
10723 for (int ix = 0; ix < met->nx; ix++)
10724 for (int iy = 0; iy < met->ny; iy++)
10725 for (int ip = 0; ip < met->np; ip++)
10726 met->pl[ix][iy][ip] =
10727 (float) (met->hyam[ip] / 100. +
10728 met->hybm[ip] * met->ps[ix][iy]);
10729 }
10730
10731 /* Use a and b coefficients for half levels (at layer interfaces)... */
10732 else if (ctl->met_vert_coord == 4) {
10733
10734 /* Check number of levels... */
10735 if (met->np + 1 != ctl->met_nlev)
10736 ERRMSG("Mismatch in number of model levels!");
10737
10738 /* Calculate pressure... */
10739#pragma omp parallel for default(shared) collapse(2)
10740 for (int ix = 0; ix < met->nx; ix++)
10741 for (int iy = 0; iy < met->ny; iy++)
10742 for (int ip = 0; ip < met->np; ip++) {
10743 const double p0 =
10744 met->hyam[ip] / 100. + met->hybm[ip] * met->ps[ix][iy];
10745 const double p1 =
10746 met->hyam[ip + 1] / 100. + met->hybm[ip + 1] * met->ps[ix][iy];
10747 met->pl[ix][iy][ip] = (float) ((p1 - p0) / log(p1 / p0));
10748 }
10749 }
10750
10751 /* Check ordering of pressure levels... */
10752 for (int ix = 0; ix < met->nx; ix++)
10753 for (int iy = 0; iy < met->ny; iy++)
10754 for (int ip = 1; ip < met->np; ip++)
10755 if ((met->pl[ix][iy][0] > met->pl[ix][iy][1]
10756 && met->pl[ix][iy][ip - 1] <= met->pl[ix][iy][ip])
10757 || (met->pl[ix][iy][0] < met->pl[ix][iy][1]
10758 && met->pl[ix][iy][ip - 1] >= met->pl[ix][iy][ip]))
10759 ERRMSG("Pressure profiles are not monotonic!");
10760 }
10761
10762 /* Interpolate from model levels to pressure levels... */
10763 if (ctl->met_np > 0) {
10764
10765 /* Interpolate variables... */
10766 read_met_ml2pl(ctl, met, met->t, "T");
10767 read_met_ml2pl(ctl, met, met->u, "U");
10768 read_met_ml2pl(ctl, met, met->v, "V");
10769 read_met_ml2pl(ctl, met, met->w, "W");
10770 read_met_ml2pl(ctl, met, met->h2o, "H2O");
10771 read_met_ml2pl(ctl, met, met->o3, "O3");
10772 read_met_ml2pl(ctl, met, met->lwc, "LWC");
10773 read_met_ml2pl(ctl, met, met->rwc, "RWC");
10774 read_met_ml2pl(ctl, met, met->iwc, "IWC");
10775 read_met_ml2pl(ctl, met, met->swc, "SWC");
10776 read_met_ml2pl(ctl, met, met->cc, "CC");
10777
10778 /* Set new pressure levels... */
10779 met->np = ctl->met_np;
10780 for (int ip = 0; ip < met->np; ip++)
10781 met->p[ip] = ctl->met_p[ip];
10782 }
10783
10784 /* Check ordering of pressure levels... */
10785 for (int ip = 1; ip < met->np; ip++)
10786 if (met->p[ip - 1] < met->p[ip])
10787 ERRMSG("Pressure levels must be descending!");
10788}
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:12093
#define MH2O
Molar mass of water vapor [g/mol].
Definition mptrac.h:295
#define MO3
Molar mass of ozone [g/mol].
Definition mptrac.h:300
Here is the call graph for this function:

◆ read_met_nc_2d()

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.

This function reads a 2-dimensional meteorological variable from a NetCDF file and stores it in a specified destination array. It supports both packed and unpacked data formats and handles missing values and scaling factors accordingly. The function also checks the meteorological data layout to ensure correct data copying.

Parameters
ncidThe NetCDF file ID.
varnamesArray of variable names in order of preference.
nvarnamesNumber of entries in varnames.
ctlA pointer to a structure containing control parameters.
metA pointer to a structure containing meteorological data.
ddA pointer to an dd_t structure containing MPI information, including rank and neighbours.
destThe destination array to store the read data.
sclA scaling factor to apply to the read data.
initFlag indicating whether to initialize the destination array before reading.
Returns
Returns 1 on success, 0 on failure.

The function performs the following steps:

  • Checks if the specified variable exists in the NetCDF file.
  • Reads packed data if scaling factors are available, otherwise reads unpacked data.
  • Handles missing values and scaling factors appropriately.
  • Copies the data to the destination array, applying the scaling factor if provided.
Author
Lars Hoffmann

Definition at line 10981 of file mptrac.c.

10990 {
10991
10992 const char *varsel = NULL;
10993
10994 float offset, scalfac;
10995
10996 int varid;
10997
10998 /* Check if variable exists... */
10999 for (size_t i = 0; i < nvarnames; i++)
11000 if (nc_inq_varid(ncid, varnames[i], &varid) == NC_NOERR) {
11001 varsel = varnames[i];
11002 break;
11003 }
11004 if (varsel == NULL)
11005 return 0;
11006
11007 /* Read packed data... */
11008 if (ctl->met_nc_scale && !ctl->dd
11009 && nc_get_att_float(ncid, varid, "add_offset", &offset) == NC_NOERR
11010 && nc_get_att_float(ncid, varid, "scale_factor",
11011 &scalfac) == NC_NOERR) {
11012
11013 /* Allocate... */
11014 short *help;
11015 ALLOC(help, short,
11016 EX * EY * EP);
11017
11018 /* Read fill value and missing value... */
11019 short fillval, missval;
11020 if (nc_get_att_short(ncid, varid, "_FillValue", &fillval) != NC_NOERR)
11021 fillval = 0;
11022 if (nc_get_att_short(ncid, varid, "missing_value", &missval) != NC_NOERR)
11023 missval = 0;
11024
11025 /* Write info... */
11026 LOG(2, "Read 2-D variable: %s"
11027 " (FILL = %d, MISS = %d, SCALE = %g, OFFSET = %g)",
11028 varsel, fillval, missval, scalfac, offset);
11029
11030 /* Read data... */
11031 NC(nc_get_var_short(ncid, varid, help));
11032
11033 /* Check meteo data layout... */
11034 if (ctl->met_convention != 0)
11035 ERRMSG("Meteo data layout not implemented for packed netCDF files!");
11036
11037 /* Copy and check data... */
11038 omp_set_dynamic(1);
11039#pragma omp parallel for default(shared)
11040 for (int ix = 0; ix < met->nx; ix++)
11041 for (int iy = 0; iy < met->ny; iy++) {
11042 if (init)
11043 dest[ix][iy] = 0;
11044 const short aux = help[ARRAY_2D(iy, ix, met->nx)];
11045 if ((fillval == 0 || aux != fillval)
11046 && (missval == 0 || aux != missval)
11047 && fabsf(aux * scalfac + offset) < 1e14f)
11048 dest[ix][iy] += scl * (aux * scalfac + offset);
11049 else
11050 dest[ix][iy] = NAN;
11051 }
11052 omp_set_dynamic(0);
11053
11054 /* Free... */
11055 free(help);
11056 }
11057
11058 /* Unpacked data... */
11059 else if (!ctl->dd) {
11060
11061 /* Allocate... */
11062 float *help;
11063 ALLOC(help, float,
11064 EX * EY);
11065
11066 /* Read fill value and missing value... */
11067 float fillval, missval;
11068 if (nc_get_att_float(ncid, varid, "_FillValue", &fillval) != NC_NOERR)
11069 fillval = 0;
11070 if (nc_get_att_float(ncid, varid, "missing_value", &missval) != NC_NOERR)
11071 missval = 0;
11072
11073 /* Write info... */
11074 LOG(2, "Read 2-D variable: %s (FILL = %g, MISS = %g)",
11075 varsel, fillval, missval);
11076
11077 /* Read data... */
11078 NC(nc_get_var_float(ncid, varid, help));
11079
11080 /* Check meteo data layout... */
11081 if (ctl->met_convention == 0) {
11082
11083 /* Copy and check data (ordering: lat, lon)... */
11084 omp_set_dynamic(1);
11085#pragma omp parallel for default(shared)
11086 for (int ix = 0; ix < met->nx; ix++)
11087 for (int iy = 0; iy < met->ny; iy++) {
11088 if (init)
11089 dest[ix][iy] = 0;
11090 const float aux = help[ARRAY_2D(iy, ix, met->nx)];
11091 if ((fillval == 0 || aux != fillval)
11092 && (missval == 0 || aux != missval)
11093 && fabsf(aux) < 1e14f)
11094 dest[ix][iy] += scl * aux;
11095 else
11096 dest[ix][iy] = NAN;
11097 }
11098 omp_set_dynamic(0);
11099
11100 } else {
11101
11102 /* Copy and check data (ordering: lon, lat)... */
11103 omp_set_dynamic(1);
11104#pragma omp parallel for default(shared)
11105 for (int iy = 0; iy < met->ny; iy++)
11106 for (int ix = 0; ix < met->nx; ix++) {
11107 if (init)
11108 dest[ix][iy] = 0;
11109 const float aux = help[ARRAY_2D(ix, iy, met->ny)];
11110 if ((fillval == 0 || aux != fillval)
11111 && (missval == 0 || aux != missval)
11112 && fabsf(aux) < 1e14f)
11113 dest[ix][iy] += scl * aux;
11114 else
11115 dest[ix][iy] = NAN;
11116 }
11117 omp_set_dynamic(0);
11118 }
11119
11120 /* Free... */
11121 free(help);
11122 }
11123
11124 /* Domain decomposed data... */
11125 else {
11126
11127 /* Read fill value and missing value... */
11128 float fillval, missval;
11129 if (nc_get_att_float(ncid, varid, "_FillValue", &fillval) != NC_NOERR)
11130 fillval = 0;
11131 if (nc_get_att_float(ncid, varid, "missing_value", &missval) != NC_NOERR)
11132 missval = 0;
11133
11134 /* Write info... */
11135 LOG(2, "Read 2-D variable: %s (FILL = %g, MISS = %g)",
11136 varsel, fillval, missval);
11137
11138#ifdef DD
11139 if (ctl->met_nc_read_mode == 1) {
11140 met_global_2d_check_var(ncid, varid, 3, dd, 0);
11141 NC(nc_var_par_access(ncid, varid, NC_INDEPENDENT));
11142 const float *help = met_global_2d_read(ncid, varid, 3, varid, dd);
11143 SELECT_TIMER("MET_CONVERT", "INPUT");
11144 omp_set_dynamic(1);
11145#pragma omp parallel for default(shared)
11146 for (int iy = 0; iy < met->ny; iy++)
11147 for (int ix = 0; ix < met->nx; ix++) {
11148 const float aux = help[(size_t) iy * (size_t) met->nx + (size_t) ix];
11149 if (init)
11150 dest[ix][iy] = 0;
11151 if ((fillval == 0 || aux != fillval)
11152 && (missval == 0 || aux != missval) && fabsf(aux) < 1e14f)
11153 dest[ix][iy] += scl * aux;
11154 else
11155 dest[ix][iy] = NAN;
11156 }
11157 omp_set_dynamic(0);
11158 /* Return to the caller's timer (only called from read_met_nc_surface)... */
11159 SELECT_TIMER("READ_MET_SURFACE", "INPUT");
11160 return 1;
11161 }
11162#endif
11163
11164 /* Define hyperslab... */
11165 float *help;
11166 size_t help_subdomain_start[3];
11167 size_t help_subdomain_count[3];
11168
11169 help_subdomain_start[0] = 0;
11170 if (ctl->met_convention == 0) {
11171 help_subdomain_start[1] = dd->subdomain_start[2];
11172 help_subdomain_start[2] = dd->subdomain_start[3];
11173 } else {
11174 help_subdomain_start[1] = dd->subdomain_start[3];
11175 help_subdomain_start[2] = dd->subdomain_start[2];
11176 }
11177
11178 help_subdomain_count[0] = 1;
11179 if (ctl->met_convention == 0) {
11180 help_subdomain_count[1] = dd->subdomain_count[2]; //y
11181 help_subdomain_count[2] = dd->subdomain_count[3]; //x
11182 } else {
11183 help_subdomain_count[1] = dd->subdomain_count[3]; //x
11184 help_subdomain_count[2] = dd->subdomain_count[2]; //y
11185 }
11186
11187 ALLOC(help, float,
11188 (int) dd->subdomain_count[2] * (int) dd->subdomain_count[3]);
11189
11190 /* Read data... */
11191 SELECT_TIMER("MET_READ", "INPUT");
11192#ifdef DD
11193 nc_var_par_access(ncid, varid, NC_COLLECTIVE);
11194#endif
11195 NC(nc_get_vara_float
11196 (ncid, varid, help_subdomain_start, help_subdomain_count, help));
11197
11198 /* Read halos at boundaries... */
11199 size_t help_halo_bnd_start[3];
11200 size_t help_halo_bnd_count[3];
11201
11202 help_halo_bnd_start[0] = 0;
11203 if (ctl->met_convention == 0) {
11204 help_halo_bnd_start[1] = dd->halo_bnd_start[2];
11205 help_halo_bnd_start[2] = dd->halo_bnd_start[3];
11206 } else {
11207 help_halo_bnd_start[1] = dd->halo_bnd_start[3];
11208 help_halo_bnd_start[2] = dd->halo_bnd_start[2];
11209 }
11210
11211 help_halo_bnd_count[0] = 1;
11212 if (ctl->met_convention == 0) {
11213 help_halo_bnd_count[1] = dd->halo_bnd_count[2]; //y
11214 help_halo_bnd_count[2] = dd->halo_bnd_count[3]; //x
11215 } else {
11216 help_halo_bnd_count[1] = dd->halo_bnd_count[3]; //x
11217 help_halo_bnd_count[2] = dd->halo_bnd_count[2]; //y
11218 }
11219
11220 float *help_halo;
11221 ALLOC(help_halo, float,
11222 help_halo_bnd_count[1] * help_halo_bnd_count[2]);
11223
11224#ifdef DD
11225 nc_var_par_access(ncid, varid, NC_COLLECTIVE);
11226#endif
11227 NC(nc_get_vara_float
11228 (ncid, varid, help_halo_bnd_start, help_halo_bnd_count,
11229 help_halo));
11230
11231 SELECT_TIMER("MET_CONVERT", "INPUT");
11232
11233 /* Check meteo data layout... */
11234 if (ctl->met_convention == 0) {
11235
11236 /* Copy and check data (ordering: lat, lon)... */
11237 omp_set_dynamic(1);
11238#pragma omp parallel for default(shared)
11239 for (int ix = 0; ix < (int) help_subdomain_count[2]; ix++)
11240 for (int iy = 0; iy < (int) help_subdomain_count[1]; iy++) {
11241 if (init == 1)
11242 dest[ix + dd->halo_offset_start][iy] = 0;
11243 const float aux =
11244 help[ARRAY_2D(iy, ix, (int) help_subdomain_count[2])];
11245 if ((fillval == 0 || aux != fillval)
11246 && (missval == 0 || aux != missval)
11247 && fabsf(aux) < 1e14f) {
11248 dest[ix + dd->halo_offset_start][iy] += scl * aux;
11249 } else
11250 dest[ix + dd->halo_offset_start][iy] = NAN;
11251 }
11252
11253#pragma omp parallel for default(shared)
11254 for (int ix = 0; ix < (int) help_halo_bnd_count[2]; ix++)
11255 for (int iy = 0; iy < (int) help_halo_bnd_count[1]; iy++) {
11256 if (init == 1)
11257 dest[ix + dd->halo_offset_end][iy] = 0;
11258 const float aux =
11259 help_halo[ARRAY_2D(iy, ix, (int) help_halo_bnd_count[2])];
11260 if ((fillval == 0 || aux != fillval)
11261 && (missval == 0 || aux != missval)
11262 && fabsf(aux) < 1e14f)
11263 dest[ix + dd->halo_offset_end][iy] += scl * aux;
11264 else {
11265 dest[ix + dd->halo_offset_end][iy] = NAN;
11266 }
11267 }
11268 omp_set_dynamic(0);
11269
11270 } else {
11271
11272 /* Copy and check data (ordering: lon, lat)... */
11273 omp_set_dynamic(1);
11274#pragma omp parallel for default(shared)
11275 for (int ix = 0; ix < (int) help_subdomain_count[1]; ix++)
11276 for (int iy = 0; iy < (int) help_subdomain_count[2]; iy++) {
11277 if (init == 1)
11278 dest[ix + dd->halo_offset_start][iy] = 0;
11279 const float aux =
11280 help[ARRAY_2D(ix, iy, (int) help_subdomain_count[1])];
11281 if ((fillval == 0 || aux != fillval)
11282 && (missval == 0 || aux != missval)
11283 && fabsf(aux) < 1e14f)
11284 dest[ix + dd->halo_offset_start][iy] += scl * aux;
11285 else
11286 dest[ix + dd->halo_offset_start][iy] = NAN;
11287 }
11288
11289#pragma omp parallel for default(shared)
11290 for (int ix = 0; ix < (int) help_halo_bnd_count[1]; ix++)
11291 for (int iy = 0; iy < (int) help_halo_bnd_count[2]; iy++) {
11292 if (init == 1)
11293 dest[ix + dd->halo_offset_end][iy] = 0;
11294 const float aux =
11295 help_halo[ARRAY_2D(ix, iy, (int) help_halo_bnd_count[1])];
11296 if ((fillval == 0 || aux != fillval)
11297 && (missval == 0 || aux != missval)
11298 && fabsf(aux) < 1e14f)
11299 dest[ix + dd->halo_offset_end][iy] += scl * aux;
11300 else
11301 dest[ix + dd->halo_offset_end][iy] = NAN;
11302 }
11303 omp_set_dynamic(0);
11304 }
11305 /* Return to the caller's timer (only called from read_met_nc_surface)... */
11306 SELECT_TIMER("READ_MET_SURFACE", "INPUT");
11307
11308 /* Free... */
11309 free(help);
11310 free(help_halo);
11311 }
11312
11313 /* Return... */
11314 return 1;
11315}
size_t halo_bnd_count[4]
Extent of the periodic boundary halo hyperslab.
Definition mptrac.h:4105
int halo_offset_end
Offset of the periodic halo block at the end of the local x-array.
Definition mptrac.h:4111
size_t halo_bnd_start[4]
Start indices of the periodic boundary halo hyperslab.
Definition mptrac.h:4102
int halo_offset_start
Offset of the periodic halo block at the beginning of the local x-array.
Definition mptrac.h:4108
size_t subdomain_count[4]
Extent of the local subdomain hyperslab (including inner halos).
Definition mptrac.h:4099
size_t subdomain_start[4]
Start indices of the local subdomain hyperslab (including inner halos).
Definition mptrac.h:4096

◆ read_met_nc_3d()

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.

This function reads a 3-dimensional meteorological variable from a NetCDF file and stores it in a specified destination array. It supports both packed and unpacked data formats and handles missing values and scaling factors accordingly. The function also checks the meteorological data layout to ensure correct data copying.

Parameters
ncidThe NetCDF file ID.
varnamesArray of variable names in order of preference.
nvarnamesNumber of entries in varnames.
ctlA pointer to a structure containing control parameters.
metA pointer to a structure containing meteorological data.
ddA pointer to an dd_t structure containing MPI information, including rank and neighbours.
destThe destination array to store the read data.
sclA scaling factor to apply to the read data.
Returns
Returns 1 on success, 0 on failure.

The function performs the following steps:

  • Checks if the specified variable exists in the NetCDF file.
  • Reads packed data if scaling factors are available, otherwise reads unpacked data.
  • Handles missing values and scaling factors appropriately.
  • Copies the data to the destination array, applying the scaling factor if provided.
Author
Lars Hoffmann

Definition at line 11319 of file mptrac.c.

11327 {
11328
11329 const char *varsel = NULL;
11330
11331 float offset, scalfac;
11332
11333 int varid;
11334
11335 /* Check if variable exists... */
11336 for (size_t i = 0; i < nvarnames; i++)
11337 if (nc_inq_varid(ncid, varnames[i], &varid) == NC_NOERR) {
11338 varsel = varnames[i];
11339 break;
11340 }
11341 if (varsel == NULL)
11342 return 0;
11343
11344 /* Read packed data... */
11345 if (ctl->met_nc_scale && !ctl->dd
11346 && nc_get_att_float(ncid, varid, "add_offset", &offset) == NC_NOERR
11347 && nc_get_att_float(ncid, varid, "scale_factor",
11348 &scalfac) == NC_NOERR) {
11349
11350 /* Allocate... */
11351 short *help;
11352 ALLOC(help, short,
11353 EX * EY * EP);
11354
11355 /* Read fill value and missing value... */
11356 short fillval, missval;
11357 if (nc_get_att_short(ncid, varid, "_FillValue", &fillval) != NC_NOERR)
11358 fillval = 0;
11359 if (nc_get_att_short(ncid, varid, "missing_value", &missval) != NC_NOERR)
11360 missval = 0;
11361
11362 /* Write info... */
11363 LOG(2, "Read 3-D variable: %s "
11364 "(FILL = %d, MISS = %d, SCALE = %g, OFFSET = %g)",
11365 varsel, fillval, missval, scalfac, offset);
11366
11367 /* Read data... */
11368 NC(nc_get_var_short(ncid, varid, help));
11369
11370 /* Check meteo data layout... */
11371 if (ctl->met_convention != 0)
11372 ERRMSG("Meteo data layout not implemented for packed netCDF files!");
11373
11374 /* Copy and check data... */
11375 omp_set_dynamic(1);
11376#pragma omp parallel for default(shared)
11377 for (int ix = 0; ix < met->nx; ix++)
11378 for (int iy = 0; iy < met->ny; iy++)
11379 for (int ip = 0; ip < met->np; ip++) {
11380 const short aux = help[ARRAY_3D(ip, iy, met->ny, ix, met->nx)];
11381 if ((fillval == 0 || aux != fillval)
11382 && (missval == 0 || aux != missval)
11383 && fabsf(aux * scalfac + offset) < 1e14f)
11384 dest[ix][iy][ip] = scl * (aux * scalfac + offset);
11385 else
11386 dest[ix][iy][ip] = NAN;
11387 }
11388 omp_set_dynamic(0);
11389
11390 /* Free... */
11391 free(help);
11392 }
11393
11394 /* Unpacked data... */
11395 else if (!ctl->dd) {
11396
11397 /* Allocate... */
11398 float *help;
11399 ALLOC(help, float,
11400 EX * EY * EP);
11401
11402 /* Read fill value and missing value... */
11403 float fillval, missval;
11404 if (nc_get_att_float(ncid, varid, "_FillValue", &fillval) != NC_NOERR)
11405 fillval = 0;
11406 if (nc_get_att_float(ncid, varid, "missing_value", &missval) != NC_NOERR)
11407 missval = 0;
11408
11409 /* Write info... */
11410 LOG(2, "Read 3-D variable: %s (FILL = %g, MISS = %g)",
11411 varsel, fillval, missval);
11412
11413 /* Read data... */
11414 NC(nc_get_var_float(ncid, varid, help));
11415
11416 /* Check meteo data layout... */
11417 if (ctl->met_convention == 0) {
11418
11419 /* Copy and check data (ordering: lev, lat, lon)... */
11420 omp_set_dynamic(1);
11421#pragma omp parallel for default(shared)
11422 for (int ix = 0; ix < met->nx; ix++)
11423 for (int iy = 0; iy < met->ny; iy++)
11424 for (int ip = 0; ip < met->np; ip++) {
11425 const float aux = help[ARRAY_3D(ip, iy, met->ny, ix, met->nx)];
11426 if ((fillval == 0 || aux != fillval)
11427 && (missval == 0 || aux != missval)
11428 && fabsf(aux) < 1e14f)
11429 dest[ix][iy][ip] = scl * aux;
11430 else
11431 dest[ix][iy][ip] = NAN;
11432 }
11433 omp_set_dynamic(0);
11434
11435 } else {
11436
11437 /* Copy and check data (ordering: lon, lat, lev)... */
11438 omp_set_dynamic(1);
11439#pragma omp parallel for default(shared)
11440 for (int ip = 0; ip < met->np; ip++)
11441 for (int iy = 0; iy < met->ny; iy++)
11442 for (int ix = 0; ix < met->nx; ix++) {
11443 const float aux = help[ARRAY_3D(ix, iy, met->ny, ip, met->np)];
11444 if ((fillval == 0 || aux != fillval)
11445 && (missval == 0 || aux != missval)
11446 && fabsf(aux) < 1e14f)
11447 dest[ix][iy][ip] = scl * aux;
11448 else
11449 dest[ix][iy][ip] = NAN;
11450 }
11451 omp_set_dynamic(0);
11452 }
11453
11454 /* Free... */
11455 free(help);
11456 }
11457
11458 /* Domain decomposed data... */
11459 else {
11460
11461 /* Read fill value and missing value... */
11462 float fillval, missval;
11463 if (nc_get_att_float(ncid, varid, "_FillValue", &fillval) != NC_NOERR)
11464 fillval = 0;
11465 if (nc_get_att_float(ncid, varid, "missing_value", &missval) != NC_NOERR)
11466 missval = 0;
11467
11468 /* Write info... */
11469 LOG(2, "Read 3-D variable: %s (FILL = %g, MISS = %g)",
11470 varsel, fillval, missval);
11471
11472#ifdef DD
11473 if (ctl->met_nc_read_mode == 1) {
11474 met_global_2d_check_var(ncid, varid, 4, dd, met->np);
11475 NC(nc_var_par_access(ncid, varid, NC_INDEPENDENT));
11476 for (int ip = 0; ip < met->np; ip++) {
11477 const float *help = met_global_2d_read(ncid, varid, 4, ip, dd);
11478 SELECT_TIMER("MET_CONVERT", "INPUT");
11479 omp_set_dynamic(1);
11480#pragma omp parallel for default(shared)
11481 for (int iy = 0; iy < met->ny; iy++)
11482 for (int ix = 0; ix < met->nx; ix++) {
11483 const float aux =
11484 help[(size_t) iy * (size_t) met->nx + (size_t) ix];
11485 if ((fillval == 0 || aux != fillval)
11486 && (missval == 0 || aux != missval) && fabsf(aux) < 1e14f)
11487 dest[ix][iy][ip] = scl * aux;
11488 else
11489 dest[ix][iy][ip] = NAN;
11490 }
11491 omp_set_dynamic(0);
11492 }
11493 /* Return to the caller's timer (only called from read_met_nc_levels)... */
11494 SELECT_TIMER("READ_MET_NC_LEVELS", "INPUT");
11495 return 1;
11496 }
11497#endif
11498
11499 /* Define hyperslab... */
11500 size_t help_subdomain_start[4];
11501 size_t help_subdomain_count[4];
11502 size_t help_halo_bnd_start[4];
11503 size_t help_halo_bnd_count[4];
11504
11505 if (ctl->met_convention == 0) {
11506 for (int i = 0; i < 4; i++) {
11507 help_subdomain_start[i] = dd->subdomain_start[i];
11508 help_subdomain_count[i] = dd->subdomain_count[i];
11509 help_halo_bnd_start[i] = dd->halo_bnd_start[i];
11510 help_halo_bnd_count[i] = dd->halo_bnd_count[i];
11511 }
11512 } else {
11513 help_subdomain_start[0] = dd->subdomain_start[0];
11514 help_subdomain_start[1] = dd->subdomain_start[3];
11515 help_subdomain_start[2] = dd->subdomain_start[2];
11516 help_subdomain_start[3] = dd->subdomain_start[1];
11517
11518 help_subdomain_count[0] = dd->subdomain_count[0];
11519 help_subdomain_count[1] = dd->subdomain_count[3];
11520 help_subdomain_count[2] = dd->subdomain_count[2];
11521 help_subdomain_count[3] = dd->subdomain_count[1];
11522
11523 help_halo_bnd_start[0] = dd->halo_bnd_start[0];
11524 help_halo_bnd_start[1] = dd->halo_bnd_start[3];
11525 help_halo_bnd_start[2] = dd->halo_bnd_start[2];
11526 help_halo_bnd_start[3] = dd->halo_bnd_start[1];
11527
11528 help_halo_bnd_count[0] = dd->halo_bnd_count[0];
11529 help_halo_bnd_count[1] = dd->halo_bnd_count[3];
11530 help_halo_bnd_count[2] = dd->halo_bnd_count[2];
11531 help_halo_bnd_count[3] = dd->halo_bnd_count[1];
11532 }
11533
11534 /* Allocate... */
11535 float *help;
11536 ALLOC(help, float,
11537 (int) dd->subdomain_count[0] * (int) dd->subdomain_count[1]
11538 * (int) dd->subdomain_count[2] * (int) dd->subdomain_count[3]);
11539
11540 /* Use default NetCDF parallel I/O behavior */
11541 SELECT_TIMER("MET_READ", "INPUT");
11542#ifdef DD
11543 NC(nc_var_par_access(ncid, varid, NC_INDEPENDENT));
11544#endif
11545 NC(nc_get_vara_float
11546 (ncid, varid, help_subdomain_start, help_subdomain_count, help));
11547
11548 /* Read halos separately at boundaries... */
11549 float *help_halo;
11550 ALLOC(help_halo, float,
11551 dd->halo_bnd_count[0] * dd->halo_bnd_count[1] *
11552 dd->halo_bnd_count[2] * dd->halo_bnd_count[3]);
11553
11554#ifdef DD
11555 NC(nc_var_par_access(ncid, varid, NC_INDEPENDENT));
11556#endif
11557 if (dd->halo_bnd_count[1] > 0 && dd->halo_bnd_count[2] > 0
11558 && dd->halo_bnd_count[3] > 0) {
11559 NC(nc_get_vara_float
11560 (ncid, varid, help_halo_bnd_start, help_halo_bnd_count,
11561 help_halo));
11562 }
11563
11564 SELECT_TIMER("MET_CONVERT", "INPUT");
11565
11566 /* Check meteo data layout... */
11567 if (ctl->met_convention == 0) {
11568
11569 /* Copy and check data (ordering: lev, lat, lon)... */
11570 omp_set_dynamic(1);
11571#pragma omp parallel for default(shared)
11572 for (int ix = 0; ix < (int) dd->subdomain_count[3]; ix++)
11573 for (int iy = 0; iy < (int) dd->subdomain_count[2]; iy++)
11574 for (int ip = 0; ip < met->np; ip++) {
11575 const float aux =
11576 help[ARRAY_3D(ip, iy, (int) dd->subdomain_count[2], ix,
11577 (int) dd->subdomain_count[3])];
11578 if ((fillval == 0 || aux != fillval)
11579 && (missval == 0 || aux != missval)
11580 && fabsf(aux) < 1e14f) {
11581 dest[ix + dd->halo_offset_start][iy][ip] = scl * aux;
11582
11583 } else
11584 dest[ix + dd->halo_offset_start][iy][ip] = NAN;
11585 }
11586
11587#pragma omp parallel for default(shared)
11588 for (int ix = 0; ix < (int) dd->halo_bnd_count[3]; ix++)
11589 for (int iy = 0; iy < (int) dd->halo_bnd_count[2]; iy++)
11590 for (int ip = 0; ip < met->np; ip++) {
11591 const float aux =
11592 help_halo[ARRAY_3D(ip, iy, (int) dd->halo_bnd_count[2], ix,
11593 (int) dd->halo_bnd_count[3])];
11594 if ((fillval == 0 || aux != fillval)
11595 && (missval == 0 || aux != missval)
11596 && fabsf(aux) < 1e14f)
11597 dest[ix + dd->halo_offset_end][iy][ip] = scl * aux;
11598 else
11599 dest[ix + dd->halo_offset_end][iy][ip] = NAN;
11600 }
11601 omp_set_dynamic(0);
11602
11603 } else {
11604
11605 /* Copy and check data (ordering: lon, lat, lev)... */
11606 omp_set_dynamic(1);
11607#pragma omp parallel for default(shared)
11608 for (int ip = 0; ip < met->np; ip++)
11609 for (int iy = 0; iy < (int) dd->subdomain_count[2]; iy++)
11610 for (int ix = 0; ix < (int) dd->subdomain_count[3]; ix++) {
11611 const float aux =
11612 help[ARRAY_3D
11613 (ix, iy, (int) dd->subdomain_count[2], ip, met->np)];
11614 if ((fillval == 0 || aux != fillval)
11615 && (missval == 0 || aux != missval)
11616 && fabsf(aux) < 1e14f)
11617 dest[ix + dd->halo_offset_start][iy][ip] = scl * aux;
11618 else
11619 dest[ix + dd->halo_offset_start][iy][ip] = NAN;
11620 }
11621
11622#pragma omp parallel for default(shared)
11623 for (int ip = 0; ip < met->np; ip++)
11624 for (int iy = 0; iy < (int) dd->halo_bnd_count[2]; iy++)
11625 for (int ix = 0; ix < (int) dd->halo_bnd_count[3]; ix++) {
11626 const float aux =
11627 help_halo[ARRAY_3D(ix, iy, (int) dd->halo_bnd_count[2], ip,
11628 met->np)];
11629 if ((fillval == 0 || aux != fillval)
11630 && (missval == 0 || aux != missval)
11631 && fabsf(aux) < 1e14f)
11632 dest[ix + dd->halo_offset_end][iy][ip] = scl * aux;
11633 else
11634 dest[ix + dd->halo_offset_end][iy][ip] = NAN;
11635 }
11636 omp_set_dynamic(0);
11637 }
11638 /* Return to the caller's timer (only called from read_met_nc_levels)... */
11639 SELECT_TIMER("READ_MET_NC_LEVELS", "INPUT");
11640
11641 /* Free... */
11642 free(help);
11643 free(help_halo);
11644 }
11645
11646 /* Return... */
11647 return 1;
11648}

◆ read_met_ml2pl()

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.

This function interpolates meteorological data from model levels to pressure levels. The interpolation is performed in parallel over the spatial grid defined in the meteorological data structure.

Parameters
[in]ctlA pointer to a control structure containing the number of pressure levels (met_np) and the pressure levels themselves (met_p).
[in]metA pointer to a meteorological data structure containing the grid dimensions (nx, ny) and the pressure profile (pl).
[in,out]varA 3D array containing the meteorological variable to be interpolated. On output, this array will contain the interpolated values at the specified pressure levels.
[in]varnameA string representing the name of the meteorological variable being interpolated.

This function performs the following steps:

  • Sets a timer for the operation.
  • Logs the start of the interpolation process with the variable name.
  • Loops over the spatial columns (grid points).
  • For each column, copies the pressure profile.
  • Interpolates the meteorological variable to the specified pressure levels.
  • Copies the interpolated data back into the var array.
Note
The interpolation is performed in parallel using OpenMP.
Author
Lars Hoffmann

Definition at line 12093 of file mptrac.c.

12097 {
12098
12099 double aux[EP], p[EP];
12100
12101 /* Set timer... */
12102 SELECT_TIMER("READ_MET_ML2PL", "METPROC");
12103 LOG(2, "Interpolate meteo data to pressure levels: %s", varname);
12104
12105 /* Loop over columns... */
12106#pragma omp parallel for default(shared) private(aux,p) collapse(2)
12107 for (int ix = 0; ix < met->nx; ix++)
12108 for (int iy = 0; iy < met->ny; iy++) {
12109
12110 /* Copy pressure profile... */
12111 for (int ip = 0; ip < met->np; ip++)
12112 p[ip] = met->pl[ix][iy][ip];
12113
12114 /* Interpolate... */
12115 for (int ip = 0; ip < ctl->met_np; ip++) {
12116 double pt = ctl->met_p[ip];
12117 if ((pt > p[0] && p[0] > p[1]) || (pt < p[0] && p[0] < p[1]))
12118 pt = p[0];
12119 else if ((pt > p[met->np - 1] && p[1] > p[0])
12120 || (pt < p[met->np - 1] && p[1] < p[0]))
12121 pt = p[met->np - 1];
12122 const int ip2 = locate_irr(p, met->np, pt);
12123 aux[ip] = LIN(p[ip2], var[ix][iy][ip2],
12124 p[ip2 + 1], var[ix][iy][ip2 + 1], pt);
12125 }
12126
12127 /* Copy data... */
12128 for (int ip = 0; ip < ctl->met_np; ip++)
12129 var[ix][iy][ip] = (float) aux[ip];
12130 }
12131}
Here is the call graph for this function:

◆ read_met_monotonize()

void read_met_monotonize ( const ctl_t * ctl,
met_t * met )

Makes zeta and pressure profiles monotone.

This function ensures that zeta and pressure profiles are monotone increasing and decreasing with altitude. It iterates over each grid point and each level to identify inversions and linearly interpolate between them to maintain monotonicity. The interpolation is performed for both zeta and pressure profiles.

Parameters
ctlA pointer to a control parameter structure.
metA pointer to a structure containing meteorological data.

The function performs the following steps:

  • Sets up a timer to monitor the processing time.
  • Iterates over each grid point in parallel using OpenMP.
  • Identifies inversions in both zeta and pressure profiles and interpolates linearly between them to make the profiles monotone increasing.
Note
This function is crucial for maintaining the physical consistency of meteorological profiles, ensuring accurate atmospheric simulations.
Author
Jan Clemens

Definition at line 12135 of file mptrac.c.

12137 {
12138
12139 /* Check parameters... */
12140 if (ctl->advect_vert_coord != 1)
12141 return;
12142
12143 /* Set timer... */
12144 SELECT_TIMER("READ_MET_MONOTONIZE", "METPROC");
12145 LOG(2, "Make zeta profiles monotone...");
12146
12147 /* Create monotone zeta profiles... */
12148#pragma omp parallel for default(shared) collapse(2)
12149 for (int i = 0; i < met->nx; i++)
12150 for (int j = 0; j < met->ny; j++) {
12151 int k = 1;
12152
12153 while (k < met->npl) { /* Check if there is an inversion at level k... */
12154 if ((met->zetal[i][j][k - 1] >= met->zetal[i][j][k])) {
12155 /* Find the upper level k+l over the inversion... */
12156 int l = 0;
12157 do {
12158 l++;
12159 }
12160 while ((met->zetal[i][j][k - 1] >=
12161 met->zetal[i][j][k + l]) & (k + l < met->npl));
12162
12163 /* Interpolate linear between the top and bottom
12164 of the inversion... */
12165 float s =
12166 (float) (met->zetal[i][j][k + l] - met->zetal[i][j][k - 1])
12167 / (float) (met->hybrid[k + l] - met->hybrid[k - 1]);
12168
12169 for (int m = k; m < k + l; m++) {
12170 float d = (float) (met->hybrid[m] - met->hybrid[k - 1]);
12171 met->zetal[i][j][m] = s * d + met->zetal[i][j][k - 1];
12172 }
12173
12174 /* Search for more inversions above the last inversion ... */
12175 k = k + l;
12176 } else {
12177 k++;
12178 }
12179 }
12180 }
12181
12182 /* Create monotone pressure profiles... */
12183#pragma omp parallel for default(shared) collapse(2)
12184 for (int i = 0; i < met->nx; i++)
12185 for (int j = 0; j < met->ny; j++) {
12186 int k = 1;
12187
12188 while (k < met->npl) { /* Check if there is an inversion at level k... */
12189 if ((met->pl[i][j][k - 1] <= met->pl[i][j][k])) {
12190
12191 /* Find the upper level k+l over the inversion... */
12192 int l = 0;
12193 do {
12194 l++;
12195 }
12196 while ((met->pl[i][j][k - 1] <= met->pl[i][j][k + l]) & (k + l <
12197 met->npl));
12198
12199 /* Interpolate linear between the top and bottom
12200 of the inversion... */
12201 float s = (float) (met->pl[i][j][k + l] - met->pl[i][j][k - 1])
12202 / (float) (met->hybrid[k + l] - met->hybrid[k - 1]);
12203
12204 for (int m = k; m < k + l; m++) {
12205 float d = (float) (met->hybrid[m] - met->hybrid[k - 1]);
12206 met->pl[i][j][m] = s * d + met->pl[i][j][k - 1];
12207 }
12208
12209 /* Search for more inversions above the last inversion ... */
12210 k += l;
12211 } else {
12212 k++;
12213 }
12214 }
12215 }
12216}

◆ read_met_nc()

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.

This function reads meteorological data from a NetCDF file specified by the filename parameter, using the NetCDF library. It reads grid, surface, and vertical level data, processes the data (including extrapolation, boundary conditions, and downsampling), and calculates various derived meteorological fields such as geopotential heights, potential vorticity, cloud properties, and convective available potential energy (CAPE).

Parameters
filenameA constant character pointer representing the name of the NetCDF file to read the meteorological data from.
ctlA pointer to a ctl_t structure, which contains control parameters for reading and processing the meteorological data.
metA pointer to a met_t structure that will store the meteorological data read and processed from the NetCDF file.
ddA pointer to an dd_t structure containing MPI information, including rank and neighbours.
Returns
Returns 1 on success, or 0 if the file cannot be opened.
Note
  • The function opens the NetCDF file in read-only mode using nc_open and handles any errors during the file opening process.
  • The function reads grid data, vertical level data, and surface data from the file, and processes the data to calculate additional meteorological parameters.
  • If the file cannot be opened, the function logs a warning and returns 0.
  • It is important to ensure that the NetCDF file contains the expected structure for meteorological data (grid, levels, surface data).
Author
Lars Hoffmann

Definition at line 12220 of file mptrac.c.

12224 {
12225
12226 int ncid = 0;
12227 int have_ncid = 0;
12228
12229 /* With domain decomposition, all tasks collectively open the file in
12230 parallel mode and each task reads its own subdomain... */
12231#ifdef DD
12232 if (ctl->dd) {
12233 NC(nc_open_par
12234 (filename, NC_NOWRITE | NC_SHARE, MPI_COMM_WORLD, MPI_INFO_NULL,
12235 &ncid));
12236 have_ncid = 1;
12237 }
12238#endif
12239
12240 /* Without domain decomposition, the file is opened in serial mode... */
12241 if (!have_ncid && nc_open(filename, NC_NOWRITE, &ncid) != NC_NOERR) {
12242 WARN("Cannot open file!");
12243 return 0;
12244 }
12245
12246 /* Set coordinate system of meteo data... */
12247 met->coord_type = ctl->met_coord_type;
12248
12249 /* Read coordinates of meteo data... */
12250 read_met_nc_grid(filename, ncid, ctl, met, dd);
12251
12252 /* Setup for global read mode happens under the general input timer... */
12253 SELECT_TIMER("GET_MET", "INPUT");
12254#ifdef DD
12255 if (ctl->dd && ctl->met_nc_read_mode == 1)
12256 met_global_2d_init(dd, met);
12257#endif
12258
12259 /* Read surface data... */
12260 read_met_nc_surface(ncid, ctl, met, dd);
12261
12262 /* Read meteo data on vertical levels... */
12263 read_met_nc_levels(ncid, ctl, met, dd);
12264
12265 /* The remaining work happens under the general input timer... */
12266 SELECT_TIMER("GET_MET", "INPUT");
12267
12268#ifdef DD
12269 if (ctl->dd && ctl->met_nc_read_mode == 1)
12270 met_global_2d_free();
12271#endif
12272
12273 /* Close file... */
12274 NC(nc_close(ncid));
12275
12276 /* Return success... */
12277 return 1;
12278}
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:10574
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:10417
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:10230
Here is the call graph for this function:

◆ dd_read_met_nc_grid()

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.

This routine reads the global longitude and latitude grid from a NetCDF meteorological input file and initializes the domain decomposition used for parallel processing. The global grid is partitioned into zonal and meridional subdomains according to ctl->dd_subdomains_zonal and ctl->dd_subdomains_meridional.

For the current MPI rank, the routine determines the core subdomain in index space and constructs a hyperslab describing the portion of the meteorological grid that must be read from the NetCDF file. The hyperslab is extended with inner halo cells to overlap with neighboring subdomains. For ranks located at the zonal boundaries, additional periodic boundary halos are created.

The function populates the local longitude and latitude arrays in the met_t structure for the subdomain including halos, and stores the hyperslab definitions and halo offsets in the dd_t structure.

Parameters
[out]ddDomain decomposition structure. On return it contains global grid dimensions, hyperslab definitions for the local subdomain, and halo metadata.
[in]ctlControl structure specifying the number of zonal and meridional subdomains and the halo size (dd_subdomains_zonal, dd_subdomains_meridional, dd_halos_size).
[out]metMeteorological grid structure. On return it contains the local longitude and latitude arrays and the grid dimensions (nx, ny, np) of the halo-extended subdomain.
[in]ncidNetCDF file identifier returned by nc_open().
Note
  • Requires MPI when compiled with -DMPI.
  • The domain decomposition is performed in index space; the global grid may therefore have irregular spacing (e.g. Gaussian latitudes).
  • Zonal periodicity is handled by constructing additional boundary halo hyperslabs with appropriate longitude shifts.
Author
Jan Clemens
Lars Hoffmann

Definition at line 12282 of file mptrac.c.

12286 {
12287
12288 int varid;
12289
12290 /* Get the MPI information... */
12291 int rank = 0, size = 1;
12292#ifdef MPI
12293 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
12294 MPI_Comm_size(MPI_COMM_WORLD, &size);
12295#endif
12296
12297 /* Get grid dimensions... */
12298 NC_INQ_DIM("lon", &dd->nx_glob, 0, 0, 0);
12299 NC_INQ_DIM("lat", &dd->ny_glob, 0, 0, 0);
12300
12301 LOG(2, "Number of longitudes: %d", dd->nx_glob);
12302 LOG(2, "Number of latitudes: %d", dd->ny_glob);
12303
12304 /* Check grid... */
12305 if (dd->nx_glob > DD_EX_GLOB || dd->ny_glob > DD_EY_GLOB)
12306 ERRMSG("Global grid is too large!");
12307
12308 if (ctl->dd_subdomains_zonal > dd->nx_glob)
12309 ERRMSG("Too many zonal subdomains for global x grid!");
12310
12311 if (ctl->dd_subdomains_meridional > dd->ny_glob)
12312 ERRMSG("Too many meridional subdomains for global y grid!");
12313
12314 /* Read global longitudes and latitudes... */
12315 NC_GET_DOUBLE("lon", dd->lon_glob, 1);
12316 NC_GET_DOUBLE("lat", dd->lat_glob, 1);
12317
12318 LOG(2, "Longitudes: %g, %g ... %g deg",
12319 dd->lon_glob[0], dd->lon_glob[1], dd->lon_glob[dd->nx_glob - 1]);
12320 LOG(2, "Latitudes: %g, %g ... %g deg",
12321 dd->lat_glob[0], dd->lat_glob[1], dd->lat_glob[dd->ny_glob - 1]);
12322
12323 /* Rank coordinates in DD layout... */
12324 const int zonal_rank = rank / ctl->dd_subdomains_meridional;
12325 const int merid_rank = rank % ctl->dd_subdomains_meridional;
12326
12327 /* Check for edge cases... */
12328 const int left = (zonal_rank == 0);
12329 const int right = (zonal_rank == ctl->dd_subdomains_zonal - 1);
12330 const int top = (merid_rank == 0);
12331 const int bottom = (merid_rank == ctl->dd_subdomains_meridional - 1);
12332
12333 /* Core owned block (without halos)... */
12334 const int nx_block = dd->nx_glob / ctl->dd_subdomains_zonal;
12335 const int ny_block = dd->ny_glob / ctl->dd_subdomains_meridional;
12336
12337 const int ix0 = zonal_rank * nx_block;
12338 const int iy0 = merid_rank * ny_block;
12339
12340 int nx_core = nx_block;
12341 int ny_core = ny_block;
12342
12343 if (right)
12344 nx_core += dd->nx_glob - ctl->dd_subdomains_zonal * nx_block;
12345 if (bottom)
12346 ny_core += dd->ny_glob - ctl->dd_subdomains_meridional * ny_block;
12347
12348 /* Check halo extents before converting grid indices to size_t... */
12349 const int halo = ctl->dd_halos_size;
12350 if ((!left && halo > ix0)
12351 || (!right && halo > dd->nx_glob - ix0 - nx_core)
12352 || (!top && halo > iy0)
12353 || (!bottom && halo > dd->ny_glob - iy0 - ny_core))
12354 ERRMSG("DD_HALOS_SIZE exceeds the available global grid!");
12355
12356 const int nx_local = nx_core
12357 + (ctl->dd_subdomains_zonal > 1 ? 2 * halo : 0);
12358 const int ny_local = ny_core + (!top ? halo : 0) + (!bottom ? halo : 0);
12359 if (nx_local > EX || ny_local > EY)
12360 ERRMSG("DD halo grid exceeds EX or EY. Increase the grid capacity!");
12361 if (nx_local < 2 || ny_local < 2)
12362 ERRMSG("DD halo grid needs at least two points in each direction!");
12363
12364 /* Store core met size first... */
12365 met->nx = nx_core;
12366 met->ny = ny_core;
12367
12368 /* Set hyperslab for core subdomain... */
12369 dd->subdomain_start[0] = 0;
12370 dd->subdomain_start[1] = 0;
12371 dd->subdomain_start[2] = (size_t) iy0;
12372 dd->subdomain_start[3] = (size_t) ix0;
12373
12374 dd->subdomain_count[0] = 1;
12375 dd->subdomain_count[1] = (size_t) met->np;
12376 dd->subdomain_count[2] = (size_t) ny_core;
12377 dd->subdomain_count[3] = (size_t) nx_core;
12378
12379 /* Add inner halos to read window... */
12380 if (!left && !right) {
12381 dd->subdomain_start[3] -= (size_t) ctl->dd_halos_size;
12382 dd->subdomain_count[3] += (size_t) (2 * ctl->dd_halos_size);
12383 } else if (left ^ right) {
12384 dd->subdomain_count[3] += (size_t) ctl->dd_halos_size;
12385 if (!left)
12386 dd->subdomain_start[3] -= (size_t) ctl->dd_halos_size;
12387 }
12388
12389 if (!top && !bottom) {
12390 dd->subdomain_start[2] -= (size_t) ctl->dd_halos_size;
12391 dd->subdomain_count[2] += (size_t) (2 * ctl->dd_halos_size);
12392 } else if (top ^ bottom) {
12393 dd->subdomain_count[2] += (size_t) ctl->dd_halos_size;
12394 if (!top)
12395 dd->subdomain_start[2] -= (size_t) ctl->dd_halos_size;
12396 }
12397
12398 /* Set boundary halo hyperslabs... */
12399 double lon_shift = 0.0;
12400
12401 if (left ^ right) {
12402 dd->halo_bnd_start[0] = 0;
12403 dd->halo_bnd_start[1] = 0;
12404 dd->halo_bnd_start[2] = dd->subdomain_start[2];
12405 dd->halo_bnd_start[3] =
12406 (size_t) (left ? (dd->nx_glob - ctl->dd_halos_size) : 0);
12407
12408 dd->halo_bnd_count[0] = 1;
12409 dd->halo_bnd_count[1] = (size_t) met->np;
12410 dd->halo_bnd_count[2] = dd->subdomain_count[2];
12411 dd->halo_bnd_count[3] = (size_t) ctl->dd_halos_size;
12412
12413 dd->halo_offset_start = left ? (int) dd->halo_bnd_count[3] : 0;
12414 dd->halo_offset_end = left ? 0 : (int) dd->subdomain_count[3];
12415 lon_shift = left ? -360.0 : 360.0;
12416 } else {
12417 dd->halo_bnd_start[0] = 0;
12418 dd->halo_bnd_start[1] = 0;
12419 dd->halo_bnd_start[2] = 0;
12420 dd->halo_bnd_start[3] = 0;
12421
12422 dd->halo_bnd_count[0] = 0;
12423 dd->halo_bnd_count[1] = 0;
12424 dd->halo_bnd_count[2] = 0;
12425 dd->halo_bnd_count[3] = 0;
12426
12427 dd->halo_offset_start = 0;
12428 dd->halo_offset_end = 0;
12429 }
12430
12431 /* Focus on subdomain latitudes... */
12432 for (int iy = 0; iy < (int) dd->subdomain_count[2]; iy++)
12433 met->lat[iy] = dd->lat_glob[(int) dd->subdomain_start[2] + iy];
12434
12435 /* Focus on subdomain longitudes... */
12436 for (int ix = 0; ix < (int) dd->subdomain_count[3]; ix++)
12437 met->lon[ix + dd->halo_offset_start] =
12438 dd->lon_glob[(int) dd->subdomain_start[3] + ix];
12439
12440 for (int ix = 0; ix < (int) dd->halo_bnd_count[3]; ix++)
12441 met->lon[ix + dd->halo_offset_end] =
12442 dd->lon_glob[(int) dd->halo_bnd_start[3] + ix] + lon_shift;
12443
12444 /* Reset halo-extended grid dimensions... */
12445 met->nx = (int) dd->subdomain_count[3] + (int) dd->halo_bnd_count[3];
12446 met->ny = (int) dd->subdomain_count[2];
12447
12448 LOG(2, "Define subdomain properties.");
12449 LOG(2, "MPI information: Rank %d, Size %d", rank, size);
12450 LOG(2, "Edge position: l=%d,r=%d,t=%d,b=%d", left, right, top, bottom);
12451 LOG(2, "Total size for subdomain meteo data: nx %d ny %d np %d",
12452 met->nx, met->ny, met->np);
12453 LOG(2, "Hyperslab sizes for boundary halos: nx %d ny %d np %d",
12454 (int) dd->halo_bnd_count[3], (int) dd->halo_bnd_count[2],
12455 (int) dd->halo_bnd_count[1]);
12456 LOG(2, "Hyperslab sizes for subdomain and inner halos: nx %d ny %d np %d",
12457 (int) dd->subdomain_count[3], (int) dd->subdomain_count[2],
12458 (int) dd->subdomain_count[1]);
12459 LOG(2, "Subdomain start: nx %ld ny %ld np %ld",
12460 dd->subdomain_start[3], dd->subdomain_start[2], dd->subdomain_start[1]);
12461 LOG(2, "Boundary halo start: nx %ld ny %ld np %ld",
12462 dd->halo_bnd_start[3], dd->halo_bnd_start[2], dd->halo_bnd_start[1]);
12463 LOG(2, "Offsets: nx %d ny %d", dd->halo_offset_start, dd->halo_offset_end);
12464 LOG(2, "%d Subdomain longitudes: %g, %g ... %g deg",
12465 rank, met->lon[0], met->lon[1], met->lon[met->nx - 1]);
12466 LOG(2, "%d Subdomain latitudes: %g, %g ... %g deg",
12467 rank, met->lat[0], met->lat[1], met->lat[met->ny - 1]);
12468}
#define DD_EY_GLOB
Maximum number of latitudes of global meteo data.
Definition mptrac.h:634
#define DD_EX_GLOB
Maximum number of longitudes of global meteo data.
Definition mptrac.h:629

◆ read_met_pbl()

void read_met_pbl ( const ctl_t * ctl,
met_t * met )

Computes the planetary boundary layer (PBL) pressure based on meteorological data.

This function determines the PBL pressure for each grid point using one of four methods:

  1. Read PBL pressure from meteo data file.
  2. Read PBL heights from meteo data file and convert to pressure.
  3. Determine PBL pressure based on bulk Richardson number criterion.
  4. Determine PBL pressure Based on potential temperature difference. The calculated PBL height is constrained by user-defined minimum and maximum limits.
Parameters
[in]ctlPointer to the control structure (ctl_t), which contains parameters controlling the PBL calculation.
[in,out]metPointer to the meteorological data structure (met_t), which contains grid and atmospheric data. The met->pbl array is updated with the calculated PBL pressure.

Method 0 (Precomputed PBL pressure from file):

  • Read PBL pressure from meteo data file.

Method 1 (Precomputed PBL height from file):

  • Read PBL height from meteo data file.
  • Interpolates the PBL pressure using the geopotential heights from the meteo file.

Method 2 (Richardson number criterion):

  • Implements a method based on the bulk Richardson number (critical value: Ri = 0.25).
  • Iteratively evaluates vertical levels, calculating wind shear, and thermal gradients, until the Richardson number exceeds the critical threshold.
  • Interpolates between levels to find the precise height.

Method 3 (Potential temperature difference):

  • Computes the PBL height as the altitude where the potential temperature exceeds the surface value by 2 K.
  • Interpolates between levels to find the precise height.

Final Adjustments:

  • Ensures the PBL height respects user-defined minimum and maximum thresholds.
Note
Method 2 is a standard method for estimating PBL depths, but the current implementation seems to underestimate PBL depths compared to ECMWF PBL data or Method 3. Therefore, Method 3, is selected by default. If PBL heights are available from the meteo data files, it is recommended to select Method 1.
Author
Lars Hoffmann

Definition at line 12472 of file mptrac.c.

12474 {
12475
12476 /* Set timer... */
12477 SELECT_TIMER("READ_MET_PBL", "METPROC");
12478 LOG(2, "Calculate planetary boundary layer...");
12479
12480 /* Convert PBL height from meteo file to pressure... */
12481 if (ctl->met_pbl == 1) {
12482
12483 /* Loop over grid points... */
12484#pragma omp parallel for default(shared) collapse(2)
12485 for (int ix = 0; ix < met->nx; ix++)
12486 for (int iy = 0; iy < met->ny; iy++) {
12487
12488 /* Get pressure at top of PBL... */
12489 const float z = met->zs[ix][iy] + met->pbl[ix][iy];
12490 const int ip = locate_irr_float(met->z[ix][iy], met->np, z, 0);
12491 met->pbl[ix][iy] =
12492 (float) (LIN(met->z[ix][iy][ip], met->p[ip],
12493 met->z[ix][iy][ip + 1], met->p[ip + 1], z));
12494 }
12495 }
12496
12497 /* Determine PBL based on Richardson number... */
12498 else if (ctl->met_pbl == 2) {
12499
12500 /* Parameters used to estimate the height of the PBL
12501 (e.g., Vogelezang and Holtslag, 1996; Seidel et al., 2012)... */
12502 const double rib_crit = 0.25, dz = 0.05, umin = 5.0;
12503
12504 /* Loop over grid points... */
12505#pragma omp parallel for default(shared) collapse(2)
12506 for (int ix = 0; ix < met->nx; ix++)
12507 for (int iy = 0; iy < met->ny; iy++) {
12508
12509 /* Set bottom level of PBL... */
12510 const double pbl_bot = met->ps[ix][iy] * exp(-dz / H0);
12511
12512 /* Find lowest level near the bottom... */
12513 int ip;
12514 for (ip = 1; ip < met->np; ip++)
12515 if (met->p[ip] < pbl_bot)
12516 break;
12517
12518 /* Get near surface data... */
12519 const double h2os = LIN(met->p[ip - 1], met->h2o[ix][iy][ip - 1],
12520 met->p[ip], met->h2o[ix][iy][ip], pbl_bot);
12521 const double tvs = THETAVIRT(pbl_bot, met->ts[ix][iy], h2os);
12522
12523 /* Init... */
12524 double rib_old = 0;
12525
12526 /* Loop over levels... */
12527 for (; ip < met->np; ip++) {
12528
12529 /* Get squared horizontal wind speed... */
12530 double vh2 = SQR(met->u[ix][iy][ip] - met->us[ix][iy])
12531 + SQR(met->v[ix][iy][ip] - met->vs[ix][iy]);
12532 vh2 = MAX(vh2, SQR(umin));
12533
12534 /* Calculate bulk Richardson number... */
12535 const double rib =
12536 G0 * 1e3 * (met->z[ix][iy][ip] - met->zs[ix][iy]) / tvs
12537 * (THETAVIRT(met->p[ip], met->t[ix][iy][ip],
12538 met->h2o[ix][iy][ip]) - tvs) / vh2;
12539
12540 /* Check for critical value... */
12541 if (rib >= rib_crit) {
12542 met->pbl[ix][iy] = (float) (LIN(rib_old, met->p[ip - 1],
12543 rib, met->p[ip], rib_crit));
12544 if (met->pbl[ix][iy] > pbl_bot)
12545 met->pbl[ix][iy] = (float) pbl_bot;
12546 break;
12547 }
12548
12549 /* Save Richardson number... */
12550 rib_old = rib;
12551 }
12552 }
12553 }
12554
12555 /* Determine PBL based on potential temperature... */
12556 if (ctl->met_pbl == 3) {
12557
12558 /* Parameters used to estimate the height of the PBL
12559 (following HYSPLIT model)... */
12560 const double dtheta = 2.0, zmin = 0.1;
12561
12562 /* Loop over grid points... */
12563#pragma omp parallel for default(shared) collapse(2)
12564 for (int ix = 0; ix < met->nx; ix++)
12565 for (int iy = 0; iy < met->ny; iy++) {
12566
12567 /* Potential temperature at the surface... */
12568 const double theta0 = THETA(met->ps[ix][iy], met->ts[ix][iy]);
12569
12570 /* Find topmost level where theta exceeds surface value by 2 K... */
12571 int ip;
12572 for (ip = met->np - 2; ip > 0; ip--)
12573 if (met->p[ip] >= 300.)
12574 if (met->p[ip] > met->ps[ix][iy]
12575 || THETA(met->p[ip], met->t[ix][iy][ip]) <= theta0 + dtheta)
12576 break;
12577
12578 /* Interpolate... */
12579 met->pbl[ix][iy]
12580 = (float) (LIN(THETA(met->p[ip + 1], met->t[ix][iy][ip + 1]),
12581 met->p[ip + 1],
12582 THETA(met->p[ip], met->t[ix][iy][ip]),
12583 met->p[ip], theta0 + dtheta));
12584
12585 /* Check minimum value... */
12586 double pbl_min = met->ps[ix][iy] * exp(-zmin / H0);
12587 if (met->pbl[ix][iy] > pbl_min || met->p[ip] > met->ps[ix][iy])
12588 met->pbl[ix][iy] = (float) pbl_min;
12589 }
12590 }
12591
12592 /* Loop over grid points... */
12593#pragma omp parallel for default(shared) collapse(2)
12594 for (int ix = 0; ix < met->nx; ix++)
12595 for (int iy = 0; iy < met->ny; iy++) {
12596
12597 /* Check minimum value... */
12598 double pbl_min = met->ps[ix][iy] * exp(-ctl->met_pbl_min / H0);
12599 met->pbl[ix][iy] = MIN(met->pbl[ix][iy], (float) pbl_min);
12600
12601 /* Check maximum value... */
12602 double pbl_max = met->ps[ix][iy] * exp(-ctl->met_pbl_max / H0);
12603 met->pbl[ix][iy] = MAX(met->pbl[ix][iy], (float) pbl_max);
12604 }
12605}
Here is the call graph for this function:

◆ read_met_periodic()

void read_met_periodic ( met_t * met)

Applies periodic boundary conditions to meteorological data along longitudinal axis.

This function applies periodic boundary conditions to meteorological data along the longitudinal axis. It checks if the difference between the last and first longitudes and the difference between the second and first longitudes are approximately equal to 360 degrees, indicating periodicity. If the condition is met, the function increases the longitude counter, sets the longitude value for the new grid point, and copies meteorological data from the first grid point to the last grid point to ensure periodicity.

Parameters
metA pointer to a structure containing meteorological data.

The function performs the following steps:

  • Sets timer for performance monitoring.
  • Checks if the difference between the last and first longitudes and the difference between the second and first longitudes are approximately equal to 360 degrees, indicating periodicity.
  • If periodicity is confirmed:
    • Increases the longitude counter.
    • Sets the longitude value for the new grid point by adding the difference between the second and first longitudes to the longitude of the penultimate grid point.
    • Copies meteorological data from the first grid point to the last grid point to ensure periodicity:
      • Surface variables (e.g., pressure, temperature, wind speed, land-sea mask, sea surface temperature) are copied.
      • Meteorological variables at each pressure level are copied.
      • Meteorological variables at each hybrid pressure level are copied.
Note
This function is useful for generating continuous meteorological fields over a periodic domain, which is common in atmospheric modeling, especially for global simulations.
Author
Lars Hoffmann

Definition at line 12609 of file mptrac.c.

12610 {
12611
12612 /* Set timer... */
12613 SELECT_TIMER("READ_MET_PERIODIC", "METPROC");
12614 LOG(2, "Apply periodic boundary conditions...");
12615
12616 /* Check longitudes... */
12617 if (!(fabs(met->lon[met->nx - 1] - met->lon[0]
12618 + met->lon[1] - met->lon[0] - 360) < 0.01))
12619 return;
12620
12621 /* Increase longitude counter... */
12622 if ((++met->nx) >= EX)
12623 ERRMSG("Cannot create periodic boundary conditions!");
12624
12625 /* Set longitude... */
12626 met->lon[met->nx - 1] = met->lon[met->nx - 2] + met->lon[1] - met->lon[0];
12627
12628 /* Loop over latitudes and pressure levels... */
12629#pragma omp parallel for default(shared)
12630 for (int iy = 0; iy < met->ny; iy++) {
12631 met->ps[met->nx - 1][iy] = met->ps[0][iy];
12632 met->zs[met->nx - 1][iy] = met->zs[0][iy];
12633 met->ts[met->nx - 1][iy] = met->ts[0][iy];
12634 met->us[met->nx - 1][iy] = met->us[0][iy];
12635 met->vs[met->nx - 1][iy] = met->vs[0][iy];
12636 met->ess[met->nx - 1][iy] = met->ess[0][iy];
12637 met->nss[met->nx - 1][iy] = met->nss[0][iy];
12638 met->shf[met->nx - 1][iy] = met->shf[0][iy];
12639 met->lsm[met->nx - 1][iy] = met->lsm[0][iy];
12640 met->sst[met->nx - 1][iy] = met->sst[0][iy];
12641 met->pbl[met->nx - 1][iy] = met->pbl[0][iy];
12642 met->cape[met->nx - 1][iy] = met->cape[0][iy];
12643 met->cin[met->nx - 1][iy] = met->cin[0][iy];
12644 for (int ip = 0; ip < met->np; ip++) {
12645 met->t[met->nx - 1][iy][ip] = met->t[0][iy][ip];
12646 met->u[met->nx - 1][iy][ip] = met->u[0][iy][ip];
12647 met->v[met->nx - 1][iy][ip] = met->v[0][iy][ip];
12648 met->w[met->nx - 1][iy][ip] = met->w[0][iy][ip];
12649 met->h2o[met->nx - 1][iy][ip] = met->h2o[0][iy][ip];
12650 met->o3[met->nx - 1][iy][ip] = met->o3[0][iy][ip];
12651 met->lwc[met->nx - 1][iy][ip] = met->lwc[0][iy][ip];
12652 met->rwc[met->nx - 1][iy][ip] = met->rwc[0][iy][ip];
12653 met->iwc[met->nx - 1][iy][ip] = met->iwc[0][iy][ip];
12654 met->swc[met->nx - 1][iy][ip] = met->swc[0][iy][ip];
12655 met->cc[met->nx - 1][iy][ip] = met->cc[0][iy][ip];
12656 }
12657 for (int ip = 0; ip < met->npl; ip++) {
12658 met->ul[met->nx - 1][iy][ip] = met->ul[0][iy][ip];
12659 met->vl[met->nx - 1][iy][ip] = met->vl[0][iy][ip];
12660 met->wl[met->nx - 1][iy][ip] = met->wl[0][iy][ip];
12661 met->pl[met->nx - 1][iy][ip] = met->pl[0][iy][ip];
12662 met->zetal[met->nx - 1][iy][ip] = met->zetal[0][iy][ip];
12663 met->zeta_dotl[met->nx - 1][iy][ip] = met->zeta_dotl[0][iy][ip];
12664 }
12665 }
12666}

◆ read_met_polar_winds()

void read_met_polar_winds ( met_t * met)

Applies a fix for polar winds in meteorological data.

This function applies a fix for polar winds in meteorological data, particularly focusing on the u and v wind components. It checks if the latitudes at the top and bottom of the grid are close to the poles. If so, it transforms the winds at 89-degree latitude into Cartesian coordinates, takes their mean, and replaces the winds at 90-degree latitude with this mean, effectively fixing the unrealistic behavior of winds at the poles.

Parameters
metA pointer to a structure containing meteorological data.

The function performs the following steps:

  • Sets a timer for performance monitoring.
  • Checks if the latitudes at the top and bottom of the grid are close to the poles (within 0.001 degree latitude of the poles).
  • For each hemisphere (north and south):
    • Sets latitude indices for 89 degrees and 90 degrees.
    • Determines the sign of longitude adjustments based on the hemisphere.
    • Constructs lookup tables for cosine and sine of longitudes.
    • Loops over pressure levels and performs the following operations:
      • Transforms u and v wind components at 89 degrees latitude into Cartesian coordinates and calculates their mean.
      • Replaces u and v wind components at 90 degrees latitude with the calculated mean, effectively fixing the polar winds.
Note
This function is useful for correcting unrealistic behavior of winds near the poles in meteorological data, which can affect various atmospheric simulations.
Based on a Python code provided by Jens-Uwe Grooß.
Author
Lars Hoffmann

Definition at line 12670 of file mptrac.c.

12671 {
12672
12673 /* Set timer... */
12674 SELECT_TIMER("READ_MET_POLAR_WINDS", "METPROC");
12675 LOG(2, "Apply fix for polar winds...");
12676
12677 if (met->coord_type != 0)
12678 return;
12679
12680 /* Check latitudes... */
12681 if (fabs(met->lat[0]) < 89.999 || fabs(met->lat[met->ny - 1]) < 89.999)
12682 return;
12683
12684 /* Loop over hemispheres... */
12685 for (int ihem = 0; ihem < 2; ihem++) {
12686
12687 /* Set latitude indices... */
12688 int i89 = 1, i90 = 0, sign = 1;
12689 if (ihem == 1) {
12690 i89 = met->ny - 2;
12691 i90 = met->ny - 1;
12692 }
12693 if (met->lat[i90] < 0)
12694 sign = -1;
12695
12696 /* Look-up table of cosinus and sinus... */
12697 double clon[EX], slon[EX];
12698#pragma omp parallel for default(shared)
12699 for (int ix = 0; ix < met->nx; ix++) {
12700 clon[ix] = cos(sign * DEG2RAD(met->lon[ix]));
12701 slon[ix] = sin(sign * DEG2RAD(met->lon[ix]));
12702 }
12703
12704 /* Loop over levels... */
12705#pragma omp parallel for default(shared)
12706 for (int ip = 0; ip < met->np; ip++) {
12707
12708 /* Transform 89 degree u and v winds into Cartesian coordinates and take the mean... */
12709 double vel89x = 0, vel89y = 0;
12710 for (int ix = 0; ix < met->nx; ix++) {
12711 vel89x +=
12712 (met->u[ix][i89][ip] * clon[ix] -
12713 met->v[ix][i89][ip] * slon[ix]) / met->nx;
12714 vel89y +=
12715 (met->u[ix][i89][ip] * slon[ix] +
12716 met->v[ix][i89][ip] * clon[ix]) / met->nx;
12717 }
12718
12719 /* Replace 90 degree winds by 89 degree mean... */
12720 for (int ix = 0; ix < met->nx; ix++) {
12721 met->u[ix][i90][ip]
12722 = (float) (vel89x * clon[ix] + vel89y * slon[ix]);
12723 met->v[ix][i90][ip]
12724 = (float) (-vel89x * slon[ix] + vel89y * clon[ix]);
12725 }
12726 }
12727 }
12728}

◆ read_met_pv()

void read_met_pv ( met_t * met)

Calculates potential vorticity (PV) from meteorological data.

This function calculates the potential vorticity (PV) from the provided meteorological data. It employs finite difference methods to estimate gradients of temperature, wind components, and pressure in longitude, latitude, and pressure dimensions. These gradients are then used to compute PV at each grid point. Additionally, a fix for polar regions is applied to ensure smoothness of PV values in these regions.

Parameters
metA pointer to a structure containing meteorological data.

The function performs the following steps:

  • Sets a timer for performance monitoring.
  • Computes powers for pressure calculation.
  • Loops over grid points in longitude:
    • Sets latitude indices.
    • Loops over grid points in latitude:
      • Sets indices and auxiliary variables.
      • Loops over pressure levels:
        • Computes gradients in longitude, latitude, and pressure.
        • Calculates PV using computed gradients.
  • Applies a fix for polar regions to ensure smoothness of PV values.
Note
Potential vorticity is a fundamental quantity in atmospheric dynamics, representing the potential of a fluid parcel to rotate due to changes in pressure, temperature, and wind fields.
Based on a Python code by Mathew Barlow (https://github.com/mathewbarlow/potential-vorticity).
Author
Lars Hoffmann

Definition at line 12732 of file mptrac.c.

12733 {
12734
12735 double pows[EP];
12736
12737 /* Set timer... */
12738 SELECT_TIMER("READ_MET_PV", "METPROC");
12739 LOG(2, "Calculate potential vorticity...");
12740
12741 /* Set powers... */
12742#pragma omp parallel for default(shared)
12743 for (int ip = 0; ip < met->np; ip++)
12744 pows[ip] = pow(1000. / met->p[ip], KAPPA);
12745
12746 /* Loop over grid points... */
12747#pragma omp parallel for default(shared)
12748 for (int ix = 0; ix < met->nx; ix++) {
12749
12750 /* Set indices... */
12751 const int ix0 = MAX(ix - 1, 0);
12752 const int ix1 = MIN(ix + 1, met->nx - 1);
12753
12754 /* Loop over grid points... */
12755 for (int iy = 0; iy < met->ny; iy++) {
12756
12757 /* Set indices... */
12758 const int iy0 = MAX(iy - 1, 0);
12759 const int iy1 = MIN(iy + 1, met->ny - 1);
12760
12761 /* Set auxiliary variables... */
12762 const double latr = 0.5 * (met->lat[iy1] + met->lat[iy0]);
12763 double dx, dy, c0, c1, cr, vort;
12764
12765 // Calculate potential vorticity..
12766 if (met->coord_type == 0) { // coords are lat/lon
12767 dx = 1000. * DEG2DX(met->lon[ix1] - met->lon[ix0], latr);
12768 dy = 1000. * DEG2DY(met->lat[iy1] - met->lat[iy0]);
12769 c0 = cos(DEG2RAD(met->lat[iy0]));
12770 c1 = cos(DEG2RAD(met->lat[iy1]));
12771 cr = cos(DEG2RAD(latr));
12772 vort = 2 * OMEGA_EARTH * sin(DEG2RAD(latr));
12773 } else { // coords are in meters
12774 dx = met->lon[ix1] - met->lon[ix0];
12775 dy = met->lat[iy1] - met->lat[iy0];
12776
12777 c0 = 1.0;
12778 c1 = 1.0;
12779 cr = 1.0;
12780
12781 vort = 2 * OMEGA_EARTH * sin(latr / (RE * 1000));
12782 }
12783
12784 /* Loop over grid points... */
12785 for (int ip = 0; ip < met->np; ip++) {
12786
12787 /* Get gradients in longitude... */
12788 const double dtdx
12789 = (met->t[ix1][iy][ip] - met->t[ix0][iy][ip]) * pows[ip] / dx;
12790 const double dvdx = (met->v[ix1][iy][ip] - met->v[ix0][iy][ip]) / dx;
12791
12792 /* Get gradients in latitude... */
12793 const double dtdy
12794 = (met->t[ix][iy1][ip] - met->t[ix][iy0][ip]) * pows[ip] / dy;
12795 const double dudy
12796 = (met->u[ix][iy1][ip] * c1 - met->u[ix][iy0][ip] * c0) / dy;
12797
12798 /* Set indices... */
12799 const int ip0 = MAX(ip - 1, 0);
12800 const int ip1 = MIN(ip + 1, met->np - 1);
12801
12802 /* Get gradients in pressure... */
12803 double dtdp, dudp, dvdp;
12804 const double dp0 = 100. * (met->p[ip] - met->p[ip0]);
12805 const double dp1 = 100. * (met->p[ip1] - met->p[ip]);
12806 if (ip != ip0 && ip != ip1) {
12807 double denom = dp0 * dp1 * (dp0 + dp1);
12808 dtdp = (dp0 * dp0 * met->t[ix][iy][ip1] * pows[ip1]
12809 - dp1 * dp1 * met->t[ix][iy][ip0] * pows[ip0]
12810 + (dp1 * dp1 - dp0 * dp0) * met->t[ix][iy][ip] * pows[ip])
12811 / denom;
12812 dudp = (dp0 * dp0 * met->u[ix][iy][ip1]
12813 - dp1 * dp1 * met->u[ix][iy][ip0]
12814 + (dp1 * dp1 - dp0 * dp0) * met->u[ix][iy][ip])
12815 / denom;
12816 dvdp = (dp0 * dp0 * met->v[ix][iy][ip1]
12817 - dp1 * dp1 * met->v[ix][iy][ip0]
12818 + (dp1 * dp1 - dp0 * dp0) * met->v[ix][iy][ip])
12819 / denom;
12820 } else {
12821 const double denom = dp0 + dp1;
12822 dtdp =
12823 (met->t[ix][iy][ip1] * pows[ip1] -
12824 met->t[ix][iy][ip0] * pows[ip0]) / denom;
12825 dudp = (met->u[ix][iy][ip1] - met->u[ix][iy][ip0]) / denom;
12826 dvdp = (met->v[ix][iy][ip1] - met->v[ix][iy][ip0]) / denom;
12827 }
12828
12829 /* Calculate PV... */
12830 met->pv[ix][iy][ip] = (float)
12831 (1e6 * G0 *
12832 (-dtdp * (dvdx - dudy / cr + vort) + dvdp * dtdx - dudp * dtdy));
12833 }
12834 }
12835 }
12836
12837 /* Fix for polar regions... */
12838#pragma omp parallel for default(shared)
12839 for (int ix = 0; ix < met->nx; ix++)
12840 for (int ip = 0; ip < met->np; ip++) {
12841 met->pv[ix][0][ip]
12842 = met->pv[ix][1][ip]
12843 = met->pv[ix][2][ip];
12844 met->pv[ix][met->ny - 1][ip]
12845 = met->pv[ix][met->ny - 2][ip]
12846 = met->pv[ix][met->ny - 3][ip];
12847 }
12848}
#define DEG2DY(dlat)
Convert a latitude difference to a distance in the y-direction (north-south).
Definition mptrac.h:842
#define OMEGA_EARTH
Angular velocity of Earth [s^-1].
Definition mptrac.h:330
#define DEG2DX(dlon, lat)
Convert a longitude difference to a distance in the x-direction (east-west) at a specific latitude.
Definition mptrac.h:821

◆ read_met_ozone()

void read_met_ozone ( met_t * met)

Calculates the total column ozone from meteorological ozone data.

This function calculates the total column ozone from the provided meteorological ozone data. It integrates ozone concentrations over altitude to obtain the column ozone density. The result is then converted to Dobson units, which represent the thickness of the ozone layer if compressed into one layer at standard temperature and pressure.

Parameters
metA pointer to a structure containing meteorological ozone data.

The function performs the following steps:

  • Sets a timer for performance monitoring.
  • Loops over columns in longitude and latitude:
    • Integrates ozone concentrations over altitude.
    • Converts the integrated ozone density to Dobson units.
Note
Total column ozone is a critical metric for understanding ozone distribution in the atmosphere, with implications for climate, air quality, and UV radiation.
Author
Lars Hoffmann

Definition at line 12852 of file mptrac.c.

12853 {
12854
12855 /* Set timer... */
12856 SELECT_TIMER("READ_MET_OZONE", "METPROC");
12857 LOG(2, "Calculate total column ozone...");
12858
12859 /* Loop over columns... */
12860#pragma omp parallel for default(shared) collapse(2)
12861 for (int ix = 0; ix < met->nx; ix++)
12862 for (int iy = 0; iy < met->ny; iy++) {
12863
12864 /* Integrate... */
12865 double cd = 0;
12866 for (int ip = 1; ip < met->np; ip++)
12867 if (met->p[ip - 1] <= met->ps[ix][iy]) {
12868 const double vmr =
12869 0.5 * (met->o3[ix][iy][ip - 1] + met->o3[ix][iy][ip]);
12870 const double dp = met->p[ip - 1] - met->p[ip];
12871 cd += vmr * MO3 / MA * dp * 1e2 / G0;
12872 }
12873
12874 /* Convert to Dobson units... */
12875 met->o3c[ix][iy] = (float) (cd / DOBSON_UNIT);
12876 }
12877}
#define DOBSON_UNIT
Ozone column mass corresponding to one Dobson unit [kg/m^2].
Definition mptrac.h:335

◆ read_met_sample()

void read_met_sample ( const ctl_t * ctl,
met_t * met )

Downsamples meteorological data based on specified parameters.

This function downsamples meteorological data based on the provided control parameters. It reduces the resolution of meteorological data by averaging over specified intervals in longitude, latitude, and altitude.

Parameters
ctlA pointer to a structure containing control parameters for downsampling.
metA pointer to a structure containing meteorological data to be downsampled.

The function performs the following steps:

  • Checks if downsampling parameters are set to a value less than or equal to 1, if so, returns without downsampling.
  • Sets a timer for performance monitoring.
  • Allocates memory for a temporary meteorological data structure.
  • Copies metadata from the original structure to the temporary structure.
  • Performs downsampling by smoothing over specified intervals:
    • Computes weighted averages over the specified intervals.
    • Updates the temporary structure with the smoothed values.
  • Downsamples the smoothed data:
    • Updates longitude and latitude arrays with downsampled values.
    • Stores downsampled meteorological variables in the original structure.
  • Frees memory allocated for the temporary structure.
Note
Downsampling meteorological data can be useful for reducing computational cost while preserving essential features for modeling and analysis.
Author
Lars Hoffmann

Definition at line 12881 of file mptrac.c.

12883 {
12884
12885 met_t *help;
12886
12887 /* Check parameters... */
12888 if (ctl->met_dp <= 1 && ctl->met_dx <= 1 && ctl->met_dy <= 1
12889 && ctl->met_sp <= 1 && ctl->met_sx <= 1 && ctl->met_sy <= 1)
12890 return;
12891
12892 /* Set timer... */
12893 SELECT_TIMER("READ_MET_SAMPLE", "METPROC");
12894 LOG(2, "Downsampling of meteo data...");
12895
12896 /* Allocate... */
12897 ALLOC(help, met_t, 1);
12898
12899 /* Copy data... */
12900 help->nx = met->nx;
12901 help->ny = met->ny;
12902 help->np = met->np;
12903 memcpy(help->lon, met->lon, sizeof(met->lon));
12904 memcpy(help->lat, met->lat, sizeof(met->lat));
12905 memcpy(help->p, met->p, sizeof(met->p));
12906
12907 /* Smoothing... */
12908 for (int ix = 0; ix < met->nx; ix += ctl->met_dx) {
12909 for (int iy = 0; iy < met->ny; iy += ctl->met_dy) {
12910 for (int ip = 0; ip < met->np; ip += ctl->met_dp) {
12911 help->ps[ix][iy] = 0;
12912 help->zs[ix][iy] = 0;
12913 help->ts[ix][iy] = 0;
12914 help->us[ix][iy] = 0;
12915 help->vs[ix][iy] = 0;
12916 help->ess[ix][iy] = 0;
12917 help->nss[ix][iy] = 0;
12918 help->shf[ix][iy] = 0;
12919 help->lsm[ix][iy] = 0;
12920 help->sst[ix][iy] = 0;
12921 help->pbl[ix][iy] = 0;
12922 help->cape[ix][iy] = 0;
12923 help->cin[ix][iy] = 0;
12924 help->t[ix][iy][ip] = 0;
12925 help->u[ix][iy][ip] = 0;
12926 help->v[ix][iy][ip] = 0;
12927 help->w[ix][iy][ip] = 0;
12928 help->h2o[ix][iy][ip] = 0;
12929 help->o3[ix][iy][ip] = 0;
12930 help->lwc[ix][iy][ip] = 0;
12931 help->rwc[ix][iy][ip] = 0;
12932 help->iwc[ix][iy][ip] = 0;
12933 help->swc[ix][iy][ip] = 0;
12934 help->cc[ix][iy][ip] = 0;
12935 float wsum = 0;
12936 for (int ix2 = ix - ctl->met_sx + 1; ix2 <= ix + ctl->met_sx - 1;
12937 ix2++) {
12938 int ix3 = ix2;
12939 if (ix3 < 0)
12940 ix3 += met->nx;
12941 else if (ix3 >= met->nx)
12942 ix3 -= met->nx;
12943
12944 for (int iy2 = MAX(iy - ctl->met_sy + 1, 0);
12945 iy2 <= MIN(iy + ctl->met_sy - 1, met->ny - 1); iy2++)
12946 for (int ip2 = MAX(ip - ctl->met_sp + 1, 0);
12947 ip2 <= MIN(ip + ctl->met_sp - 1, met->np - 1); ip2++) {
12948 const float w =
12949 (1.0f - (float) abs(ix - ix2) / (float) ctl->met_sx)
12950 * (1.0f - (float) abs(iy - iy2) / (float) ctl->met_sy)
12951 * (1.0f - (float) abs(ip - ip2) / (float) ctl->met_sp);
12952 help->ps[ix][iy] += w * met->ps[ix3][iy2];
12953 help->zs[ix][iy] += w * met->zs[ix3][iy2];
12954 help->ts[ix][iy] += w * met->ts[ix3][iy2];
12955 help->us[ix][iy] += w * met->us[ix3][iy2];
12956 help->vs[ix][iy] += w * met->vs[ix3][iy2];
12957 help->ess[ix][iy] += w * met->ess[ix3][iy2];
12958 help->nss[ix][iy] += w * met->nss[ix3][iy2];
12959 help->shf[ix][iy] += w * met->shf[ix3][iy2];
12960 help->lsm[ix][iy] += w * met->lsm[ix3][iy2];
12961 help->sst[ix][iy] += w * met->sst[ix3][iy2];
12962 help->pbl[ix][iy] += w * met->pbl[ix3][iy2];
12963 help->cape[ix][iy] += w * met->cape[ix3][iy2];
12964 help->cin[ix][iy] += w * met->cin[ix3][iy2];
12965 help->t[ix][iy][ip] += w * met->t[ix3][iy2][ip2];
12966 help->u[ix][iy][ip] += w * met->u[ix3][iy2][ip2];
12967 help->v[ix][iy][ip] += w * met->v[ix3][iy2][ip2];
12968 help->w[ix][iy][ip] += w * met->w[ix3][iy2][ip2];
12969 help->h2o[ix][iy][ip] += w * met->h2o[ix3][iy2][ip2];
12970 help->o3[ix][iy][ip] += w * met->o3[ix3][iy2][ip2];
12971 help->lwc[ix][iy][ip] += w * met->lwc[ix3][iy2][ip2];
12972 help->rwc[ix][iy][ip] += w * met->rwc[ix3][iy2][ip2];
12973 help->iwc[ix][iy][ip] += w * met->iwc[ix3][iy2][ip2];
12974 help->swc[ix][iy][ip] += w * met->swc[ix3][iy2][ip2];
12975 help->cc[ix][iy][ip] += w * met->cc[ix3][iy2][ip2];
12976 wsum += w;
12977 }
12978 }
12979 help->ps[ix][iy] /= wsum;
12980 help->zs[ix][iy] /= wsum;
12981 help->ts[ix][iy] /= wsum;
12982 help->us[ix][iy] /= wsum;
12983 help->vs[ix][iy] /= wsum;
12984 help->ess[ix][iy] /= wsum;
12985 help->nss[ix][iy] /= wsum;
12986 help->shf[ix][iy] /= wsum;
12987 help->lsm[ix][iy] /= wsum;
12988 help->sst[ix][iy] /= wsum;
12989 help->pbl[ix][iy] /= wsum;
12990 help->cape[ix][iy] /= wsum;
12991 help->cin[ix][iy] /= wsum;
12992 help->t[ix][iy][ip] /= wsum;
12993 help->u[ix][iy][ip] /= wsum;
12994 help->v[ix][iy][ip] /= wsum;
12995 help->w[ix][iy][ip] /= wsum;
12996 help->h2o[ix][iy][ip] /= wsum;
12997 help->o3[ix][iy][ip] /= wsum;
12998 help->lwc[ix][iy][ip] /= wsum;
12999 help->rwc[ix][iy][ip] /= wsum;
13000 help->iwc[ix][iy][ip] /= wsum;
13001 help->swc[ix][iy][ip] /= wsum;
13002 help->cc[ix][iy][ip] /= wsum;
13003 }
13004 }
13005 }
13006
13007 /* Downsampling... */
13008 met->nx = 0;
13009 for (int ix = 0; ix < help->nx; ix += ctl->met_dx) {
13010 met->lon[met->nx] = help->lon[ix];
13011 met->ny = 0;
13012 for (int iy = 0; iy < help->ny; iy += ctl->met_dy) {
13013 met->lat[met->ny] = help->lat[iy];
13014 met->ps[met->nx][met->ny] = help->ps[ix][iy];
13015 met->zs[met->nx][met->ny] = help->zs[ix][iy];
13016 met->ts[met->nx][met->ny] = help->ts[ix][iy];
13017 met->us[met->nx][met->ny] = help->us[ix][iy];
13018 met->vs[met->nx][met->ny] = help->vs[ix][iy];
13019 met->ess[met->nx][met->ny] = help->ess[ix][iy];
13020 met->nss[met->nx][met->ny] = help->nss[ix][iy];
13021 met->shf[met->nx][met->ny] = help->shf[ix][iy];
13022 met->lsm[met->nx][met->ny] = help->lsm[ix][iy];
13023 met->sst[met->nx][met->ny] = help->sst[ix][iy];
13024 met->pbl[met->nx][met->ny] = help->pbl[ix][iy];
13025 met->cape[met->nx][met->ny] = help->cape[ix][iy];
13026 met->cin[met->nx][met->ny] = help->cin[ix][iy];
13027 met->np = 0;
13028 for (int ip = 0; ip < help->np; ip += ctl->met_dp) {
13029 met->p[met->np] = help->p[ip];
13030 met->t[met->nx][met->ny][met->np] = help->t[ix][iy][ip];
13031 met->u[met->nx][met->ny][met->np] = help->u[ix][iy][ip];
13032 met->v[met->nx][met->ny][met->np] = help->v[ix][iy][ip];
13033 met->w[met->nx][met->ny][met->np] = help->w[ix][iy][ip];
13034 met->h2o[met->nx][met->ny][met->np] = help->h2o[ix][iy][ip];
13035 met->o3[met->nx][met->ny][met->np] = help->o3[ix][iy][ip];
13036 met->lwc[met->nx][met->ny][met->np] = help->lwc[ix][iy][ip];
13037 met->rwc[met->nx][met->ny][met->np] = help->rwc[ix][iy][ip];
13038 met->iwc[met->nx][met->ny][met->np] = help->iwc[ix][iy][ip];
13039 met->swc[met->nx][met->ny][met->np] = help->swc[ix][iy][ip];
13040 met->cc[met->nx][met->ny][met->np] = help->cc[ix][iy][ip];
13041 met->np++;
13042 }
13043 met->ny++;
13044 }
13045 met->nx++;
13046 }
13047
13048 /* Free... */
13049 free(help);
13050}

◆ read_met_tropo()

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 the results in the meteorological data structure.

This function calculates the tropopause and related meteorological variables using different methods specified by the control parameters. The calculated tropopause pressure is stored in the provided meteorological data structure.

Parameters
ctlA pointer to a structure containing control parameters.
climA pointer to the climatological data structure.
metA pointer to the meteorological data structure to store the calculated tropopause pressure and related variables.

The function performs the following steps:

  • Sets a timer for performance monitoring.
  • Retrieves altitude and pressure profiles from the meteorological data structure.
  • Depending on the control parameters (ctl->met_tropo), it calculates the tropopause using one of the following methods:
    • If ctl->met_tropo == 0, it does not calculate the tropopause and assigns NaN values to the tropopause pressure.
    • If ctl->met_tropo == 1, it uses tropopause climatology to estimate the tropopause pressure based on latitude and time.
    • If ctl->met_tropo == 2, it calculates the tropopause based on the cold point method, finding the altitude where the temperature is at a minimum.
    • If ctl->met_tropo == 3 or ctl->met_tropo == 4, it calculates the tropopause using the WMO definition, which involves identifying a sharp temperature lapse rate between two pressure levels.
    • If ctl->met_tropo == 5, it calculates the dynamical tropopause based on potential vorticity and potential temperature profiles.
  • Interpolates temperature, geopotential height, and water vapor content to the tropopause pressure level using spatial interpolation.
  • Stores the interpolated values in the meteorological data structure.
Note
The function supports parallelization using OpenMP directives to improve performance.
Author
Lars Hoffmann

Definition at line 13054 of file mptrac.c.

13057 {
13058
13059 double p2[200], pv[EP], pv2[200], t[EP], t2[200], th[EP],
13060 th2[200], z[EP], z2[200];
13061
13062 /* Set timer... */
13063 SELECT_TIMER("READ_MET_TROPO", "METPROC");
13064 LOG(2, "Calculate tropopause...");
13065
13066 /* Get altitude and pressure profiles... */
13067#pragma omp parallel for default(shared)
13068 for (int iz = 0; iz < met->np; iz++)
13069 z[iz] = Z(met->p[iz]);
13070#pragma omp parallel for default(shared)
13071 for (int iz = 0; iz <= 190; iz++) {
13072 z2[iz] = 4.5 + 0.1 * iz;
13073 p2[iz] = P(z2[iz]);
13074 }
13075
13076 /* Do not calculate tropopause... */
13077 if (ctl->met_tropo == 0)
13078#pragma omp parallel for default(shared) collapse(2)
13079 for (int ix = 0; ix < met->nx; ix++)
13080 for (int iy = 0; iy < met->ny; iy++)
13081 met->pt[ix][iy] = NAN;
13082
13083 /* Use tropopause climatology... */
13084 else if (ctl->met_tropo == 1) {
13085 if (met->coord_type != 0)
13086 ERRMSG("Only lat/lon grid supported");
13087#pragma omp parallel for default(shared) collapse(2)
13088 for (int ix = 0; ix < met->nx; ix++)
13089 for (int iy = 0; iy < met->ny; iy++)
13090 met->pt[ix][iy] = (float) clim_tropo(clim, met->time, met->lat[iy]);
13091 }
13092
13093 /* Use cold point... */
13094 else if (ctl->met_tropo == 2) {
13095
13096 /* Loop over grid points... */
13097#pragma omp parallel for default(shared) private(t,t2) collapse(2)
13098 for (int ix = 0; ix < met->nx; ix++)
13099 for (int iy = 0; iy < met->ny; iy++) {
13100
13101 /* Interpolate temperature profile... */
13102 for (int iz = 0; iz < met->np; iz++)
13103 t[iz] = met->t[ix][iy][iz];
13104 spline(z, t, met->np, z2, t2, 171, ctl->met_tropo_spline);
13105
13106 /* Find minimum... */
13107 int iz = (int) gsl_stats_min_index(t2, 1, 171);
13108 if (iz > 0 && iz < 170)
13109 met->pt[ix][iy] = (float) p2[iz];
13110 else
13111 met->pt[ix][iy] = NAN;
13112 }
13113 }
13114
13115 /* Use WMO definition... */
13116 else if (ctl->met_tropo == 3 || ctl->met_tropo == 4) {
13117
13118 /* Loop over grid points... */
13119#pragma omp parallel for default(shared) private(t,t2) collapse(2)
13120 for (int ix = 0; ix < met->nx; ix++)
13121 for (int iy = 0; iy < met->ny; iy++) {
13122
13123 /* Interpolate temperature profile... */
13124 int iz;
13125 for (iz = 0; iz < met->np; iz++)
13126 t[iz] = met->t[ix][iy][iz];
13127 spline(z, t, met->np, z2, t2, 191, ctl->met_tropo_spline);
13128
13129 /* Find 1st tropopause... */
13130 met->pt[ix][iy] = NAN;
13131 for (iz = 0; iz <= 170; iz++) {
13132 int found = 1;
13133 for (int iz2 = iz + 1; iz2 <= iz + 20; iz2++)
13134 if (LAPSE(p2[iz], t2[iz], p2[iz2], t2[iz2]) > 2.0) {
13135 found = 0;
13136 break;
13137 }
13138 if (found) {
13139 if (iz > 0 && iz < 170)
13140 met->pt[ix][iy] = (float) p2[iz];
13141 break;
13142 }
13143 }
13144
13145 /* Find 2nd tropopause... */
13146 if (ctl->met_tropo == 4 && isfinite(met->pt[ix][iy])) {
13147 met->pt[ix][iy] = NAN;
13148 for (; iz <= 170; iz++) {
13149 int found = 1;
13150 for (int iz2 = iz + 1; iz2 <= iz + 10; iz2++)
13151 if (LAPSE(p2[iz], t2[iz], p2[iz2], t2[iz2]) < 3.0) {
13152 found = 0;
13153 break;
13154 }
13155 if (found)
13156 break;
13157 }
13158 for (; iz <= 170; iz++) {
13159 int found = 1;
13160 for (int iz2 = iz + 1; iz2 <= iz + 20; iz2++)
13161 if (LAPSE(p2[iz], t2[iz], p2[iz2], t2[iz2]) > 2.0) {
13162 found = 0;
13163 break;
13164 }
13165 if (found) {
13166 if (iz > 0 && iz < 170)
13167 met->pt[ix][iy] = (float) p2[iz];
13168 break;
13169 }
13170 }
13171 }
13172 }
13173 }
13174
13175 /* Use dynamical tropopause... */
13176 else if (ctl->met_tropo == 5) {
13177
13178 /* Loop over grid points... */
13179#pragma omp parallel for default(shared) private(pv,pv2,th,th2) collapse(2)
13180 for (int ix = 0; ix < met->nx; ix++)
13181 for (int iy = 0; iy < met->ny; iy++) {
13182
13183 /* Interpolate potential vorticity profile... */
13184 for (int iz = 0; iz < met->np; iz++)
13185 pv[iz] = met->pv[ix][iy][iz];
13186 spline(z, pv, met->np, z2, pv2, 171, ctl->met_tropo_spline);
13187
13188 /* Interpolate potential temperature profile... */
13189 for (int iz = 0; iz < met->np; iz++)
13190 th[iz] = THETA(met->p[iz], met->t[ix][iy][iz]);
13191 spline(z, th, met->np, z2, th2, 171, ctl->met_tropo_spline);
13192
13193 /* Find dynamical tropopause... */
13194 met->pt[ix][iy] = NAN;
13195 for (int iz = 0; iz <= 170; iz++)
13196 if (fabs(pv2[iz]) >= ctl->met_tropo_pv
13197 || th2[iz] >= ctl->met_tropo_theta) {
13198 if (iz > 0 && iz < 170)
13199 met->pt[ix][iy] = (float) p2[iz];
13200 break;
13201 }
13202 }
13203 }
13204
13205 else
13206 ERRMSG("Cannot calculate tropopause!");
13207
13208 /* Interpolate temperature, geopotential height, and water vapor... */
13209#pragma omp parallel for default(shared) collapse(2)
13210 for (int ix = 0; ix < met->nx; ix++)
13211 for (int iy = 0; iy < met->ny; iy++) {
13212 double h2ot, tt, zt;
13214 intpol_met_space_3d(met, met->t, met->pt[ix][iy], met->lon[ix],
13215 met->lat[iy], &tt, ci, cw, 1);
13216 intpol_met_space_3d(met, met->z, met->pt[ix][iy], met->lon[ix],
13217 met->lat[iy], &zt, ci, cw, 0);
13218 intpol_met_space_3d(met, met->h2o, met->pt[ix][iy], met->lon[ix],
13219 met->lat[iy], &h2ot, ci, cw, 0);
13220 met->tt[ix][iy] = (float) tt;
13221 met->zt[ix][iy] = (float) zt;
13222 met->h2ot[ix][iy] = (float) h2ot;
13223 }
13224}
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:13437
#define LAPSE(p1, t1, p2, t2)
Calculate lapse rate.
Definition mptrac.h:1341
Here is the call graph for this function:

◆ read_obs()

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.

This function reads observation data from a specified file in either ASCII or NetCDF format, depending on the value of the OBS_TYPE control parameter. It stores the time, altitude, longitude, latitude, and observation values in the provided arrays.

Parameters
filenameThe path to the observation data file.
ctlA pointer to a structure containing control parameters.
rtAn array to store the time values of the observations.
rzAn array to store the altitude values of the observations.
rlonAn array to store the longitude values of the observations.
rlatAn array to store the latitude values of the observations.
robsAn array to store the observation values.
nobsA pointer to an integer variable to store the number of observations read.

The function performs the following steps:

  • Logs an informational message indicating the observation data file being read.
  • Reads the observation data from the file based on the OBS_TYPE control parameter:
    • If ctl->obs_type == 0, it reads the data from an ASCII file using the read_obs_asc function.
    • If ctl->obs_type == 1, it reads the data from a NetCDF file using the read_obs_nc function.
    • If ctl->obs_type is neither 0 nor 1, it generates an error message indicating that the OBS_TYPE must be set to 0 or 1.
  • Checks if the time values are in ascending order and generates an error message if not.
  • Logs statistical information about the observation data, including the number of observations, time range, altitude range, longitude range, latitude range, and observation value range.
Note
The function assumes that the observation data file is formatted correctly and that the arrays provided have sufficient memory allocated to store the data.
Author
Lars Hoffmann
Mingzhao Liu

Definition at line 13228 of file mptrac.c.

13236 {
13237
13238 /* Write info... */
13239 LOG(1, "Read observation data: %s", filename);
13240
13241 /* Read data... */
13242 if (ctl->obs_type == 0)
13243 read_obs_asc(filename, rt, rz, rlon, rlat, robs, nobs);
13244 else if (ctl->obs_type == 1)
13245 read_obs_nc(filename, rt, rz, rlon, rlat, robs, nobs);
13246 else
13247 ERRMSG("Set OBS_TYPE to 0 or 1!");
13248
13249 /* Check time... */
13250 for (int i = 1; i < *nobs; i++)
13251 if (rt[i] < rt[i - 1])
13252 ERRMSG("Time must be ascending!");
13253
13254 /* Write info... */
13255 int n = *nobs;
13256 double mini, maxi;
13257 LOG(2, "Number of observations: %d", *nobs);
13258 gsl_stats_minmax(&mini, &maxi, rt, 1, (size_t) n);
13259 LOG(2, "Time range: %.2f ... %.2f s", mini, maxi);
13260 gsl_stats_minmax(&mini, &maxi, rz, 1, (size_t) n);
13261 LOG(2, "Altitude range: %g ... %g km", mini, maxi);
13262 gsl_stats_minmax(&mini, &maxi, rlon, 1, (size_t) n);
13263 LOG(2, "Longitude range: %g ... %g deg", mini, maxi);
13264 gsl_stats_minmax(&mini, &maxi, rlat, 1, (size_t) n);
13265 LOG(2, "Latitude range: %g ... %g deg", mini, maxi);
13266 gsl_stats_minmax(&mini, &maxi, robs, 1, (size_t) n);
13267 LOG(2, "Observation range: %g ... %g", mini, maxi);
13268}
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:13272
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:13300
Here is the call graph for this function:

◆ read_obs_asc()

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.

This function reads observation data from a specified ASCII file. It extracts time, altitude, longitude, latitude, and observation values from each line of the file and stores them in the provided arrays.

Parameters
filenameThe path to the ASCII file containing the observation data.
rtAn array to store the time values of the observations.
rzAn array to store the altitude values of the observations.
rlonAn array to store the longitude values of the observations.
rlatAn array to store the latitude values of the observations.
robsAn array to store the observation values.
nobsA pointer to an integer variable to store the number of observations read.

The function performs the following steps:

  • Attempts to open the specified observation data file in read mode.
  • Reads each line of the file and parses it to extract the time, altitude, longitude, latitude, and observation values using the sscanf function.
  • Stores the extracted values in the respective arrays.
  • Checks if the number of observations exceeds the maximum allowed limit (NOBS) and generates an error message if so.
  • Closes the observation data file after reading all data.
Note
The function assumes that the observation data file is properly formatted and that the arrays provided have sufficient memory allocated to store the data.
Author
Lars Hoffmann

Definition at line 13272 of file mptrac.c.

13279 {
13280
13281 /* Open observation data file... */
13282 FILE *in;
13283 if (!(in = fopen(filename, "r")))
13284 ERRMSG("Cannot open file!");
13285
13286 /* Read observations... */
13287 char line[LEN];
13288 while (fgets(line, LEN, in))
13289 if (sscanf(line, "%lg %lg %lg %lg %lg", &rt[*nobs], &rz[*nobs],
13290 &rlon[*nobs], &rlat[*nobs], &robs[*nobs]) == 5)
13291 if ((++(*nobs)) >= NOBS)
13292 ERRMSG("Too many observations!");
13293
13294 /* Close observation data file... */
13295 fclose(in);
13296}
#define NOBS
Maximum number of observation data points.
Definition mptrac.h:589

◆ read_obs_nc()

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.

This function reads observation data from a specified NetCDF file. It extracts time, altitude, longitude, latitude, and observation values from the variables in the NetCDF file and stores them in the provided arrays.

Parameters
filenameThe path to the NetCDF file containing the observation data.
rtAn array to store the time values of the observations.
rzAn array to store the altitude values of the observations.
rlonAn array to store the longitude values of the observations.
rlatAn array to store the latitude values of the observations.
robsAn array to store the observation values.
nobsA pointer to an integer variable to store the number of observations read.

The function performs the following steps:

  • Attempts to open the specified NetCDF file in read-only mode using the nc_open function.
  • Queries the dimensions of the 'nobs' variable in the NetCDF file to determine the number of observations using the NC_INQ_DIM macro.
  • Reads the 'time', 'alt', 'lon', 'lat', and 'obs' variables from the NetCDF file using the NC_GET_DOUBLE macro and stores them in the respective arrays.
  • Closes the NetCDF file after reading all data using the nc_close function.
Note
The function assumes that the NetCDF file contains the required variables ('time', 'alt', 'lon', 'lat', 'obs') and that the arrays provided have sufficient memory allocated to store the data.
Author
Lars Hoffmann

Definition at line 13300 of file mptrac.c.

13307 {
13308
13309 int ncid, varid;
13310
13311 /* Open netCDF file... */
13312 if (nc_open(filename, NC_NOWRITE, &ncid) != NC_NOERR)
13313 ERRMSG("Cannot open file!");
13314
13315 /* Read the observations from the NetCDF file... */
13316 NC_INQ_DIM("nobs", nobs, 1, NOBS, 1);
13317 NC_GET_DOUBLE("time", rt, 1);
13318 NC_GET_DOUBLE("alt", rz, 1);
13319 NC_GET_DOUBLE("lon", rlon, 1);
13320 NC_GET_DOUBLE("lat", rlat, 1);
13321 NC_GET_DOUBLE("obs", robs, 1);
13322
13323 /* Close file... */
13324 NC(nc_close(ncid));
13325}

◆ scan_ctl()

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.

This function scans either a control file or command-line arguments for a specified variable name and retrieves its value. It searches for the variable name in the control file or command-line arguments and returns its corresponding value. If the variable is not found, it returns a default value specified by the user.

Parameters
filenameThe name of the control file to be scanned. If NULL, only command-line arguments will be scanned.
argcThe number of command-line arguments.
argvAn array of command-line arguments.
varnameThe name of the variable to be searched.
arridxThe index of the variable array, if applicable. Set to -1 if not an array.
defvalueThe default value to be returned if the variable is not found.
valueA pointer to a character array to store the retrieved value.
Returns
The retrieved value of the variable as a double.

The function performs the following steps:

  • Attempts to open the specified control file in read mode using the fopen function. If the filename ends with a '-', the file is not opened.
  • Constructs the full variable name based on the variable name and array index provided.
  • Reads data from the control file, searching for the full variable name. If found, it sets the contain flag to 1 and breaks the loop.
  • Searches through the command-line arguments for the full variable name. If found, it sets the value and contain flag and breaks the loop.
  • Closes the control file if opened.
  • If the variable is not found, it sets the value to the default value provided or throws an error if no default value is provided.
  • Writes the variable name and its value to the log.
  • Copies the retrieved value to the value parameter if it is not NULL.
  • Returns the retrieved value as a double after converting it from a string using the atof function.
Note
This function assumes that the variable names and their values in the control file or command-line arguments are separated by whitespace.
Author
Lars Hoffmann

Definition at line 13329 of file mptrac.c.

13336 {
13337
13338 FILE *in = NULL;
13339
13340 char fullname1[LEN], fullname2[LEN], rval[LEN];
13341
13342 int contain = 0, i;
13343
13344 /* Open file... */
13345 if (filename[strlen(filename) - 1] != '-')
13346 if (!(in = fopen(filename, "r")))
13347 ERRMSG("Cannot open file!");
13348
13349 /* Set full variable name... */
13350 if (arridx >= 0) {
13351 sprintf(fullname1, "%s[%d]", varname, arridx);
13352 sprintf(fullname2, "%s[*]", varname);
13353 } else {
13354 sprintf(fullname1, "%s", varname);
13355 sprintf(fullname2, "%s", varname);
13356 }
13357
13358 /* Read data... */
13359 if (in != NULL) {
13360 char dummy[LEN], line[LEN], rvarname[LEN];
13361 while (fgets(line, LEN, in)) {
13362 if (sscanf(line, "%4999s %4999s %4999s", rvarname, dummy, rval) == 3)
13363 if (strcasecmp(rvarname, fullname1) == 0 ||
13364 strcasecmp(rvarname, fullname2) == 0) {
13365 contain = 1;
13366 break;
13367 }
13368 }
13369 }
13370 for (i = 1; i < argc - 1; i++)
13371 if (strcasecmp(argv[i], fullname1) == 0 ||
13372 strcasecmp(argv[i], fullname2) == 0) {
13373 sprintf(rval, "%s", argv[i + 1]);
13374 contain = 1;
13375 break;
13376 }
13377
13378 /* Close file... */
13379 if (in != NULL)
13380 fclose(in);
13381
13382 /* Check for missing variables... */
13383 if (!contain) {
13384 if (strlen(defvalue) > 0)
13385 sprintf(rval, "%s", defvalue);
13386 else
13387 ERRMSG("Missing variable %s!\n", fullname1);
13388 }
13389
13390 /* Write info... */
13391 LOG(1, "%s = %s", fullname1, rval);
13392
13393 /* Return values... */
13394 if (value != NULL)
13395 sprintf(value, "%s", rval);
13396 return atof(rval);
13397}

◆ sedi()

double sedi ( const double p,
const double T,
const double rp,
const double rhop )

Calculates the sedimentation velocity of a particle in air.

This function calculates the sedimentation velocity of a particle in air using the given parameters.

Parameters
pThe atmospheric pressure [hPa].
TThe temperature [K].
rpThe radius of the particle [microns].
rhopThe density of the particle [kg/m^3].
Returns
The sedimentation velocity of the particle [m/s].

The function performs the following steps:

  • Converts the radius of the particle from microns to meters.
  • Calculates the density of dry air using the given atmospheric pressure and temperature.
  • Calculates the dynamic viscosity of air using Sutherland's formula.
  • Calculates the thermal velocity of an air molecule using the given temperature.
  • Calculates the mean free path of an air molecule.
  • Computes the Knudsen number for air based on the ratio of mean free path to particle radius.
  • Applies the Cunningham slip-flow correction factor to account for particle size.
  • Computes the sedimentation velocity of the particle based on the difference in densities between the particle and air, incorporating the slip-flow correction.
Note
This function assumes that the ideal gas law and Stokes' law are applicable for calculating the sedimentation velocity of the particle.
Nonpositive or nonfinite particle radius or density gives zero velocity.
Author
Lars Hoffmann

Definition at line 13401 of file mptrac.c.

13405 {
13406
13407 if (!isfinite(rp) || rp <= 0. || !isfinite(rhop) || rhop <= 0.)
13408 return 0.;
13409
13410 /* Convert particle radius from microns to m... */
13411 const double rp_help = rp * 1e-6;
13412
13413 /* Density of dry air [kg / m^3]... */
13414 const double rho = RHO(p, T);
13415
13416 /* Dynamic viscosity of air [kg / (m s)]... */
13417 const double eta = 1.8325e-5 * (416.16 / (T + 120.)) * pow(T / 296.16, 1.5);
13418
13419 /* Thermal velocity of an air molecule [m / s]... */
13420 const double v = sqrt(8. * KB * T / (M_PI * M_AIR_MOLECULE));
13421
13422 /* Mean free path of an air molecule [m]... */
13423 const double lambda = 2. * eta / (rho * v);
13424
13425 /* Knudsen number for air (dimensionless)... */
13426 const double K = lambda / rp_help;
13427
13428 /* Cunningham slip-flow correction (dimensionless)... */
13429 const double G = 1. + K * (1.249 + 0.42 * exp(-0.87 / K));
13430
13431 /* Sedimentation velocity [m / s]... */
13432 return 2. * SQR(rp_help) * (rhop - rho) * G0 / (9. * eta) * G;
13433}
#define KB
Boltzmann constant [kg m^2/(K s^2)].
Definition mptrac.h:285
#define M_AIR_MOLECULE
Mean mass of an air molecule [kg].
Definition mptrac.h:340

◆ spline()

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.

This function interpolates a set of data points using either cubic spline interpolation or linear interpolation, depending on the specified method.

Parameters
xThe array of x-coordinates of the data points.
yThe array of y-coordinates of the data points.
nThe number of data points.
x2The array of x-coordinates where interpolation is required.
y2The array to store the interpolated y-values.
n2The number of points to interpolate.
methodThe interpolation method: 1 for cubic spline, 0 for linear interpolation.

If the method is set to 1 (cubic spline interpolation):

  • The function initializes a cubic spline interpolator using GSL.
  • It interpolates the y-values at the specified x-coordinates using the spline.
  • The interpolated y-values are stored in the provided y2 array.

If the method is set to 0 (linear interpolation):

  • The function performs linear interpolation between adjacent data points.
  • It locates the interval where each interpolation point falls and calculates the interpolated y-value using linear interpolation.
  • The interpolated y-values are stored in the provided y2 array.
Note
The x-coordinates in both arrays (x and x2) must be sorted in ascending order.
Author
Lars Hoffmann

Definition at line 13437 of file mptrac.c.

13444 {
13445
13446 /* Cubic spline interpolation... */
13447 if (method == 1) {
13448
13449 /* Allocate... */
13450 gsl_interp_accel *acc = gsl_interp_accel_alloc();
13451 gsl_spline *s = gsl_spline_alloc(gsl_interp_cspline, (size_t) n);
13452
13453 /* Interpolate profile... */
13454 gsl_spline_init(s, x, y, (size_t) n);
13455 for (int i = 0; i < n2; i++)
13456 if (x2[i] <= x[0])
13457 y2[i] = y[0];
13458 else if (x2[i] >= x[n - 1])
13459 y2[i] = y[n - 1];
13460 else
13461 y2[i] = gsl_spline_eval(s, x2[i], acc);
13462
13463 /* Free... */
13464 gsl_spline_free(s);
13465 gsl_interp_accel_free(acc);
13466 }
13467
13468 /* Linear interpolation... */
13469 else {
13470 for (int i = 0; i < n2; i++)
13471 if (x2[i] <= x[0])
13472 y2[i] = y[0];
13473 else if (x2[i] >= x[n - 1])
13474 y2[i] = y[n - 1];
13475 else {
13476 const int idx = locate_irr(x, n, x2[i]);
13477 y2[i] = LIN(x[idx], y[idx], x[idx + 1], y[idx + 1], x2[i]);
13478 }
13479 }
13480}
Here is the call graph for this function:

◆ stddev()

float stddev ( const float * data,
const int n )

Calculates the standard deviation of a set of data.

This function calculates the standard deviation of a set of floating-point data values.

Parameters
dataPointer to the array of data values.
nNumber of data values in the array.
Returns
The standard deviation of the data values. If the number of data values is less than or equal to 0, returns 0.

The standard deviation is calculated using the formula:

\[ \sigma = \sqrt{\frac{\sum_{i=1}^{n} (x_i - \bar{x})^2}{n}} \]

where:

  • \( \sigma \) is the standard deviation,
  • \( x_i \) is each data value,
  • \( \bar{x} \) is the mean of the data values, and
  • \( n \) is the total number of data values.
Author
Lars Hoffmann

Definition at line 13484 of file mptrac.c.

13486 {
13487
13488 if (n <= 0)
13489 return 0;
13490
13491 float mean = 0, var = 0;
13492
13493 for (int i = 0; i < n; ++i) {
13494 mean += data[i];
13495 var += SQR(data[i]);
13496 }
13497
13498 var = var / (float) n - SQR(mean / (float) n);
13499
13500 return (var > 0 ? sqrtf(var) : 0);
13501}

◆ time2jsec()

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.

This function calculates the number of seconds elapsed since January 1, 2000, 12:00:00 UTC, based on the provided year, month, day, hour, minute, and second. It also includes a fractional part to represent the remaining seconds.

Parameters
yearThe year.
monThe month (1-12).
dayThe day of the month (1-31).
hourThe hour of the day (0-23).
minThe minute (0-59).
secThe second (0-59).
remainThe fractional part of seconds.
jsecPointer to store the calculated number of seconds since January 1, 2000, 12:00:00 UTC.

The function calculates the time elapsed since January 1, 2000, 12:00:00 UTC, up to the specified time and includes any fractional seconds indicated by the "remain" parameter.

Note
The function uses the timegm function, which is similar to mktime but operates in UTC.
Author
Lars Hoffmann

Definition at line 13505 of file mptrac.c.

13513 {
13514
13515 struct tm t0, t1;
13516
13517 t0.tm_year = 100;
13518 t0.tm_mon = 0;
13519 t0.tm_mday = 1;
13520 t0.tm_hour = 0;
13521 t0.tm_min = 0;
13522 t0.tm_sec = 0;
13523
13524 t1.tm_year = year - 1900;
13525 t1.tm_mon = mon - 1;
13526 t1.tm_mday = day;
13527 t1.tm_hour = hour;
13528 t1.tm_min = min;
13529 t1.tm_sec = sec;
13530
13531 *jsec = (double) timegm(&t1) - (double) timegm(&t0) + remain;
13532}

◆ timer()

void timer ( const char * name,
const char * group,
const int output )

Measures and reports elapsed time for named and grouped timers.

The timer function measures elapsed time for a specified named timer and an optional group of timers, accumulating time statistics such as minimum, maximum, and mean elapsed times. It also provides an option to log the timing statistics to an output.

Parameters
nameA string representing the name of the timer.
groupA string representing the group to which the timer belongs.
outputAn integer flag indicating whether to report the timing statistics (non-zero to report).

The function keeps track of multiple timers and groups. When called, it:

  • Gets the current time and calculates the elapsed time since the last call.
  • Adds the elapsed time to the current timers' statistics.
  • Reports the statistics if the output parameter is non-zero.
  • Identifies the IDs of the next timer and group based on the provided name and group.
  • Checks if the name and group are new, and if so, initializes them.
  • Saves the starting time for the next measurement.
Note
The function uses OpenMP's omp_get_wtime() to get the current wall time.
The function maintains static arrays and variables to store timer names, groups, and statistics.
The maximum number of timers and groups is defined by the NTIMER macro.
Warning
If the number of timers or groups exceeds NTIMER, the function will trigger an error message.
Author
Lars Hoffmann

Definition at line 13536 of file mptrac.c.

13539 {
13540
13541 static char names[NTIMER][100], groups[NTIMER][100];
13542
13543 static double rt_name[NTIMER], rt_group[NTIMER],
13544 rt_min[NTIMER], rt_max[NTIMER], dt, t0, t1;
13545
13546 static int iname = -1, igroup = -1, nname, ngroup, ct_name[NTIMER];
13547
13548 /* Get time... */
13549 t1 = omp_get_wtime();
13550 dt = t1 - t0;
13551
13552 /* Add elapsed time to current timers... */
13553 if (iname >= 0) {
13554 rt_name[iname] += dt;
13555 rt_min[iname] = (ct_name[iname] <= 0 ? dt : MIN(rt_min[iname], dt));
13556 rt_max[iname] = (ct_name[iname] <= 0 ? dt : MAX(rt_max[iname], dt));
13557 ct_name[iname]++;
13558 }
13559 if (igroup >= 0)
13560 rt_group[igroup] += t1 - t0;
13561
13562 /* Report timers... */
13563 if (output) {
13564 for (int i = 0; i < nname; i++)
13565 LOG(1, "TIMER_%s = %.3f s (min= %g s, mean= %g s,"
13566 " max= %g s, n= %d)", names[i], rt_name[i], rt_min[i],
13567 rt_name[i] / ct_name[i], rt_max[i], ct_name[i]);
13568 for (int i = 0; i < ngroup; i++)
13569 LOG(1, "TIMER_GROUP_%s = %.3f s", groups[i], rt_group[i]);
13570 double total = 0.0;
13571 for (int i = 0; i < nname; i++)
13572 total += rt_name[i];
13573 LOG(1, "TIMER_TOTAL = %.3f s", total);
13574 }
13575
13576 /* Identify IDs of next timer... */
13577 for (iname = 0; iname < nname; iname++)
13578 if (strcasecmp(name, names[iname]) == 0)
13579 break;
13580 for (igroup = 0; igroup < ngroup; igroup++)
13581 if (strcasecmp(group, groups[igroup]) == 0)
13582 break;
13583
13584 /* Check whether this is a new timer... */
13585 if (iname >= nname) {
13586 sprintf(names[iname], "%s", name);
13587 if ((++nname) >= NTIMER)
13588 ERRMSG("Too many timers!");
13589 }
13590
13591 /* Check whether this is a new group... */
13592 if (igroup >= ngroup) {
13593 sprintf(groups[igroup], "%s", group);
13594 if ((++ngroup) >= NTIMER)
13595 ERRMSG("Too many groups!");
13596 }
13597
13598 /* Save starting time... */
13599 t0 = t1;
13600}
#define NTIMER
Maximum number of timers.
Definition mptrac.h:2470

◆ time_from_filename()

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.

The time_from_filename function parses a given filename to extract a timestamp and converts it to Julian seconds. The timestamp is expected to follow a specific format and position within the filename, defined by the offset parameter.

Parameters
filenameA string representing the filename containing the timestamp.
offsetAn integer indicating the position from the end of the filename where the timestamp starts.
with_secondsAn integer indicating if the filename contains seconds or not.
Returns
The time in Julian seconds as a double.

The function performs the following steps:

  • Extracts the year, month, day, hour, minute and (optionally) second components of the timestamp from the filename using the given offset.
  • Validates the extracted components to ensure they represent a valid date and time.
  • Converts the validated date and time components to Julian seconds using the time2jsec function.
  • Returns the computed time in Julian seconds.
Note
The expected formats of the timestamp in the filename are:
  • if with_seconds=0: YYYY-MM-DD_HH-MM (e.g., "2023-05-27_14-45")
  • if with_seconds=1: YYYY-MM-DD_HH-MM-SS (e.g., "2023-05-27_14-45-12")
Warning
If the extracted components do not represent a valid date and time, the function will trigger an error message.
Author
Lars Hoffmann

Definition at line 13604 of file mptrac.c.

13607 {
13608
13609 char tstr[10];
13610
13611 double t;
13612
13613 /* Get time from filename... */
13614 int len = (int) strlen(filename);
13615 sprintf(tstr, "%.4s", &filename[len - offset]);
13616 int year = atoi(tstr);
13617 sprintf(tstr, "%.2s", &filename[len - offset + 5]);
13618 int mon = atoi(tstr);
13619 sprintf(tstr, "%.2s", &filename[len - offset + 8]);
13620 int day = atoi(tstr);
13621 sprintf(tstr, "%.2s", &filename[len - offset + 11]);
13622 int hour = atoi(tstr);
13623 sprintf(tstr, "%.2s", &filename[len - offset + 14]);
13624 int min = atoi(tstr);
13625
13626 int sec = 0;
13627 if (with_seconds) {
13628 sprintf(tstr, "%.2s", &filename[len - offset + 17]);
13629 sec = atoi(tstr);
13630 }
13631
13632 /* Check time... */
13633 if (year < 1900 || year > 2100 || mon < 1 || mon > 12 || day < 1
13634 || day > 31 || hour < 0 || hour > 23 || min < 0 || min > 59)
13635 ERRMSG("Cannot read time from filename!");
13636
13637 /* Convert time to Julian seconds... */
13638 time2jsec(year, mon, day, hour, min, sec, 0.0, &t);
13639
13640 /* Return time... */
13641 return t;
13642}
Here is the call graph for this function:

◆ tropo_weight()

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.

This function calculates a weighting factor for a given pressure value in relation to the tropopause pressure. The weighting factor is determined as follows:

  • Returns 1 if the pressure is greater than a calculated upper limit.
  • Returns 0 if the pressure is less than a calculated lower limit.
  • Linearly interpolates between 1 and 0 within the range defined by the upper and lower limits.
Parameters
[in]ctlPointer to the control data structure.
[in]climPointer to the climatology data structure.
[in]atmPointer to the atmospheric data structure.
[in]ipIndex of the pressure value to evaluate within the atmospheric data.
Returns
Weighting factor (double) in the range [0, 1].
Author
Lars Hoffmann

Definition at line 13646 of file mptrac.c.

13650 {
13651
13652 /* Get tropopause pressure... */
13653 const double pt = clim_tropo(clim, atm->time[ip],
13654 ctl->met_coord_type ==
13655 0 ? atm->lat[ip] : ctl->met_utm_ref_lat);
13656
13657 /* Get pressure range... */
13658 const double p1 = pt * 0.866877899;
13659 const double p0 = pt / 0.866877899;
13660
13661 /* Get weighting factor... */
13662 if (atm->p[ip] > p0)
13663 return 1;
13664 else if (atm->p[ip] < p1)
13665 return 0;
13666 else
13667 return LIN(p0, 1.0, p1, 0.0, atm->p[ip]);
13668}
Here is the call graph for this function:

◆ write_atm_asc()

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.

The write_atm_asc function writes the atmospheric data stored in the atm structure to an ASCII file specified by filename or to pipe to gnuplot if requested.

Parameters
filenameA string representing the name of the file to write the data to.
ctlA pointer to a ctl_t structure containing control parameters.
atmA pointer to an atm_t structure containing atmospheric data.
tThe current time used for filtering and timestamping.

The function performs the following steps:

  • Sets the time interval for the output data based on the control parameters.
  • Checks if gnuplot output is requested and, if so, creates a pipe to gnuplot and sets up the plot.
  • If gnuplot output is not requested, creates an ASCII file for writing.
  • Writes the header information to the output file, including the description of each column.
  • Iterates over the particles in the atm structure, filtering by time if specified, and writes the data to the output file.
  • Closes the output file or gnuplot pipe.
Author
Lars Hoffmann

Definition at line 13672 of file mptrac.c.

13676 {
13677
13678 FILE *out;
13679
13680 /* Set time interval for output... */
13681 const double t0 = t - 0.5 * ctl->dt_mod;
13682 const double t1 = t + 0.5 * ctl->dt_mod;
13683
13684 /* Check if gnuplot output is requested... */
13685 if (ctl->atm_gpfile[0] != '-') {
13686
13687 /* Create gnuplot pipe... */
13688 if (!(out = popen("gnuplot", "w")))
13689 ERRMSG("Cannot create pipe to gnuplot!");
13690
13691 /* Set plot filename... */
13692 fprintf(out, "set out \"%s.png\"\n", filename);
13693
13694 /* Set time string... */
13695 double r;
13696 int year, mon, day, hour, min, sec;
13697 jsec2time(t, &year, &mon, &day, &hour, &min, &sec, &r);
13698 fprintf(out, "timestr=\"%d-%02d-%02d, %02d:%02d UTC\"\n",
13699 year, mon, day, hour, min);
13700
13701 /* Dump gnuplot file to pipe... */
13702 FILE *in;
13703 if (!(in = fopen(ctl->atm_gpfile, "r")))
13704 ERRMSG("Cannot open file!");
13705 char line[LEN];
13706 while (fgets(line, LEN, in))
13707 fprintf(out, "%s", line);
13708 fclose(in);
13709 }
13710
13711 else {
13712
13713 /* Create file... */
13714 if (!(out = fopen(filename, "w")))
13715 ERRMSG("Cannot create file!");
13716 }
13717
13718 /* Write header... */
13719
13720 if (ctl->met_coord_type == 0) {
13721 fprintf(out,
13722 "# $1 = time [s]\n"
13723 "# $2 = altitude [km]\n"
13724 "# $3 = longitude [deg]\n" "# $4 = latitude [deg]\n");
13725 } else {
13726 fprintf(out,
13727 "# $1 = time [s]\n"
13728 "# $2 = altitude [km]\n" "# $3 = x [m]\n" "# $4 = y [m]\n");
13729 }
13730
13731 for (int iq = 0; iq < ctl->nq; iq++)
13732 fprintf(out, "# $%i = %s [%s]\n", iq + 5, ctl->qnt_name[iq],
13733 ctl->qnt_unit[iq]);
13734 fprintf(out, "\n");
13735
13736 /* Write data... */
13737 for (int ip = 0; ip < atm->np; ip += ctl->atm_stride) {
13738
13739 /* Check time... */
13740 if (ctl->atm_filter == 2 && (atm->time[ip] < t0 || atm->time[ip] > t1))
13741 continue;
13742
13743 /* Write output... */
13744 if (ctl->met_coord_type == 0) {
13745 fprintf(out, "%.2f %g %g %g",
13746 atm->time[ip], Z(atm->p[ip]), atm->lon[ip], atm->lat[ip]
13747 );
13748 } else {
13749 fprintf(out, "%.2f %g %.2f %.2f",
13750 atm->time[ip], Z(atm->p[ip]), atm->lon[ip], atm->lat[ip]
13751 );
13752 }
13753
13754 for (int iq = 0; iq < ctl->nq; iq++) {
13755 fprintf(out, " ");
13756 if (ctl->atm_filter == 1 && (atm->time[ip] < t0 || atm->time[ip] > t1))
13757 fprintf(out, ctl->qnt_format[iq], NAN);
13758 else
13759 fprintf(out, ctl->qnt_format[iq], atm->q[iq][ip]);
13760 }
13761 fprintf(out, "\n");
13762 }
13763
13764 /* Close file... */
13765 fclose(out);
13766}
Here is the call graph for this function:

◆ write_atm_bin()

void write_atm_bin ( const char * filename,
const ctl_t * ctl,
const atm_t * atm )

Writes air parcel data to a binary file.

The write_atm_bin function writes the air parcel data stored in the atm structure to a binary file specified by filename. The function includes versioning information and ensures that all relevant data arrays are written in a consistent binary format.

Parameters
filenameA string representing the name of the file to write the data to.
ctlA pointer to a ctl_t structure containing control parameters.
atmA pointer to an atm_t structure containing atmospheric data.

The function performs the following steps:

  • Creates the binary file for writing. If the file cannot be created, it triggers an error message.
  • Writes a version number for the binary data format.
  • Writes the number of particles to the file.
  • Writes the time, pressure, longitude, and latitude arrays to the file.
  • Iterates over the quantities specified in the ctl structure and writes each quantity array to the file.
  • Writes a final flag to indicate the end of the binary data.
  • Closes the file.
Author
Lars Hoffmann

Definition at line 13770 of file mptrac.c.

13773 {
13774
13775 FILE *out;
13776
13777 /* Create file... */
13778 if (!(out = fopen(filename, "w")))
13779 ERRMSG("Cannot create file!");
13780
13781 /* Write version of binary data... */
13782 int version = 100;
13783 FWRITE(&version, int,
13784 1,
13785 out);
13786
13787 /* Write data... */
13788 FWRITE(&atm->np, int,
13789 1,
13790 out);
13791 FWRITE(atm->time, double,
13792 (size_t) atm->np,
13793 out);
13794 FWRITE(atm->p, double,
13795 (size_t) atm->np,
13796 out);
13797 FWRITE(atm->lon, double,
13798 (size_t) atm->np,
13799 out);
13800 FWRITE(atm->lat, double,
13801 (size_t) atm->np,
13802 out);
13803 for (int iq = 0; iq < ctl->nq; iq++)
13804 FWRITE(atm->q[iq], double,
13805 (size_t) atm->np,
13806 out);
13807
13808 /* Write final flag... */
13809 int final = 999;
13810 FWRITE(&final, int,
13811 1,
13812 out);
13813
13814 /* Close file... */
13815 fclose(out);
13816}

◆ write_atm_clams()

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.

The write_atm_clams function creates a NetCDF file and writes air parcel data into it. The data includes time, latitude, longitude, pressure, and other specified quantities. The function defines the dimensions and variables, sets global attributes, and writes the data to the file.

Parameters
filenameA string representing the name of the output file.
ctlA pointer to a ctl_t structure containing control parameters.
atmA pointer to an atm_t structure containing atmospheric data.

The function performs the following steps:

  • Creates the NetCDF file with the specified filename.
  • Defines the dimensions for time and the number of particles (NPARTS).
  • Defines variables for time, latitude, longitude, pressure, zeta, and other quantities.
  • Sets global attributes for the vertical coordinate name and model.
  • Writes the data into the NetCDF file.
  • Closes the NetCDF file after writing.
Author
Jan Clemens

Definition at line 13820 of file mptrac.c.

13823 {
13824
13825 if (ctl->met_coord_type != 0)
13826 ERRMSG("CLaMS atmospheric files support only lat/lon grids");
13827
13828 int tid, pid, ncid, varid;
13829 size_t start[2], count[2];
13830
13831 /* Create file... */
13832 NC(nc_create(filename, NC_NETCDF4, &ncid));
13833
13834 /* Define dimensions... */
13835 NC(nc_def_dim(ncid, "time", 1, &tid));
13836 NC(nc_def_dim(ncid, "NPARTS", (size_t) atm->np, &pid));
13837
13838 /* Define variables and their attributes... */
13839 int dim_ids[2] = { tid, pid };
13840 NC_DEF_VAR("time", NC_DOUBLE, 1, &tid, "Time",
13841 "seconds since 2000-01-01 00:00:00 UTC", ctl->atm_nc_level, 0);
13842 NC_DEF_VAR("LAT", NC_DOUBLE, 1, &pid, "Latitude", "deg",
13843 ctl->atm_nc_level, 0);
13844 NC_DEF_VAR("LON", NC_DOUBLE, 1, &pid, "Longitude", "deg",
13845 ctl->atm_nc_level, 0);
13846 NC_DEF_VAR("PRESS", NC_DOUBLE, 1, &pid, "Pressure", "hPa",
13847 ctl->atm_nc_level, 0);
13848 NC_DEF_VAR("ZETA", NC_DOUBLE, 1, &pid, "Zeta", "K", ctl->atm_nc_level, 0);
13849 for (int iq = 0; iq < ctl->nq; iq++)
13850 NC_DEF_VAR(ctl->qnt_name[iq], NC_DOUBLE, 2, dim_ids,
13851 ctl->qnt_name[iq], ctl->qnt_unit[iq],
13852 ctl->atm_nc_level, ctl->atm_nc_quant[iq]);
13853
13854 /* Define global attributes... */
13855 NC_PUT_ATT_GLOBAL("exp_VERTCOOR_name", "zeta");
13856 NC_PUT_ATT_GLOBAL("model", "MPTRAC");
13857
13858 /* End definitions... */
13859 NC(nc_enddef(ncid));
13860
13861 /* Write data... */
13862 NC_PUT_DOUBLE("time", atm->time, 0);
13863 NC_PUT_DOUBLE("LAT", atm->lat, 0);
13864 NC_PUT_DOUBLE("LON", atm->lon, 0);
13865 NC_PUT_DOUBLE("PRESS", atm->p, 0);
13866 NC_PUT_DOUBLE("ZETA", atm->q[ctl->qnt_zeta_d], 0);
13867 for (int iq = 0; iq < ctl->nq; iq++)
13868 NC_PUT_DOUBLE(ctl->qnt_name[iq], atm->q[iq], 0);
13869
13870 /* Close file... */
13871 NC(nc_close(ncid));
13872}
#define NC_PUT_ATT_GLOBAL(attname, text)
Add a global text attribute to a NetCDF file.
Definition mptrac.h:1727
#define NC_DEF_VAR(varname, type, ndims, dims, long_name, units, level, quant)
Define a NetCDF variable with attributes.
Definition mptrac.h:1546
#define NC_PUT_DOUBLE(varname, ptr, hyperslab)
Write double precision data to a NetCDF variable.
Definition mptrac.h:1641

◆ write_atm_clams_traj()

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.

The write_atm_clams_traj function writes trajectory data for the CLaMS model to a NetCDF file. The file is created and populated with data including time, latitude, longitude, pressure, and other quantities. The function also handles the creation of a final initialization file at the last time step.

Parameters
dirnameA string representing the directory name where the file will be created.
ctlA pointer to a ctl_t structure containing control parameters.
atmA pointer to an atm_t structure containing atmospheric data.
tThe current time in seconds since a reference epoch.

The function performs the following steps:

  • Determines the start and stop times of the calculation.
  • Constructs the output filename based on the start and stop times.
  • Defines the hyperslab for the trajectory file.
  • Creates the NetCDF file if it's the first time step and defines dimensions and variables.
  • Writes the trajectory data to the NetCDF file.
  • At the last time step, creates an initialization file with the final data.
Author
Jan Clemens

Definition at line 13876 of file mptrac.c.

13880 {
13881
13882 if (ctl->met_coord_type != 0)
13883 ERRMSG("CLaMS atmospheric files support only lat/lon grids");
13884
13885 /* Global Counter... */
13886 static size_t out_cnt = 0;
13887
13888 double r, r_start, r_stop;
13889 int year, mon, day, hour, min, sec;
13890 int year_start, mon_start, day_start, hour_start, min_start, sec_start;
13891 int year_stop, mon_stop, day_stop, hour_stop, min_stop, sec_stop;
13892 char filename_out[2 * LEN] = "traj_fix_3d_YYYYMMDDHH_YYYYMMDDHH.nc";
13893
13894 int ncid, varid, tid, pid, cid;
13895 int dim_ids[2];
13896
13897 /* time, nparc */
13898 size_t start[2];
13899 size_t count[2];
13900
13901 /* Determine start and stop times of calculation... */
13902 jsec2time(t, &year, &mon, &day, &hour, &min, &sec, &r);
13903 jsec2time(ctl->t_start, &year_start, &mon_start, &day_start, &hour_start,
13904 &min_start, &sec_start, &r_start);
13905 jsec2time(ctl->t_stop, &year_stop, &mon_stop, &day_stop, &hour_stop,
13906 &min_stop, &sec_stop, &r_stop);
13907
13908 sprintf(filename_out,
13909 "%s/traj_fix_3d_%02d%02d%02d%02d_%02d%02d%02d%02d.nc", dirname,
13910 year_start % 100, mon_start, day_start, hour_start,
13911 year_stop % 100, mon_stop, day_stop, hour_stop);
13912 LOG(1, "Write traj file: %s", filename_out);
13913
13914 /* Define hyperslap for the traj_file... */
13915 start[0] = out_cnt;
13916 start[1] = 0;
13917 count[0] = 1;
13918 count[1] = (size_t) atm->np;
13919
13920 /* Create the file at the first timestep... */
13921 if (out_cnt == 0) {
13922
13923 /* Create file... */
13924 NC(nc_create(filename_out, NC_NETCDF4, &ncid));
13925
13926 /* Define dimensions... */
13927 NC(nc_def_dim(ncid, "time", NC_UNLIMITED, &tid));
13928 NC(nc_def_dim(ncid, "NPARTS", (size_t) atm->np, &pid));
13929 NC(nc_def_dim(ncid, "TMDT", 7, &cid));
13930 dim_ids[0] = tid;
13931 dim_ids[1] = pid;
13932
13933 /* Define variables and their attributes... */
13934 NC_DEF_VAR("time", NC_DOUBLE, 1, &tid, "Time",
13935 "seconds since 2000-01-01 00:00:00 UTC", ctl->atm_nc_level, 0);
13936 NC_DEF_VAR("LAT", NC_DOUBLE, 2, dim_ids, "Latitude", "deg",
13937 ctl->atm_nc_level, 0);
13938 NC_DEF_VAR("LON", NC_DOUBLE, 2, dim_ids, "Longitude", "deg",
13939 ctl->atm_nc_level, 0);
13940 NC_DEF_VAR("PRESS", NC_DOUBLE, 2, dim_ids, "Pressure", "hPa",
13941 ctl->atm_nc_level, 0);
13942 NC_DEF_VAR("ZETA", NC_DOUBLE, 2, dim_ids, "Zeta", "K",
13943 ctl->atm_nc_level, 0);
13944 for (int iq = 0; iq < ctl->nq; iq++)
13945 NC_DEF_VAR(ctl->qnt_name[iq], NC_DOUBLE, 2, dim_ids,
13946 ctl->qnt_name[iq], ctl->qnt_unit[iq],
13947 ctl->atm_nc_level, ctl->atm_nc_quant[iq]);
13948
13949 /* Define global attributes... */
13950 NC_PUT_ATT_GLOBAL("exp_VERTCOOR_name", "zeta");
13951 NC_PUT_ATT_GLOBAL("model", "MPTRAC");
13952
13953 /* End definitions... */
13954 NC(nc_enddef(ncid));
13955 NC(nc_close(ncid));
13956 }
13957
13958 /* Increment global counter to change hyperslap... */
13959 out_cnt++;
13960
13961 /* Open file... */
13962 NC(nc_open(filename_out, NC_WRITE, &ncid));
13963
13964 /* Write data... */
13965 NC_PUT_DOUBLE("time", atm->time, 1);
13966 NC_PUT_DOUBLE("LAT", atm->lat, 1);
13967 NC_PUT_DOUBLE("LON", atm->lon, 1);
13968 NC_PUT_DOUBLE("PRESS", atm->p, 1);
13969 if (ctl->advect_vert_coord == 1) {
13970 NC_PUT_DOUBLE("ZETA", atm->q[ctl->qnt_zeta], 1);
13971 } else if (ctl->qnt_zeta >= 0) {
13972 NC_PUT_DOUBLE("ZETA", atm->q[ctl->qnt_zeta_d], 1);
13973 }
13974 for (int iq = 0; iq < ctl->nq; iq++)
13975 NC_PUT_DOUBLE(ctl->qnt_name[iq], atm->q[iq], 1);
13976
13977 /* Close file... */
13978 NC(nc_close(ncid));
13979
13980 /* At the last time step create the init_fix_YYYYMMDDHH file... */
13981 if ((year == year_stop) && (mon == mon_stop)
13982 && (day == day_stop) && (hour == hour_stop)) {
13983
13984 /* Set filename... */
13985 char filename_init[2 * LEN] = "./init_fix_YYYYMMDDHH.nc";
13986 sprintf(filename_init, "%s/init_fix_%02d%02d%02d%02d.nc",
13987 dirname, year_stop % 100, mon_stop, day_stop, hour_stop);
13988 LOG(1, "Write init file: %s", filename_init);
13989
13990 /* Create file... */
13991 NC(nc_create(filename_init, NC_NETCDF4, &ncid));
13992
13993 /* Define dimensions... */
13994 NC(nc_def_dim(ncid, "time", 1, &tid));
13995 NC(nc_def_dim(ncid, "NPARTS", (size_t) atm->np, &pid));
13996 dim_ids[0] = tid;
13997 dim_ids[1] = pid;
13998
13999 /* Define variables and their attributes... */
14000 NC_DEF_VAR("time", NC_DOUBLE, 1, &tid, "Time",
14001 "seconds since 2000-01-01 00:00:00 UTC", ctl->atm_nc_level, 0);
14002 NC_DEF_VAR("LAT", NC_DOUBLE, 1, &pid, "Latitude", "deg",
14003 ctl->atm_nc_level, 0);
14004 NC_DEF_VAR("LON", NC_DOUBLE, 1, &pid, "Longitude", "deg",
14005 ctl->atm_nc_level, 0);
14006 NC_DEF_VAR("PRESS", NC_DOUBLE, 1, &pid, "Pressure", "hPa",
14007 ctl->atm_nc_level, 0);
14008 NC_DEF_VAR("ZETA", NC_DOUBLE, 1, &pid, "Zeta", "K", ctl->atm_nc_level, 0);
14009 for (int iq = 0; iq < ctl->nq; iq++)
14010 NC_DEF_VAR(ctl->qnt_name[iq], NC_DOUBLE, 2, dim_ids,
14011 ctl->qnt_name[iq], ctl->qnt_unit[iq],
14012 ctl->atm_nc_level, ctl->atm_nc_quant[iq]);
14013
14014 /* Define global attributes... */
14015 NC_PUT_ATT_GLOBAL("exp_VERTCOOR_name", "zeta");
14016 NC_PUT_ATT_GLOBAL("model", "MPTRAC");
14017
14018 /* End definitions... */
14019 NC(nc_enddef(ncid));
14020
14021 /* Write data... */
14022 NC_PUT_DOUBLE("time", atm->time, 0);
14023 NC_PUT_DOUBLE("LAT", atm->lat, 0);
14024 NC_PUT_DOUBLE("LON", atm->lon, 0);
14025 NC_PUT_DOUBLE("PRESS", atm->p, 0);
14026 NC_PUT_DOUBLE("ZETA", atm->q[ctl->qnt_zeta_d], 0);
14027 for (int iq = 0; iq < ctl->nq; iq++)
14028 NC_PUT_DOUBLE(ctl->qnt_name[iq], atm->q[iq], 0);
14029
14030 /* Close file... */
14031 NC(nc_close(ncid));
14032 }
14033}
Here is the call graph for this function:

◆ write_atm_nc()

void write_atm_nc ( const char * filename,
const ctl_t * ctl,
const atm_t * atm )

Writes air parcel data to a NetCDF file.

The write_atm_nc function creates a NetCDF file and writes air parcel data into it. The data includes time, pressure, longitude, latitude, and other specified quantities. The function defines the dimensions and variables, sets global attributes, and writes the data to the file.

Parameters
filenameA string representing the name of the output file.
ctlA pointer to a ctl_t structure containing control parameters.
atmA pointer to an atm_t structure containing atmospheric data.

The function performs the following steps:

  • Creates the NetCDF file with the specified filename.
  • Defines the dimension for the number of observations (obs).
  • Defines variables for time, pressure, longitude, latitude, and other quantities.
  • Sets global attributes for the feature type.
  • Writes the data into the NetCDF file.
  • Closes the NetCDF file after writing.
Author
Lars Hoffmann

Definition at line 14037 of file mptrac.c.

14040 {
14041
14042 int ncid, obsid, varid;
14043
14044 size_t start[2], count[2];
14045
14046 /* Create file... */
14047 NC(nc_create(filename, NC_NETCDF4, &ncid));
14048
14049 /* Define dimensions... */
14050 NC(nc_def_dim(ncid, "obs", (size_t) atm->np, &obsid));
14051
14052 /* Define variables and their attributes... */
14053 NC_DEF_VAR("time", NC_DOUBLE, 1, &obsid, "time",
14054 "seconds since 2000-01-01 00:00:00 UTC", ctl->atm_nc_level, 0);
14055 NC_DEF_VAR("press", NC_DOUBLE, 1, &obsid, "pressure", "hPa",
14056 ctl->atm_nc_level, 0);
14057 NC_DEF_VAR("lon", NC_DOUBLE, 1, &obsid, "longitude", "degrees_east",
14058 ctl->atm_nc_level, 0);
14059 NC_DEF_VAR("lat", NC_DOUBLE, 1, &obsid, "latitude", "degrees_north",
14060 ctl->atm_nc_level, 0);
14061 for (int iq = 0; iq < ctl->nq; iq++)
14062 NC_DEF_VAR(ctl->qnt_name[iq], NC_DOUBLE, 1, &obsid,
14063 ctl->qnt_longname[iq], ctl->qnt_unit[iq],
14064 ctl->atm_nc_level, ctl->atm_nc_quant[iq]);
14065
14066 /* Define global attributes... */
14067 NC_PUT_ATT_GLOBAL("featureType", "point");
14068
14069 /* End definitions... */
14070 NC(nc_enddef(ncid));
14071
14072 /* Write data... */
14073 NC_PUT_DOUBLE("time", atm->time, 0);
14074 NC_PUT_DOUBLE("press", atm->p, 0);
14075 NC_PUT_DOUBLE("lon", atm->lon, 0);
14076 NC_PUT_DOUBLE("lat", atm->lat, 0);
14077 for (int iq = 0; iq < ctl->nq; iq++)
14078 NC_PUT_DOUBLE(ctl->qnt_name[iq], atm->q[iq], 0);
14079
14080 /* Close file... */
14081 NC(nc_close(ncid));
14082}

◆ write_budget()

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.

The output contains the number of active air parcels, their total mass, and sums of the configured cumulative mass-loss quantities. If ensembles are enabled, one row per ensemble and an additional total row are written. Domain-decomposed runs report the local budget of each subdomain. Missing mass-loss quantities are reported as NaN.

Parameters
filenamePath to the budget output file.
ctlPointer to the control structure.
atmPointer to the atmospheric data structure.
tCurrent model time [s].
Author
Lars Hoffmann

Definition at line 14086 of file mptrac.c.

14090 {
14091
14092 static FILE *out;
14093
14094 /* Set timer... */
14095 SELECT_TIMER("WRITE_BUDGET", "OUTPUT");
14096
14097 /* Check quantities... */
14098 if (ctl->qnt_m < 0)
14099 ERRMSG("Need quantity mass!");
14100 const int ensemble = (ctl->nens > 0);
14101 if (ensemble && ctl->qnt_ens < 0)
14102 ERRMSG("Missing ensemble IDs!");
14103 if (ctl->nens > NENS)
14104 ERRMSG("Too many ensembles!");
14105
14106 /* Initialize output file... */
14107 if (out == NULL) {
14108 LOG(1, "Write mass budget data: %s", filename);
14109 if (!(out = fopen(filename, "w")))
14110 ERRMSG("Cannot create file!");
14111 fprintf(out,
14112 "# $1 = time [s]\n"
14113 "# $2 = ensemble ID (-999=total)\n"
14114 "# $3 = number of active air parcels [1]\n"
14115 "# $4 = total mass [kg]\n"
14116 "# $5 = mass loss due to OH chemistry [kg]\n"
14117 "# $6 = mass loss due to H2O2 chemistry [kg]\n"
14118 "# $7 = mass loss due to KPP chemistry [kg]\n"
14119 "# $8 = mass loss due to wet deposition [kg]\n"
14120 "# $9 = mass loss due to dry deposition [kg]\n"
14121 "# $10 = mass loss due to exponential decay [kg]\n"
14122 "# $11 = total tracked mass loss [kg]\n"
14123 "# $12 = accounted mass [kg]\n\n");
14124 }
14125
14126 /* Calculate mass budget... */
14127 const int nq = 7;
14128 const int qnt[7] = {
14129 ctl->qnt_m, ctl->qnt_mloss_oh, ctl->qnt_mloss_h2o2,
14130 ctl->qnt_mloss_kpp, ctl->qnt_mloss_wet, ctl->qnt_mloss_dry,
14131 ctl->qnt_mloss_decay
14132 };
14133 double sum[NENS + 1][7] = { {0} };
14134 int np[NENS + 1] = { 0 };
14135 const double t0 = t - 0.5 * ctl->dt_mod;
14136 const double t1 = t + 0.5 * ctl->dt_mod;
14137
14138 /* Sum active air parcels... */
14139 for (int ip = 0; ip < atm->np; ip++) {
14140 if (atm->time[ip] < t0 || atm->time[ip] > t1)
14141 continue;
14142 int e = 0;
14143 if (ensemble) {
14144 e = (int) atm->q[ctl->qnt_ens][ip];
14145 if (e < 0 || e >= ctl->nens)
14146 ERRMSG("Ensemble ID out of range!");
14147 }
14148 np[e]++;
14149 np[NENS]++;
14150 for (int iq = 0; iq < nq; iq++)
14151 if (qnt[iq] >= 0) {
14152 sum[e][iq] += atm->q[qnt[iq]][ip];
14153 sum[NENS][iq] += atm->q[qnt[iq]][ip];
14154 }
14155 }
14156
14157 /* Write total and ensemble budgets... */
14158 for (int ie = -1; ie < (ensemble ? ctl->nens : 0); ie++) {
14159 const int e = (ie < 0 ? NENS : ie);
14160 double mloss = 0;
14161 for (int iq = 1; iq < nq; iq++)
14162 if (qnt[iq] >= 0)
14163 mloss += sum[e][iq];
14164 fprintf(out, "%.2f %d %d %g", t, ie < 0 ? -999 : ie, np[e], sum[e][0]);
14165 for (int iq = 1; iq < nq; iq++)
14166 fprintf(out, " %g", qnt[iq] >= 0 ? sum[e][iq] : NAN);
14167 fprintf(out, " %g %g\n", mloss, sum[e][0] + mloss);
14168 }
14169 fflush(out);
14170
14171 /* Finalize... */
14172 if (t == ctl->t_stop) {
14173 fclose(out);
14174 out = NULL;
14175 }
14176}
#define NENS
Maximum number of data points for ensemble analysis.
Definition mptrac.h:584

◆ write_csi()

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.

The write_csi function processes air parcel and observation data to calculate and write various verification statistics, including the Critical Success Index (CSI), to a specified output file at regular intervals. The statistics include measures such as the number of hits, misses, and false alarms, bias, probability of detection, false alarm rate, equitable threat score, and correlation coefficients.

Parameters
filenameA string representing the name of the output file.
ctlA pointer to a ctl_t structure containing control parameters.
atmA pointer to an atm_t structure containing atmospheric data.
tA double representing the current time.

The function performs the following steps:

  • Initializes resources and sets up the output file if the current time is the start time.
  • Reads observation data and kernel data if provided.
  • Sets grid box sizes and horizontal coordinates.
  • Allocates memory for mean and count arrays.
  • Loops over observations and model data to accumulate mean values and counts.
  • Analyzes the grid cells to calculate CSI and other statistics.
  • Writes the calculated statistics to the output file at specified intervals.
  • Frees allocated resources and closes the file when the processing is complete.
Author
Lars Hoffmann

Definition at line 14180 of file mptrac.c.

14184 {
14185
14186 if (ctl->met_coord_type != 0)
14187 ERRMSG("Only lat/lon grid supported");
14188
14189 static FILE *out;
14190
14191 static double *modmean, *obsmean, *obsstd, *rt, *rz, *rlon, *rlat, *robs,
14192 *area, dlon, dlat, dz, x[NCSI], y[NCSI], obsstdn[NCSI], kz[EP], kw[EP];
14193
14194 static int *obscount, nobs, nk;
14195
14196 static int ct[NENS], cx[NENS], cy[NENS], cz[NENS], n[NENS];
14197
14198 const int ensemble = (ctl->nens > 0);
14199
14200 /* Set timer */
14201 SELECT_TIMER("WRITE_CSI", "OUTPUT");
14202
14203 /* Check quantities... */
14204 if (ctl->qnt_m < 0)
14205 ERRMSG("Need quantity mass!");
14206 if (ensemble) {
14207 if (ctl->qnt_ens < 0)
14208 ERRMSG("Missing ensemble IDs!");
14209 if (ctl->nens > NENS)
14210 ERRMSG("Too many ensembles!");
14211 }
14212
14213 /* Init... */
14214 if (t == ctl->t_start) {
14215
14216 /* Allocate.. */
14217 ALLOC(area, double,
14218 ctl->csi_ny);
14219 ALLOC(rt, double,
14220 NOBS);
14221 ALLOC(rz, double,
14222 NOBS);
14223 ALLOC(rlon, double,
14224 NOBS);
14225 ALLOC(rlat, double,
14226 NOBS);
14227 ALLOC(robs, double,
14228 NOBS);
14229
14230 /* Read observation data... */
14231 read_obs(ctl->csi_obsfile, ctl, rt, rz, rlon, rlat, robs, &nobs);
14232
14233 /* Read kernel data... */
14234 if (ctl->csi_kernel[0] != '-')
14235 read_kernel(ctl->csi_kernel, kz, kw, &nk);
14236
14237 /* Create new file... */
14238 LOG(1, "Write CSI%s data: %s", ensemble ? " ensemble" : "", filename);
14239 if (!(out = fopen(filename, "w")))
14240 ERRMSG("Cannot create file!");
14241
14242 /* Write header... */
14243 fprintf(out,
14244 "# $1 = time [s]\n"
14245 "# $2 = ensemble ID\n"
14246 "# $3 = number of hits (cx)\n"
14247 "# $4 = number of misses (cy)\n"
14248 "# $5 = number of false alarms (cz)\n"
14249 "# $6 = number of observations (cx + cy)\n"
14250 "# $7 = number of forecasts (cx + cz)\n"
14251 "# $8 = bias (%%)\n"
14252 "# $9 = POD (%%)\n"
14253 "# $10 = FAR (%%)\n"
14254 "# $11 = CSI (%%)\n"
14255 "# $12 = hits by random chance\n"
14256 "# $13 = ETS (%%)\n"
14257 "# $14 = Pearson R\n"
14258 "# $15 = Spearman R\n"
14259 "# $16 = mean error [kg/m²]\n"
14260 "# $17 = RMSE [kg/m²]\n"
14261 "# $18 = MAE [kg/m²]\n"
14262 "# $19 = log-likelihood\n" "# $20 = number of points\n\n");
14263
14264 /* Set grid box size... */
14265 dz = (ctl->csi_z1 - ctl->csi_z0) / ctl->csi_nz;
14266 dlon = (ctl->csi_lon1 - ctl->csi_lon0) / ctl->csi_nx;
14267 dlat = (ctl->csi_lat1 - ctl->csi_lat0) / ctl->csi_ny;
14268
14269 /* Set horizontal coordinates... */
14270 for (int iy = 0; iy < ctl->csi_ny; iy++) {
14271 const double lat = ctl->csi_lat0 + dlat * (iy + 0.5);
14272 area[iy] = dlat * dlon * SQR(RE * M_PI / 180.0) * cos(DEG2RAD(lat));
14273 }
14274 }
14275
14276 /* Set time interval... */
14277 const double t0 = t - 0.5 * ctl->dt_mod;
14278 const double t1 = t + 0.5 * ctl->dt_mod;
14279
14280 /* Allocate... */
14281 int grid_size = ctl->csi_nx * ctl->csi_ny * ctl->csi_nz;
14282 ALLOC(modmean, double,
14283 (ensemble ? ctl->nens : 1) * grid_size);
14284 ALLOC(obsmean, double,
14285 grid_size);
14286 ALLOC(obscount, int,
14287 grid_size);
14288 ALLOC(obsstd, double,
14289 grid_size);
14290
14291 /* Init... */
14292 for (int i = 0; i < (ensemble ? ctl->nens : 1); i++)
14293 ct[i] = cx[i] = cy[i] = cz[i] = n[i] = 0;
14294
14295 /* Loop over observations... */
14296 for (int i = 0; i < nobs; i++) {
14297 if (rt[i] < t0 || rt[i] >= t1 || !isfinite(robs[i]))
14298 continue;
14299
14300 /* Check grid boundaries and calculate indices... */
14301 if (rlon[i] < ctl->csi_lon0 || rlon[i] >= ctl->csi_lon1
14302 || rlat[i] < ctl->csi_lat0 || rlat[i] >= ctl->csi_lat1
14303 || rz[i] < ctl->csi_z0 || rz[i] >= ctl->csi_z1)
14304 continue;
14305 const int ix = (int) ((rlon[i] - ctl->csi_lon0) / dlon);
14306 const int iy = (int) ((rlat[i] - ctl->csi_lat0) / dlat);
14307 const int iz = (int) ((rz[i] - ctl->csi_z0) / dz);
14308 if (ix >= ctl->csi_nx || iy >= ctl->csi_ny || iz >= ctl->csi_nz)
14309 continue;
14310
14311 /* Get mean observation index... */
14312 const int idx = ARRAY_3D(ix, iy, ctl->csi_ny, iz, ctl->csi_nz);
14313 obsmean[idx] += robs[i];
14314 obsstd[idx] += SQR(robs[i]);
14315 obscount[idx]++;
14316 }
14317
14318 /* Analyze model data... */
14319 for (int ip = 0; ip < atm->np; ip++) {
14320
14321 /* Check time... */
14322 if (atm->time[ip] < t0 || atm->time[ip] > t1)
14323 continue;
14324
14325 /* Get ensemble ID... */
14326 int ens_id = ensemble ? (int) atm->q[ctl->qnt_ens][ip] : 0;
14327 if (ens_id < 0 || ens_id >= (ensemble ? ctl->nens : 1))
14328 ERRMSG("Ensemble ID out of range!");
14329
14330 /* Check grid boundaries and get indices... */
14331 const double zpart = Z(atm->p[ip]);
14332 if (atm->lon[ip] < ctl->csi_lon0
14333 || atm->lon[ip] >= ctl->csi_lon1
14334 || atm->lat[ip] < ctl->csi_lat0
14335 || atm->lat[ip] >= ctl->csi_lat1
14336 || zpart < ctl->csi_z0 || zpart >= ctl->csi_z1)
14337 continue;
14338 const int ix = (int) ((atm->lon[ip] - ctl->csi_lon0) / dlon);
14339 const int iy = (int) ((atm->lat[ip] - ctl->csi_lat0) / dlat);
14340 const int iz = (int) ((zpart - ctl->csi_z0) / dz);
14341 if (ix >= ctl->csi_nx || iy >= ctl->csi_ny || iz >= ctl->csi_nz)
14342 continue;
14343
14344 /* Get total mass in grid cell... */
14345 const int idx =
14346 ens_id * grid_size + ARRAY_3D(ix, iy, ctl->csi_ny, iz, ctl->csi_nz);
14347 modmean[idx] +=
14348 kernel_weight(kz, kw, nk, atm->p[ip]) * atm->q[ctl->qnt_m][ip];
14349 }
14350 for (int e = 0; e < (ensemble ? ctl->nens : 1); e++) {
14351 /* Analyze all grid cells... */
14352 for (int ix = 0; ix < ctl->csi_nx; ix++)
14353 for (int iy = 0; iy < ctl->csi_ny; iy++)
14354 for (int iz = 0; iz < ctl->csi_nz; iz++) {
14355
14356 /* Calculate mean observation index... */
14357 const int idx = ARRAY_3D(ix, iy, ctl->csi_ny, iz, ctl->csi_nz);
14358 if (e == 0)
14359 if (obscount[idx]) {
14360 obsmean[idx] /= obscount[idx];
14361 obsstd[idx] =
14362 sqrt(obsstd[idx] / obscount[idx] - SQR(obsmean[idx]));
14363 }
14364
14365 /* Calculate model mean per ensemble... */
14366 const int midx = e * grid_size + idx;
14367 if (modmean[midx] > 0)
14368 modmean[midx] /= (1e6 * area[iy]);
14369
14370 /* Check number of observations... */
14371 if (obscount[idx]) {
14372
14373 /* Calculate CSI... */
14374 ct[e]++;
14375 if (obsmean[idx] >= ctl->csi_obsmin
14376 && modmean[midx] >= ctl->csi_modmin)
14377 cx[e]++;
14378 else if (obsmean[idx] >= ctl->csi_obsmin)
14379 cy[e]++;
14380 else if (modmean[midx] >= ctl->csi_modmin)
14381 cz[e]++;
14382
14383 /* Save data for other verification statistics... */
14384 if (obsmean[idx] >= ctl->csi_obsmin
14385 || modmean[midx] >= ctl->csi_modmin) {
14386 x[n[e]] = modmean[midx];
14387 y[n[e]] = obsmean[idx];
14388 if (modmean[midx] >= ctl->csi_modmin)
14389 obsstdn[n[e]] = obsstd[idx];
14390 if ((++n[e]) >= NCSI)
14391 ERRMSG("Too many points for statistics!");
14392 }
14393 }
14394 }
14395 /* Write output... */
14396 if (fmod(t, ctl->csi_dt_out) == 0) {
14397
14398 if (n[e] == 0)
14399 continue;
14400
14401 /* Calculate verification statistics
14402 (https://www.cawcr.gov.au/projects/verification/) ... */
14403 static double work[2 * NCSI], work2[2 * NCSI];
14404 const int n_obs = cx[e] + cy[e];
14405 const int n_for = cx[e] + cz[e];
14406 const double cx_rd = (ct[e] > 0) ? (1. * n_obs * n_for) / ct[e] : NAN;
14407 const double bias = (n_obs > 0) ? 100. * n_for / n_obs : NAN;
14408 const double pod = (n_obs > 0) ? 100. * cx[e] / n_obs : NAN;
14409 const double far = (n_for > 0) ? 100. * cz[e] / n_for : NAN;
14410 const double csi =
14411 (cx[e] + cy[e] + cz[e] >
14412 0) ? 100. * cx[e] / (cx[e] + cy[e] + cz[e]) : NAN;
14413 const double ets =
14414 (cx[e] + cy[e] + cz[e] - cx_rd >
14415 0) ? 100. * (cx[e] - cx_rd) / (cx[e] + cy[e] + cz[e] - cx_rd) : NAN;
14416 const double rho_p = gsl_stats_correlation(x, 1, y, 1, (size_t) n[e]);
14417 const double rho_s =
14418 gsl_stats_spearman(x, 1, y, 1, (size_t) n[e], work);
14419 for (int i = 0; i < n[e]; i++) {
14420 work[i] = x[i] - y[i];
14421 work2[i] = (obsstdn[i] != 0) ? work[i] / obsstdn[i] : 0;
14422 }
14423 const double mean = gsl_stats_mean(work, 1, (size_t) n[e]);
14424 const double rmse =
14425 gsl_stats_sd_with_fixed_mean(work, 1, (size_t) n[e], 0.0);
14426 const double absdev = gsl_stats_absdev_m(work, 1, (size_t) n[e], 0.0);
14427 const double loglikelihood =
14428 gsl_stats_tss_m(work2, 1, (size_t) n[e], 0.0) * -0.5;
14429
14430 /* Write... */
14431 fprintf(out,
14432 "%.2f %d %d %d %d %d %d %g %g %g %g %g %g %g %g %g %g %g %g %d\n",
14433 t, ensemble ? e : -999, cx[e], cy[e], cz[e], n_obs, n_for, bias,
14434 pod, far, csi, cx_rd, ets, rho_p, rho_s, mean, rmse, absdev,
14435 loglikelihood, n[e]);
14436
14437 /* Set counters to zero... */
14438 for (int i = 0; i < n[e]; i++)
14439 work[i] = work2[i] = x[i] = y[i] = obsstdn[i] = 0;
14440 ct[e] = cx[e] = cy[e] = cz[e] = n[e] = 0;
14441 }
14442 }
14443 /* Free... */
14444 free(modmean);
14445 free(obsmean);
14446 free(obscount);
14447 free(obsstd);
14448
14449 /* Finalize... */
14450 if (t == ctl->t_stop) {
14451
14452 /* Close output file... */
14453 fclose(out);
14454
14455 /* Free... */
14456 free(area);
14457 free(rt);
14458 free(rz);
14459 free(rlon);
14460 free(rlat);
14461 free(robs);
14462 }
14463}
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:13228
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:9438
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:3604
#define NCSI
Maximum number of data points for CSI calculation.
Definition mptrac.h:579
Here is the call graph for this function:

◆ write_ens()

void write_ens ( const char * filename,
const ctl_t * ctl,
const atm_t * atm,
const double t )

Writes ensemble data to a file.

The write_ens function processes air parcel data to calculate ensemble means and standard deviations for various quantities and writes them to a specified output file. It handles ensemble members and calculates statistics such as means and standard deviations for each ensemble, along with latitude, longitude, altitude, and time information.

Parameters
filenameA string representing the name of the output file.
ctlA pointer to a ctl_t structure containing control parameters.
atmA pointer to an atm_t structure containing atmospheric data.
tA double representing the current time.

The function performs the following steps:

  • Initializes resources and sets up necessary variables.
  • Sets a time interval for processing data.
  • Loops over air parcels to accumulate means and standard deviations for each ensemble member.
  • Creates an output file and writes header information.
  • Writes ensemble data, including time, altitude, latitude, longitude, means, standard deviations, and the number of members.
  • Closes the output file.
Author
Lars Hoffmann

Definition at line 14467 of file mptrac.c.

14471 {
14472
14473 if (ctl->met_coord_type != 0)
14474 ERRMSG("Only lat/lon grid supported");
14475
14476 static FILE *out;
14477
14478 static double dummy, lat, lon, qm[NQ][NENS], qs[NQ][NENS], xm[NENS][3],
14479 x[3], zm[NENS];
14480
14481 static int n[NENS];
14482
14483 /* Set timer... */
14484 SELECT_TIMER("WRITE_ENS", "OUTPUT");
14485
14486 /* Check quantities... */
14487 if (ctl->qnt_ens < 0)
14488 ERRMSG("Missing ensemble IDs!");
14489
14490 /* Set time interval... */
14491 const double t0 = t - 0.5 * ctl->dt_mod;
14492 const double t1 = t + 0.5 * ctl->dt_mod;
14493
14494 /* Init... */
14495 for (int i = 0; i < NENS; i++) {
14496 for (int iq = 0; iq < ctl->nq; iq++)
14497 qm[iq][i] = qs[iq][i] = 0;
14498 xm[i][0] = xm[i][1] = xm[i][2] = zm[i] = 0;
14499 n[i] = 0;
14500 }
14501
14502 /* Loop over air parcels... */
14503 for (int ip = 0; ip < atm->np; ip++) {
14504
14505 /* Check time... */
14506 if (atm->time[ip] < t0 || atm->time[ip] > t1)
14507 continue;
14508
14509 /* Check ensemble ID... */
14510 if (atm->q[ctl->qnt_ens][ip] < 0 || atm->q[ctl->qnt_ens][ip] >= NENS)
14511 ERRMSG("Ensemble ID is out of range!");
14512
14513 /* Get means... */
14514 geo2cart(0, atm->lon[ip], atm->lat[ip], x);
14515 for (int iq = 0; iq < ctl->nq; iq++) {
14516 qm[iq][ctl->qnt_ens] += atm->q[iq][ip];
14517 qs[iq][ctl->qnt_ens] += SQR(atm->q[iq][ip]);
14518 }
14519 xm[ctl->qnt_ens][0] += x[0];
14520 xm[ctl->qnt_ens][1] += x[1];
14521 xm[ctl->qnt_ens][2] += x[2];
14522 zm[ctl->qnt_ens] += Z(atm->p[ip]);
14523 n[ctl->qnt_ens]++;
14524 }
14525
14526 /* Create file... */
14527 LOG(1, "Write ensemble data: %s", filename);
14528 if (!(out = fopen(filename, "w")))
14529 ERRMSG("Cannot create file!");
14530
14531 /* Write header... */
14532 fprintf(out,
14533 "# $1 = time [s]\n"
14534 "# $2 = altitude [km]\n"
14535 "# $3 = longitude [deg]\n" "# $4 = latitude [deg]\n");
14536 for (int iq = 0; iq < ctl->nq; iq++)
14537 fprintf(out, "# $%d = %s (mean) [%s]\n", 5 + iq,
14538 ctl->qnt_name[iq], ctl->qnt_unit[iq]);
14539 for (int iq = 0; iq < ctl->nq; iq++)
14540 fprintf(out, "# $%d = %s (sigma) [%s]\n", 5 + ctl->nq + iq,
14541 ctl->qnt_name[iq], ctl->qnt_unit[iq]);
14542 fprintf(out, "# $%d = number of members\n\n", 5 + 2 * ctl->nq);
14543
14544 /* Write data... */
14545 for (int i = 0; i < NENS; i++)
14546 if (n[i] > 0) {
14547 cart2geo(xm[i], &dummy, &lon, &lat);
14548 fprintf(out, "%.2f %g %g %g", t, zm[i] / n[i], lon, lat);
14549 for (int iq = 0; iq < ctl->nq; iq++) {
14550 fprintf(out, " ");
14551 fprintf(out, ctl->qnt_format[iq], qm[iq][i] / n[i]);
14552 }
14553 for (int iq = 0; iq < ctl->nq; iq++) {
14554 fprintf(out, " ");
14555 double var = qs[iq][i] / n[i] - SQR(qm[iq][i] / n[i]);
14556 fprintf(out, ctl->qnt_format[iq], (var > 0 ? sqrt(var) : 0));
14557 }
14558 fprintf(out, " %d\n", n[i]);
14559 }
14560
14561 /* Close file... */
14562 fclose(out);
14563}
void cart2geo(const double *x, double *z, double *lon, double *lat)
Converts Cartesian coordinates to geographic coordinates.
Definition mptrac.c:76
Here is the call graph for this function:

◆ write_depo()

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.

Cell centers and areas are derived from GRID_LON*, GRID_LAT*, GRID_NX, and GRID_NY. If RADIO_DECAY is enabled, inventories are decayed to t. Output format is selected by DEPO_TYPE.

Parameters
[in]filenameOutput filename.
[in]ctlGrid, format, compression, and decay settings.
[in]depoCumulative radionuclide inventories.
[in]tOutput validity time [s since 2000-01-01 00:00 UTC].

Definition at line 14567 of file mptrac.c.

14571 {
14572
14573 double *area, *data, *lat, *lon;
14574
14575 /* Set timer... */
14576 SELECT_TIMER("WRITE_DEPO", "OUTPUT");
14577 LOG(1, "Write radioactive deposition data: %s", filename);
14578
14579 /* Allocate output arrays... */
14580 const int nxy = ctl->grid_nx * ctl->grid_ny;
14581 ALLOC(area, double,
14582 ctl->grid_ny);
14583 ALLOC(data, double,
14584 4 * nxy);
14585 ALLOC(lat, double,
14586 ctl->grid_ny);
14587 ALLOC(lon, double,
14588 ctl->grid_nx);
14589
14590 /* Set horizontal coordinates and grid-cell areas... */
14591 const double dlon = (ctl->grid_lon1 - ctl->grid_lon0) / ctl->grid_nx;
14592 const double dlat = (ctl->grid_lat1 - ctl->grid_lat0) / ctl->grid_ny;
14593 for (int ix = 0; ix < ctl->grid_nx; ix++)
14594 lon[ix] = ctl->grid_lon0 + dlon * (ix + 0.5);
14595 for (int iy = 0; iy < ctl->grid_ny; iy++) {
14596 lat[iy] = ctl->grid_lat0 + dlat * (iy + 0.5);
14597 area[iy] = 1e6 * dlat * dlon * SQR(RE * M_PI / 180.)
14598 * cos(DEG2RAD(lat[iy]));
14599 }
14600
14601 /* Convert inventories to activity densities at output time... */
14602 const double lambda[4] = {
14603 log(2.0) / RADIO_HALF_LIFE_PB210,
14604 log(2.0) / RADIO_HALF_LIFE_BE7,
14605 log(2.0) / RADIO_HALF_LIFE_CS137,
14606 log(2.0) / RADIO_HALF_LIFE_I131
14607 };
14608 const double *inventory[4] = {
14609 depo->Apb210, depo->Abe7, depo->Acs137, depo->Ai131
14610 };
14611 for (int iq = 0; iq < 4; iq++) {
14612 const double decay = ctl->radio_decay
14613 ? exp(-lambda[iq] * (t - ctl->t_start)) : 1.0;
14614 for (int ix = 0; ix < ctl->grid_nx; ix++)
14615 for (int iy = 0; iy < ctl->grid_ny; iy++) {
14616 const int idx = ARRAY_2D(ix, iy, ctl->grid_ny);
14617 data[iq * nxy + idx] = inventory[iq][idx] * decay / area[iy];
14618 }
14619 }
14620
14621 /* Write output... */
14622 if (ctl->depo_type == 0)
14623 write_depo_asc(filename, ctl, data, t, lon, lat, area);
14624 else if (ctl->depo_type == 1)
14625 write_depo_nc(filename, ctl, data, t, lon, lat, area);
14626 else
14627 ERRMSG("Radioactive deposition output type unknown!");
14628
14629 /* Free... */
14630 free(area);
14631 free(data);
14632 free(lat);
14633 free(lon);
14634}
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:14638
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:14678
double Apb210[EX *EY]
Deposited Pb-210 activity [Bq].
Definition mptrac.h:3709
Here is the call graph for this function:

◆ write_depo_asc()

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.

Parameters
[in]filenameOutput filename.
[in]ctlDeposition grid dimensions.
[in]dataFour consecutive fields in Bq m^-2.
[in]tOutput validity time [s since 2000-01-01 00:00 UTC].
[in]lonGrid-cell center longitudes [deg].
[in]latGrid-cell center latitudes [deg].
[in]areaGrid-cell areas by latitude [m^2].

Definition at line 14638 of file mptrac.c.

14645 {
14646
14647 FILE *out;
14648 if (!(out = fopen(filename, "w")))
14649 ERRMSG("Cannot create file!");
14650
14651 fprintf(out,
14652 "# $1 = time [s]\n"
14653 "# $2 = longitude [deg]\n"
14654 "# $3 = latitude [deg]\n"
14655 "# $4 = area [m^2]\n"
14656 "# $5 = deposited Pb-210 activity [Bq/m^2]\n"
14657 "# $6 = deposited Be-7 activity [Bq/m^2]\n"
14658 "# $7 = deposited Cs-137 activity [Bq/m^2]\n"
14659 "# $8 = deposited I-131 activity [Bq/m^2]\n\n");
14660
14661 const int nxy = ctl->grid_nx * ctl->grid_ny;
14662 for (int ix = 0; ix < ctl->grid_nx; ix++) {
14663 for (int iy = 0; iy < ctl->grid_ny; iy++) {
14664 const int idx = ARRAY_2D(ix, iy, ctl->grid_ny);
14665 fprintf(out, "%.2f %g %g %g %g %g %g %g\n",
14666 t, lon[ix], lat[iy], area[iy],
14667 data[idx], data[nxy + idx],
14668 data[2 * nxy + idx], data[3 * nxy + idx]);
14669 }
14670 fprintf(out, "\n");
14671 }
14672
14673 fclose(out);
14674}

◆ write_depo_nc()

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.

Parameters
[in]filenameOutput filename.
[in]ctlDeposition grid dimensions and netCDF compression setting.
[in]dataFour consecutive fields in Bq m^-2.
[in]tOutput validity time [s since 2000-01-01 00:00 UTC].
[in]lonGrid-cell center longitudes [deg].
[in]latGrid-cell center latitudes [deg].
[in]areaGrid-cell areas by latitude [m^2].

Definition at line 14678 of file mptrac.c.

14685 {
14686
14687 double *help;
14688 int ncid, dimid[3], varid;
14689 size_t start[2], count[2];
14690
14691 const int nxy = ctl->grid_nx * ctl->grid_ny;
14692 ALLOC(help, double,
14693 nxy);
14694
14695 /* Create file and dimensions... */
14696 NC(nc_create(filename, NC_NETCDF4, &ncid));
14697 NC(nc_def_dim(ncid, "time", 1, &dimid[0]));
14698 NC(nc_def_dim(ncid, "lat", (size_t) ctl->grid_ny, &dimid[1]));
14699 NC(nc_def_dim(ncid, "lon", (size_t) ctl->grid_nx, &dimid[2]));
14700
14701 /* Define variables... */
14702 NC_DEF_VAR("time", NC_DOUBLE, 1, &dimid[0], "time",
14703 "seconds since 2000-01-01 00:00:00 UTC", 0, 0);
14704 NC_DEF_VAR("lat", NC_DOUBLE, 1, &dimid[1], "latitude",
14705 "degrees_north", 0, 0);
14706 NC_DEF_VAR("lon", NC_DOUBLE, 1, &dimid[2], "longitude",
14707 "degrees_east", 0, 0);
14708 NC_DEF_VAR("area", NC_DOUBLE, 1, &dimid[1], "surface area", "m**2", 0, 0);
14709 NC_DEF_VAR("depo_pb210", NC_DOUBLE, 3, dimid,
14710 "ground inventory of Pb-210", "Bq m**-2", ctl->grid_nc_level, 0);
14711 NC_DEF_VAR("depo_be7", NC_DOUBLE, 3, dimid,
14712 "ground inventory of Be-7", "Bq m**-2", ctl->grid_nc_level, 0);
14713 NC_DEF_VAR("depo_cs137", NC_DOUBLE, 3, dimid,
14714 "ground inventory of Cs-137", "Bq m**-2", ctl->grid_nc_level, 0);
14715 NC_DEF_VAR("depo_i131", NC_DOUBLE, 3, dimid,
14716 "ground inventory of aerosol-bound I-131", "Bq m**-2",
14717 ctl->grid_nc_level, 0);
14718 NC(nc_enddef(ncid));
14719
14720 /* Write coordinates... */
14721 NC_PUT_DOUBLE("time", &t, 0);
14722 NC_PUT_DOUBLE("lon", lon, 0);
14723 NC_PUT_DOUBLE("lat", lat, 0);
14724 NC_PUT_DOUBLE("area", area, 0);
14725
14726 /* Write fields in netCDF dimension order... */
14727 const char *varname[4] = {
14728 "depo_pb210", "depo_be7", "depo_cs137", "depo_i131"
14729 };
14730 for (int iq = 0; iq < 4; iq++) {
14731 for (int ix = 0; ix < ctl->grid_nx; ix++)
14732 for (int iy = 0; iy < ctl->grid_ny; iy++)
14733 help[ARRAY_2D(iy, ix, ctl->grid_nx)] =
14734 data[iq * nxy + ARRAY_2D(ix, iy, ctl->grid_ny)];
14735 NC_PUT_DOUBLE(varname[iq], help, 0);
14736 }
14737
14738 NC(nc_close(ncid));
14739 free(help);
14740}

◆ write_grid()

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.

The write_grid function processes air parcel data to calculate various grid-based statistics such as column density, mean, and standard deviation for specified quantities. It then writes this data to a specified output file either in ASCII or netCDF format based on the configuration parameters provided in the ctl structure.

Parameters
filenameA string representing the name of the output file.
ctlA pointer to a ctl_t structure containing control parameters.
met0A pointer to a met_t structure containing meteorological data for the initial time step.
met1A pointer to a met_t structure containing meteorological data for the final time step.
atmA pointer to an atm_t structure containing atmospheric data.
tA double representing the current time.

The function performs the following steps:

  • Initializes resources and sets up necessary variables.
  • Reads kernel data if it is specified in the control parameters.
  • Allocates memory for various arrays to store grid data.
  • Determines the grid box size and sets up vertical and horizontal coordinates.
  • Sets a time interval for output data processing.
  • Calculates grid box indices for atmospheric model data.
  • Averages data within each grid box.
  • Calculates column density and volume mixing ratio.
  • Writes data to the output file either in ASCII or netCDF format based on the specified grid_type in the control parameters.
  • Frees allocated memory.
Note
The function supports parallel processing using OpenMP for certain computational tasks to improve performance.
Author
Lars Hoffmann

Definition at line 14744 of file mptrac.c.

14750 {
14751
14752 if (ctl->met_coord_type != 0)
14753 ERRMSG("Only lat/lon grid supported");
14754
14755 static double kz[EP], kw[EP];
14756
14757 static int nk;
14758
14759 double *cd, *mean[NQ], *sigma[NQ], *vmr_impl, *z, *lon, *lat, *area, *press;
14760
14761 int *ixs, *iys, *izs, *np;
14762
14763 /* Set timer... */
14764 SELECT_TIMER("WRITE_GRID", "OUTPUT");
14765
14766 /* Write info... */
14767 LOG(1, "Write grid data: %s", filename);
14768
14769 /* Init... */
14770 if (t == ctl->t_start) {
14771
14772 /* Read kernel data... */
14773 if (ctl->grid_kernel[0] != '-')
14774 read_kernel(ctl->grid_kernel, kz, kw, &nk);
14775 }
14776
14777 /* Allocate... */
14778 ALLOC(cd, double,
14779 ctl->grid_nx * ctl->grid_ny * ctl->grid_nz);
14780 for (int iq = 0; iq < ctl->nq; iq++) {
14781 ALLOC(mean[iq], double,
14782 ctl->grid_nx * ctl->grid_ny * ctl->grid_nz);
14783 ALLOC(sigma[iq], double,
14784 ctl->grid_nx * ctl->grid_ny * ctl->grid_nz);
14785 }
14786 ALLOC(vmr_impl, double,
14787 ctl->grid_nx * ctl->grid_ny * ctl->grid_nz);
14788 ALLOC(z, double,
14789 ctl->grid_nz);
14790 ALLOC(lon, double,
14791 ctl->grid_nx);
14792 ALLOC(lat, double,
14793 ctl->grid_ny);
14794 ALLOC(area, double,
14795 ctl->grid_ny);
14796 ALLOC(press, double,
14797 ctl->grid_nz);
14798 ALLOC(np, int,
14799 ctl->grid_nx * ctl->grid_ny * ctl->grid_nz);
14800 ALLOC(ixs, int,
14801 atm->np);
14802 ALLOC(iys, int,
14803 atm->np);
14804 ALLOC(izs, int,
14805 atm->np);
14806
14807 /* Set grid box size... */
14808 const double dz = (ctl->grid_z1 - ctl->grid_z0) / ctl->grid_nz;
14809 const double dlon = (ctl->grid_lon1 - ctl->grid_lon0) / ctl->grid_nx;
14810 const double dlat = (ctl->grid_lat1 - ctl->grid_lat0) / ctl->grid_ny;
14811
14812 /* Set vertical coordinates... */
14813#pragma omp parallel for default(shared)
14814 for (int iz = 0; iz < ctl->grid_nz; iz++) {
14815 z[iz] = ctl->grid_z0 + dz * (iz + 0.5);
14816 press[iz] = P(z[iz]);
14817 }
14818
14819 /* Set horizontal coordinates... */
14820 for (int ix = 0; ix < ctl->grid_nx; ix++)
14821 lon[ix] = ctl->grid_lon0 + dlon * (ix + 0.5);
14822#pragma omp parallel for default(shared)
14823 for (int iy = 0; iy < ctl->grid_ny; iy++) {
14824 lat[iy] = ctl->grid_lat0 + dlat * (iy + 0.5);
14825 area[iy] = dlat * dlon * SQR(RE * M_PI / 180.) * cos(DEG2RAD(lat[iy]));
14826 }
14827
14828 /* Set time interval for output... */
14829 const double t0 = t - 0.5 * ctl->dt_mod;
14830 const double t1 = t + 0.5 * ctl->dt_mod;
14831
14832 /* Get grid box indices... */
14833#pragma omp parallel for default(shared)
14834 for (int ip = 0; ip < atm->np; ip++) {
14835 const double zpart = Z(atm->p[ip]);
14836 if (atm->time[ip] < t0 || atm->time[ip] > t1
14837 || atm->lon[ip] < ctl->grid_lon0
14838 || atm->lon[ip] >= ctl->grid_lon1
14839 || atm->lat[ip] < ctl->grid_lat0
14840 || atm->lat[ip] >= ctl->grid_lat1
14841 || zpart < ctl->grid_z0 || zpart >= ctl->grid_z1) {
14842 izs[ip] = -1;
14843 continue;
14844 }
14845 ixs[ip] = (int) ((atm->lon[ip] - ctl->grid_lon0) / dlon);
14846 iys[ip] = (int) ((atm->lat[ip] - ctl->grid_lat0) / dlat);
14847 izs[ip] = (int) ((zpart - ctl->grid_z0) / dz);
14848 if (ixs[ip] >= ctl->grid_nx || iys[ip] >= ctl->grid_ny
14849 || izs[ip] >= ctl->grid_nz)
14850 izs[ip] = -1;
14851 }
14852
14853 /* Average data... */
14854 for (int ip = 0; ip < atm->np; ip++)
14855 if (izs[ip] >= 0) {
14856 const int idx =
14857 ARRAY_3D(ixs[ip], iys[ip], ctl->grid_ny, izs[ip], ctl->grid_nz);
14858 const double kernel = kernel_weight(kz, kw, nk, atm->p[ip]);
14859 np[idx]++;
14860 for (int iq = 0; iq < ctl->nq; iq++) {
14861 mean[iq][idx] += kernel * atm->q[iq][ip];
14862 sigma[iq][idx] += SQR(kernel * atm->q[iq][ip]);
14863 }
14864 }
14865
14866 /* Calculate column density and volume mixing ratio... */
14867#pragma omp parallel for default(shared)
14868 for (int ix = 0; ix < ctl->grid_nx; ix++)
14869 for (int iy = 0; iy < ctl->grid_ny; iy++)
14870 for (int iz = 0; iz < ctl->grid_nz; iz++) {
14871
14872 /* Get grid index... */
14873 const int idx = ARRAY_3D(ix, iy, ctl->grid_ny, iz, ctl->grid_nz);
14874
14875 /* Calculate column density... */
14876 cd[idx] = NAN;
14877 if (ctl->qnt_m >= 0)
14878 cd[idx] = mean[ctl->qnt_m][idx] / (1e6 * area[iy]);
14879
14880 /* Calculate volume mixing ratio (implicit)... */
14881 vmr_impl[idx] = NAN;
14882 if (ctl->qnt_m >= 0 && ctl->molmass > 0 && met0 != NULL
14883 && met1 != NULL) {
14884 vmr_impl[idx] = 0;
14885 if (mean[ctl->qnt_m][idx] > 0) {
14886
14887 /* Get temperature... */
14888 double temp;
14890 intpol_met_time_3d(met0, met0->t, met1, met1->t, t, press[iz],
14891 lon[ix], lat[iy], &temp, ci, cw, 1);
14892
14893 /* Calculate volume mixing ratio... */
14894 vmr_impl[idx] =
14895 MA / ctl->molmass * cd[idx] / (RHO(press[iz], temp) * dz * 1e3);
14896 }
14897 }
14898
14899 /* Calculate mean... */
14900 if (np[idx] > 0)
14901 for (int iq = 0; iq < ctl->nq; iq++) {
14902 mean[iq][idx] /= np[idx];
14903 const double var = sigma[iq][idx] / np[idx] - SQR(mean[iq][idx]);
14904 sigma[iq][idx] = (var > 0 ? sqrt(var) : 0);
14905 } else
14906 for (int iq = 0; iq < ctl->nq; iq++) {
14907 mean[iq][idx] = NAN;
14908 sigma[iq][idx] = NAN;
14909 }
14910 }
14911
14912 /* Write ASCII data... */
14913 if (ctl->grid_type == 0)
14914 write_grid_asc(filename, ctl, cd, mean, sigma, vmr_impl,
14915 t, z, lon, lat, area, dz, np);
14916
14917 /* Write netCDF data... */
14918 else if (ctl->grid_type == 1)
14919 write_grid_nc(filename, ctl, cd, mean, sigma, vmr_impl,
14920 t, z, lon, lat, area, dz, np);
14921
14922 /* Error message... */
14923 else
14924 ERRMSG("Grid data format GRID_TYPE unknown!");
14925
14926 /* Free... */
14927 free(cd);
14928 for (int iq = 0; iq < ctl->nq; iq++) {
14929 free(mean[iq]);
14930 free(sigma[iq]);
14931 }
14932 free(vmr_impl);
14933 free(z);
14934 free(lon);
14935 free(lat);
14936 free(area);
14937 free(press);
14938 free(np);
14939 free(ixs);
14940 free(iys);
14941 free(izs);
14942}
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:14946
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:15050
Here is the call graph for this function:

◆ write_grid_asc()

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.

The write_grid_asc function writes gridded air parcel data, including column density, mean and standard deviation for specified quantities, and volume mixing ratio (if available), to an ASCII file. The function also supports writing gnuplot commands to generate plots if requested in the control parameters.

Parameters
filenameA string representing the name of the output file.
ctlA pointer to a ctl_t structure containing control parameters.
cdAn array of doubles representing column density values.
meanAn array of arrays of doubles representing the mean values for specified quantities.
sigmaAn array of arrays of doubles representing the standard deviation values for specified quantities.
vmr_implAn array of doubles representing the volume mixing ratio (implicit) values.
tA double representing the current time.
zAn array of doubles representing vertical coordinates (altitude).
lonAn array of doubles representing longitudinal coordinates.
latAn array of doubles representing latitudinal coordinates.
areaAn array of doubles representing surface area values.
dzA double representing the layer depth.
npAn array of integers representing the number of particles.

The function performs the following steps:

  • Checks if gnuplot output is requested in the control parameters and sets up a gnuplot pipe if needed.
  • If gnuplot output is requested, sets the plot filename and time string, and dumps gnuplot file contents to the pipe.
  • Otherwise, creates the output file for writing in ASCII format.
  • Writes the header information to the output file, including column labels.
  • Writes the grid data to the output file, including time, altitude, coordinates, surface area, layer depth, column density, volume mixing ratio, number of particles, mean values for specified quantities, and standard deviation values if requested.
  • Closes the output file.
Note
The function supports writing gnuplot commands to generate plots if requested in the control parameters. It also supports writing mean and standard deviation values for specified quantities if requested.
Author
Lars Hoffmann

Definition at line 14946 of file mptrac.c.

14959 {
14960
14961 FILE *out;
14962
14963 /* Check if gnuplot output is requested... */
14964 if (ctl->grid_gpfile[0] != '-') {
14965
14966 /* Create gnuplot pipe... */
14967 if (!(out = popen("gnuplot", "w")))
14968 ERRMSG("Cannot create pipe to gnuplot!");
14969
14970 /* Set plot filename... */
14971 fprintf(out, "set out \"%s.png\"\n", filename);
14972
14973 /* Set time string... */
14974 double r;
14975 int year, mon, day, hour, min, sec;
14976 jsec2time(t, &year, &mon, &day, &hour, &min, &sec, &r);
14977 fprintf(out, "timestr=\"%d-%02d-%02d, %02d:%02d UTC\"\n",
14978 year, mon, day, hour, min);
14979
14980 /* Dump gnuplot file to pipe... */
14981 FILE *in;
14982 char line[LEN];
14983 if (!(in = fopen(ctl->grid_gpfile, "r")))
14984 ERRMSG("Cannot open file!");
14985 while (fgets(line, LEN, in))
14986 fprintf(out, "%s", line);
14987 fclose(in);
14988 }
14989
14990 else {
14991
14992 /* Create file... */
14993 if (!(out = fopen(filename, "w")))
14994 ERRMSG("Cannot create file!");
14995 }
14996
14997 /* Write header... */
14998 fprintf(out,
14999 "# $1 = time [s]\n"
15000 "# $2 = altitude [km]\n"
15001 "# $3 = longitude [deg]\n"
15002 "# $4 = latitude [deg]\n"
15003 "# $5 = surface area [km^2]\n"
15004 "# $6 = layer depth [km]\n"
15005 "# $7 = column density (implicit) [kg/m^2]\n"
15006 "# $8 = volume mixing ratio (implicit) [ppv]\n"
15007 "# $9 = number of particles [1]\n");
15008 for (int iq = 0; iq < ctl->nq; iq++)
15009 fprintf(out, "# $%i = %s (mean) [%s]\n", 10 + iq, ctl->qnt_name[iq],
15010 ctl->qnt_unit[iq]);
15011 if (ctl->grid_stddev)
15012 for (int iq = 0; iq < ctl->nq; iq++)
15013 fprintf(out, "# $%i = %s (stddev) [%s]\n", 10 + ctl->nq + iq,
15014 ctl->qnt_name[iq], ctl->qnt_unit[iq]);
15015 fprintf(out, "\n");
15016
15017 /* Write data... */
15018 for (int ix = 0; ix < ctl->grid_nx; ix++) {
15019 if (ix > 0 && ctl->grid_ny > 1 && !ctl->grid_sparse)
15020 fprintf(out, "\n");
15021 for (int iy = 0; iy < ctl->grid_ny; iy++) {
15022 if (iy > 0 && ctl->grid_nz > 1 && !ctl->grid_sparse)
15023 fprintf(out, "\n");
15024 for (int iz = 0; iz < ctl->grid_nz; iz++) {
15025 int idx = ARRAY_3D(ix, iy, ctl->grid_ny, iz, ctl->grid_nz);
15026 if (!ctl->grid_sparse || vmr_impl[idx] > 0) {
15027 fprintf(out, "%.2f %g %g %g %g %g %g %g %d", t, z[iz], lon[ix],
15028 lat[iy], area[iy], dz, cd[idx], vmr_impl[idx], np[idx]);
15029 for (int iq = 0; iq < ctl->nq; iq++) {
15030 fprintf(out, " ");
15031 fprintf(out, ctl->qnt_format[iq], mean[iq][idx]);
15032 }
15033 if (ctl->grid_stddev)
15034 for (int iq = 0; iq < ctl->nq; iq++) {
15035 fprintf(out, " ");
15036 fprintf(out, ctl->qnt_format[iq], sigma[iq][idx]);
15037 }
15038 fprintf(out, "\n");
15039 }
15040 }
15041 }
15042 }
15043
15044 /* Close file... */
15045 fclose(out);
15046}
Here is the call graph for this function:

◆ write_grid_nc()

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.

The write_grid_nc function writes gridded air parcel data, including column density, mean and standard deviation for specified quantities, and volume mixing ratio (if available), to a NetCDF file. NetCDF is a self-describing, machine-independent data format for storing scientific data.

Parameters
filenameA string representing the name of the output file.
ctlA pointer to a ctl_t structure containing control parameters.
cdAn array of doubles representing column density values.
meanAn array of arrays of doubles representing the mean values for specified quantities.
sigmaAn array of arrays of doubles representing the standard deviation values for specified quantities.
vmr_implAn array of doubles representing the volume mixing ratio (implicit) values.
tA double representing the current time.
zAn array of doubles representing vertical coordinates (altitude).
lonAn array of doubles representing longitudinal coordinates.
latAn array of doubles representing latitudinal coordinates.
areaAn array of doubles representing surface area values.
dzA double representing the layer depth.
npAn array of integers representing the number of particles.

The function performs the following steps:

  • Allocates memory for temporary arrays required for writing data.
  • Creates a NetCDF file with the specified filename.
  • Defines dimensions and variables in the NetCDF file, along with their attributes.
  • Writes the data arrays to the NetCDF file.
  • Closes the NetCDF file.
  • Frees allocated memory.
Note
NetCDF files are commonly used in scientific computing and can be accessed by various programming languages and software packages. Additionally, the function supports writing mean and standard deviation values for specified quantities if requested.
Author
Lars Hoffmann

Definition at line 15050 of file mptrac.c.

15063 {
15064
15065 char longname[2 * LEN], varname[2 * LEN];
15066
15067 double *help;
15068
15069 int *help2, ncid, dimid[10], varid;
15070
15071 size_t start[2], count[2];
15072
15073 /* Allocate... */
15074 ALLOC(help, double,
15075 ctl->grid_nx * ctl->grid_ny * ctl->grid_nz);
15076 ALLOC(help2, int,
15077 ctl->grid_nx * ctl->grid_ny * ctl->grid_nz);
15078
15079 /* Create file... */
15080 NC(nc_create(filename, NC_NETCDF4, &ncid));
15081
15082 /* Define dimensions... */
15083 NC(nc_def_dim(ncid, "time", 1, &dimid[0]));
15084 NC(nc_def_dim(ncid, "z", (size_t) ctl->grid_nz, &dimid[1]));
15085 NC(nc_def_dim(ncid, "lat", (size_t) ctl->grid_ny, &dimid[2]));
15086 NC(nc_def_dim(ncid, "lon", (size_t) ctl->grid_nx, &dimid[3]));
15087 NC(nc_def_dim(ncid, "dz", 1, &dimid[4]));
15088
15089 /* Define variables and their attributes... */
15090 NC_DEF_VAR("time", NC_DOUBLE, 1, &dimid[0], "time",
15091 "seconds since 2000-01-01 00:00:00 UTC", 0, 0);
15092 NC_DEF_VAR("z", NC_DOUBLE, 1, &dimid[1], "altitude", "km", 0, 0);
15093 NC_DEF_VAR("lat", NC_DOUBLE, 1, &dimid[2], "latitude", "degrees_north", 0,
15094 0);
15095 NC_DEF_VAR("lon", NC_DOUBLE, 1, &dimid[3], "longitude", "degrees_east", 0,
15096 0);
15097 NC_DEF_VAR("dz", NC_DOUBLE, 1, &dimid[1], "layer depth", "km", 0, 0);
15098 NC_DEF_VAR("area", NC_DOUBLE, 1, &dimid[2], "surface area", "km**2", 0, 0);
15099
15100 NC_DEF_VAR("cd", NC_FLOAT, 4, dimid, "column density", "kg m**-2",
15101 ctl->grid_nc_level, 0);
15102 NC_DEF_VAR("vmr_impl", NC_FLOAT, 4, dimid,
15103 "volume mixing ratio (implicit)", "ppv", ctl->grid_nc_level, 0);
15104 NC_DEF_VAR("np", NC_INT, 4, dimid, "number of particles", "1", 0, 0);
15105 for (int iq = 0; iq < ctl->nq; iq++) {
15106 sprintf(varname, "%s_mean", ctl->qnt_name[iq]);
15107 sprintf(longname, "%s (mean)", ctl->qnt_longname[iq]);
15108 NC_DEF_VAR(varname, NC_DOUBLE, 4, dimid, longname, ctl->qnt_unit[iq],
15109 ctl->grid_nc_level, ctl->grid_nc_quant[iq]);
15110 if (ctl->grid_stddev) {
15111 sprintf(varname, "%s_stddev", ctl->qnt_name[iq]);
15112 sprintf(longname, "%s (stddev)", ctl->qnt_longname[iq]);
15113 NC_DEF_VAR(varname, NC_DOUBLE, 4, dimid, longname, ctl->qnt_unit[iq],
15114 ctl->grid_nc_level, ctl->grid_nc_quant[iq]);
15115 }
15116 }
15117 /* End definitions... */
15118 NC(nc_enddef(ncid));
15119
15120 /* Write data... */
15121 NC_PUT_DOUBLE("time", &t, 0);
15122 NC_PUT_DOUBLE("lon", lon, 0);
15123 NC_PUT_DOUBLE("lat", lat, 0);
15124 NC_PUT_DOUBLE("z", z, 0);
15125 NC_PUT_DOUBLE("area", area, 0);
15126 NC_PUT_DOUBLE("dz", &dz, 0);
15127
15128 for (int ix = 0; ix < ctl->grid_nx; ix++)
15129 for (int iy = 0; iy < ctl->grid_ny; iy++)
15130 for (int iz = 0; iz < ctl->grid_nz; iz++)
15131 help[ARRAY_3D(iz, iy, ctl->grid_ny, ix, ctl->grid_nx)] =
15132 cd[ARRAY_3D(ix, iy, ctl->grid_ny, iz, ctl->grid_nz)];
15133 NC_PUT_DOUBLE("cd", help, 0);
15134
15135 for (int ix = 0; ix < ctl->grid_nx; ix++)
15136 for (int iy = 0; iy < ctl->grid_ny; iy++)
15137 for (int iz = 0; iz < ctl->grid_nz; iz++)
15138 help[ARRAY_3D(iz, iy, ctl->grid_ny, ix, ctl->grid_nx)] =
15139 vmr_impl[ARRAY_3D(ix, iy, ctl->grid_ny, iz, ctl->grid_nz)];
15140 NC_PUT_DOUBLE("vmr_impl", help, 0);
15141
15142 for (int ix = 0; ix < ctl->grid_nx; ix++)
15143 for (int iy = 0; iy < ctl->grid_ny; iy++)
15144 for (int iz = 0; iz < ctl->grid_nz; iz++)
15145 help2[ARRAY_3D(iz, iy, ctl->grid_ny, ix, ctl->grid_nx)] =
15146 np[ARRAY_3D(ix, iy, ctl->grid_ny, iz, ctl->grid_nz)];
15147 NC_PUT_INT("np", help2, 0);
15148
15149 for (int iq = 0; iq < ctl->nq; iq++) {
15150 sprintf(varname, "%s_mean", ctl->qnt_name[iq]);
15151 for (int ix = 0; ix < ctl->grid_nx; ix++)
15152 for (int iy = 0; iy < ctl->grid_ny; iy++)
15153 for (int iz = 0; iz < ctl->grid_nz; iz++)
15154 help[ARRAY_3D(iz, iy, ctl->grid_ny, ix, ctl->grid_nx)] =
15155 mean[iq][ARRAY_3D(ix, iy, ctl->grid_ny, iz, ctl->grid_nz)];
15156 NC_PUT_DOUBLE(varname, help, 0);
15157 }
15158
15159 if (ctl->grid_stddev)
15160 for (int iq = 0; iq < ctl->nq; iq++) {
15161 sprintf(varname, "%s_stddev", ctl->qnt_name[iq]);
15162 for (int ix = 0; ix < ctl->grid_nx; ix++)
15163 for (int iy = 0; iy < ctl->grid_ny; iy++)
15164 for (int iz = 0; iz < ctl->grid_nz; iz++)
15165 help[ARRAY_3D(iz, iy, ctl->grid_ny, ix, ctl->grid_nx)] =
15166 sigma[iq][ARRAY_3D(ix, iy, ctl->grid_ny, iz, ctl->grid_nz)];
15167 NC_PUT_DOUBLE(varname, help, 0);
15168 }
15169
15170 /* Close file... */
15171 NC(nc_close(ncid));
15172
15173 /* Free... */
15174 free(help);
15175 free(help2);
15176}
#define NC_PUT_INT(varname, ptr, hyperslab)
Write integer data to a NetCDF variable.
Definition mptrac.h:1688

◆ write_met_bin()

void write_met_bin ( const char * filename,
const ctl_t * ctl,
met_t * met )

Writes meteorological data in binary format to a specified file.

This function writes meteorological data from the met_t structure to a binary file. The data includes grid and surface data, as well as multi-level data such as temperature, velocity components, and atmospheric properties. The compression options for multi-level data (ZFP) are controlled via the ctl_t structure. The function supports multiple variables, such as surface pressure, temperature, wind components, and cloud properties.

Parameters
filenameA constant character pointer representing the name of the file to write the binary data to.
ctlA pointer to a ctl_t structure, which holds control parameters including the type of meteorological data, compression settings, and grid dimensions.
metA pointer to a met_t structure that contains the meteorological data to be written to the binary file.
Note
  • The function creates a new file to write the data. If the file cannot be created, an error is generated.
  • The type of meteorological data (ctl->met_type) and the version of the binary format are written at the beginning of the file.
  • Grid data such as longitude, latitude, pressure levels, and time are written to the file.
  • Surface data (e.g., pressure, temperature, wind components) are written using the write_met_bin_2d helper function.
  • Multi-level (3D) data such as geopotential height, temperature, and wind velocity are written using the write_met_bin_3d function with optional ZFP compression settings.
Author
Lars Hoffmann

Definition at line 15180 of file mptrac.c.

15183 {
15184
15185 /* Create file... */
15186 FILE *out, *level_log = NULL;
15187 if (!(out = fopen(filename, "w")))
15188 ERRMSG("Cannot create file!");
15189
15190 /* Create diagnostics file... */
15191 if (strcmp(ctl->met_comp_logfile, "-") != 0) {
15192
15193 if (!(level_log = fopen(ctl->met_comp_logfile, "w")))
15194 ERRMSG("Cannot create compression log file!");
15195 LOG(1, "Write compression diagnostics: %s", ctl->met_comp_logfile);
15196
15197 /* Write header... */
15198 fprintf(level_log,
15199 "# $1 = compression codec name [-]\n"
15200 "# $2 = variable name [-]\n"
15201 "# $3 = level index [-]\n"
15202 "# $4 = pressure level [hPa]\n"
15203 "# $5 = compression ratio [-]\n"
15204 "# $6 = bits per value [bit/value]\n"
15205 "# $7 = correlation coefficient [-]\n"
15206 "# $8 = mean compression error [-]\n"
15207 "# $9 = standard deviation of compression error [-]\n"
15208 "# $10 = minimum compression error [-]\n"
15209 "# $11 = maximum compression error [-]\n"
15210 "# $12 = mean value of original field [-]\n"
15211 "# $13 = value range of original field [-]\n"
15212 "# $14 = normalized root mean square error [-]\n"
15213 "# $15 = compression time [s]\n"
15214 "# $16 = compression speed [MiB/s]\n"
15215 "# $17 = decompression time [s]\n"
15216 "# $18 = decompression speed [MiB/s]\n\n");
15217 }
15218
15219 /* Write type of binary data... */
15220 FWRITE(&ctl->met_type, int,
15221 1,
15222 out);
15223
15224 /* Write version of binary data... */
15225 int version = 104;
15226 FWRITE(&version, int,
15227 1,
15228 out);
15229
15230 /* Write grid data... */
15231 FWRITE(&met->time, double,
15232 1,
15233 out);
15234 FWRITE(&met->nx, int,
15235 1,
15236 out);
15237 FWRITE(&met->ny, int,
15238 1,
15239 out);
15240 FWRITE(&met->np, int,
15241 1,
15242 out);
15243 FWRITE(met->lon, double,
15244 (size_t) met->nx,
15245 out);
15246 FWRITE(met->lat, double,
15247 (size_t) met->ny,
15248 out);
15249 FWRITE(met->p, double,
15250 (size_t) met->np,
15251 out);
15252
15253 /* Write surface data... */
15254 write_met_bin_2d(out, met, met->ps, "PS");
15255 write_met_bin_2d(out, met, met->ts, "TS");
15256 write_met_bin_2d(out, met, met->zs, "ZS");
15257 write_met_bin_2d(out, met, met->us, "US");
15258 write_met_bin_2d(out, met, met->vs, "VS");
15259 write_met_bin_2d(out, met, met->ess, "ESS");
15260 write_met_bin_2d(out, met, met->nss, "NSS");
15261 write_met_bin_2d(out, met, met->shf, "SHF");
15262 write_met_bin_2d(out, met, met->lsm, "LSM");
15263 write_met_bin_2d(out, met, met->sst, "SST");
15264 write_met_bin_2d(out, met, met->pbl, "PBL");
15265 write_met_bin_2d(out, met, met->pt, "PT");
15266 write_met_bin_2d(out, met, met->tt, "TT");
15267 write_met_bin_2d(out, met, met->zt, "ZT");
15268 write_met_bin_2d(out, met, met->h2ot, "H2OT");
15269 write_met_bin_2d(out, met, met->pct, "PCT");
15270 write_met_bin_2d(out, met, met->pcb, "PCB");
15271 write_met_bin_2d(out, met, met->cl, "CL");
15272 write_met_bin_2d(out, met, met->plcl, "PLCL");
15273 write_met_bin_2d(out, met, met->plfc, "PLFC");
15274 write_met_bin_2d(out, met, met->pel, "PEL");
15275 write_met_bin_2d(out, met, met->cape, "CAPE");
15276 write_met_bin_2d(out, met, met->cin, "CIN");
15277 write_met_bin_2d(out, met, met->o3c, "O3C");
15278
15279 /* Write level data... */
15280 write_met_bin_3d(out, ctl, met, met->z, "Z", 0, level_log);
15281 write_met_bin_3d(out, ctl, met, met->t, "T", 1, level_log);
15282 write_met_bin_3d(out, ctl, met, met->u, "U", 2, level_log);
15283 write_met_bin_3d(out, ctl, met, met->v, "V", 3, level_log);
15284 write_met_bin_3d(out, ctl, met, met->w, "W", 4, level_log);
15285 write_met_bin_3d(out, ctl, met, met->pv, "PV", 5, level_log);
15286 write_met_bin_3d(out, ctl, met, met->h2o, "H2O", 6, level_log);
15287 write_met_bin_3d(out, ctl, met, met->o3, "O3", 7, level_log);
15288 write_met_bin_3d(out, ctl, met, met->lwc, "LWC", 8, level_log);
15289 write_met_bin_3d(out, ctl, met, met->rwc, "RWC", 9, level_log);
15290 write_met_bin_3d(out, ctl, met, met->iwc, "IWC", 10, level_log);
15291 write_met_bin_3d(out, ctl, met, met->swc, "SWC", 11, level_log);
15292 write_met_bin_3d(out, ctl, met, met->cc, "CC", 12, level_log);
15293 if (METVAR != 13)
15294 ERRMSG("Number of meteo variables doesn't match!");
15295
15296 /* Write final flag... */
15297 int final = 999;
15298 FWRITE(&final, int,
15299 1,
15300 out);
15301
15302 /* Close file... */
15303 if (level_log)
15304 fclose(level_log);
15305 fclose(out);
15306}
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:15339
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:15310
Here is the call graph for this function:

◆ write_met_bin_2d()

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.

The write_met_bin_2d function writes a 2-dimensional meteorological variable to a binary file specified by the out parameter. The variable data is provided in a 2-dimensional array var with maximum dimensions EX by EY. The variable name is provided as a string in the varname parameter.

Parameters
outA pointer to a FILE structure representing the output file.
metA pointer to a met_t structure containing meteorological data.
varAn array of floats representing the 2-dimensional variable data.
varnameA string containing the name of the variable being written.

The function performs the following steps:

  • Allocates memory for a temporary buffer to hold the variable data.
  • Copies the variable data from the 2-dimensional array var to the temporary buffer help.
  • Writes the uncompressed variable data to the binary file specified by out.
  • Logs a message indicating the successful writing of the variable data.
  • Frees the allocated memory.
Note
This function is typically used to write surface data or other 2-dimensional meteorological variables to a binary file.
Author
Lars Hoffmann

Definition at line 15310 of file mptrac.c.

15314 {
15315
15316 float *help;
15317
15318 /* Allocate... */
15319 ALLOC(help, float,
15320 EX * EY);
15321
15322 /* Copy data... */
15323 for (int ix = 0; ix < met->nx; ix++)
15324 for (int iy = 0; iy < met->ny; iy++)
15325 help[ARRAY_2D(ix, iy, met->ny)] = var[ix][iy];
15326
15327 /* Write uncompressed data... */
15328 LOG(2, "Write 2-D variable: %s (uncompressed)", varname);
15329 FWRITE(help, float,
15330 (size_t) (met->nx * met->ny),
15331 out);
15332
15333 /* Free... */
15334 free(help);
15335}

◆ write_met_bin_3d()

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.

The write_met_bin_3d function writes a 3-dimensional meteorological variable to a binary file specified by the out parameter. The variable data is provided in a 3-dimensional array var with maximum dimensions EX by EY by EP. The variable name is provided as a string in the varname parameter. Additionally, the function takes parameters for specifying the compression precision and tolerance.

Parameters
outA pointer to a FILE structure representing the output file.
ctlA pointer to a ctl_t structure containing control parameters.
metA pointer to a met_t structure containing meteorological data.
varAn array of floats representing the 3-dimensional variable data.
varnameA string containing the name of the variable being written.
metvarIndex of the meteorological variable, used to select codec-specific compression settings.
level_logOptional file stream for per-level compression diagnostics, or NULL to disable logging.

The function performs the following steps:

  • Allocates memory for a temporary buffer to hold the variable data.
  • Copies the variable data from the 3-dimensional array var to the temporary buffer help.
  • Writes the variable data to the binary file specified by out using the specified compression method (uncompressed, packed, ZFP, ZSTD, cmultiscale).
  • Logs a message indicating the successful writing of the variable data.
  • Frees the allocated memory.
Note
This function is typically used to write level data or other 3-dimensional meteorological variables to a binary file.
Depending on the value of ctl->met_type, the function writes the variable data using different compression methods. If ctl->met_type is not supported, an error message is logged.
Author
Lars Hoffmann

Definition at line 15339 of file mptrac.c.

15346 {
15347
15348 float *help;
15349
15350 /* Allocate... */
15351 ALLOC(help, float,
15352 EX * EY * EP);
15353
15354 /* Copy data... */
15355#pragma omp parallel for default(shared) collapse(2)
15356 for (int ix = 0; ix < met->nx; ix++)
15357 for (int iy = 0; iy < met->ny; iy++)
15358 for (int ip = 0; ip < met->np; ip++)
15359 help[ARRAY_3D(ix, iy, met->ny, ip, met->np)] = var[ix][iy][ip];
15360
15361 /* Write uncompressed data... */
15362 if (ctl->met_type == 1) {
15363 LOG(2, "Write 3-D variable: %s (uncompressed)", varname);
15364 FWRITE(help, float,
15365 (size_t) (met->nx * met->ny * met->np),
15366 out);
15367 }
15368
15369 /* Write packed data... */
15370 else if (ctl->met_type == 2)
15371 compress_pck(ctl, met, varname, help, 0, level_log, out);
15372
15373 /* Write ZFP data... */
15374#ifdef ZFP
15375 else if (ctl->met_type == 3) {
15376 FWRITE(&ctl->met_zfp_prec[metvar], int,
15377 1,
15378 out);
15379 FWRITE(&ctl->met_zfp_tol[metvar], double,
15380 1,
15381 out);
15382 compress_zfp(ctl, met, varname, help, 0, level_log, out);
15383 }
15384#endif
15385
15386 /* Write zstd data... */
15387#ifdef ZSTD
15388 else if (ctl->met_type == 4)
15389 compress_zstd(ctl, met, varname, help, 0, level_log, out);
15390#endif
15391
15392 /* Write LZ4 data... */
15393#ifdef LZ4
15394 else if (ctl->met_type == 8)
15395 compress_lz4(ctl, met, varname, help, 0, level_log, out);
15396#endif
15397
15398 /* Write cmultiscale data... */
15399#ifdef CMS
15400 else if (ctl->met_type == 5) {
15401 compress_cms(ctl, met, varname, help, 0, level_log, out);
15402 }
15403#endif
15404
15405 /* Write SZ3 data... */
15406#ifdef SZ3
15407 else if (ctl->met_type == 7) {
15408 FWRITE(&ctl->met_sz3_prec[metvar], int,
15409 1,
15410 out);
15411 FWRITE(&ctl->met_sz3_tol[metvar], double,
15412 1,
15413 out);
15414 compress_sz3(ctl, met, varname, help, 0, level_log, out);
15415 }
15416#endif
15417
15418 /* Unknown method... */
15419 else {
15420 ERRMSG("MET_TYPE not supported!");
15421
15422 /* This will never execute, hack to avoid compilation error... */
15423 LOG(3, "%d", metvar);
15424 }
15425
15426 /* Free... */
15427 free(help);
15428}
Here is the call graph for this function:

◆ write_met_nc()

void write_met_nc ( const char * filename,
const ctl_t * ctl,
met_t * met )

Writes meteorological data to a NetCDF file.

This function creates and writes meteorological data to a NetCDF file in the NetCDF-4 format. It defines the required dimensions, grid, surface variables, and level data within the NetCDF structure and writes the corresponding values from the met_t structure. The function uses helper functions to write 2D surface and 3D level data.

Parameters
filenameA constant character pointer representing the name of the NetCDF file to create and write the data to.
ctlA pointer to a ctl_t structure that contains control parameters, such as the NetCDF level and quantization settings.
metA pointer to a met_t structure that contains the meteorological data to be written to the NetCDF file.
Note
  • The function uses the NetCDF-4 format for efficient data storage.
  • It defines the grid dimensions (time, longitude, latitude, pressure levels) and adds global attributes like units and descriptions for each variable.
  • The surface variables include surface pressure, geopotential, 2-meter temperature, and wind components, which are defined on a 2D grid (latitude × longitude).
  • The level variables, such as temperature, wind velocities, and cloud properties, are defined on a 3D grid (pressure level × latitude × longitude).
Author
Lars Hoffmann

Definition at line 15432 of file mptrac.c.

15435 {
15436
15437 /* Create file... */
15438 int ncid, varid;
15439 size_t start[4], count[4];
15440 NC(nc_create(filename, NC_NETCDF4, &ncid));
15441
15442 /* Define dimensions... */
15443 int tid, lonid, latid, levid;
15444 NC(nc_def_dim(ncid, "time", 1, &tid));
15445
15446 if (met->coord_type == 0) {
15447 NC(nc_def_dim(ncid, "lon", (size_t) met->nx, &lonid));
15448 NC(nc_def_dim(ncid, "lat", (size_t) met->ny, &latid));
15449 NC_DEF_VAR("lon", NC_DOUBLE, 1, &lonid, "longitude", "degrees_east", 0,
15450 0);
15451 NC_DEF_VAR("lat", NC_DOUBLE, 1, &latid, "latitude", "degrees_north", 0,
15452 0);
15453 } else {
15454 NC(nc_def_dim(ncid, "x", (size_t) met->nx, &lonid));
15455 NC(nc_def_dim(ncid, "y", (size_t) met->ny, &latid));
15456 NC_DEF_VAR("x", NC_DOUBLE, 1, &lonid, "x", "easting", 0, 0);
15457 NC_DEF_VAR("y", NC_DOUBLE, 1, &latid, "y", "northing", 0, 0);
15458 }
15459
15460 NC(nc_def_dim(ncid, "lev", (size_t) met->np, &levid));
15461
15462 /* Define grid... */
15463 NC_DEF_VAR("time", NC_DOUBLE, 1, &tid, "time",
15464 "seconds since 2000-01-01 00:00:00 UTC", 0, 0);
15465 NC_DEF_VAR("lev", NC_DOUBLE, 1, &levid, "pressure", "Pa", 0, 0);
15466
15467 /* Define surface variables... */
15468 int dimid2[3] = { tid, latid, lonid };
15469 NC_DEF_VAR("sp", NC_FLOAT, 3, dimid2, "Surface pressure", "Pa",
15470 ctl->met_nc_level, 0);
15471 NC_DEF_VAR("z", NC_FLOAT, 3, dimid2, "Geopotential", "m**2 s**-2",
15472 ctl->met_nc_level, 0);
15473 NC_DEF_VAR("t2m", NC_FLOAT, 3, dimid2, "2 metre temperature", "K",
15474 ctl->met_nc_level, 0);
15475 NC_DEF_VAR("u10m", NC_FLOAT, 3, dimid2, "10 metre U wind component",
15476 "m s**-1", ctl->met_nc_level, 0);
15477 NC_DEF_VAR("v10m", NC_FLOAT, 3, dimid2, "10 metre V wind component",
15478 "m s**-1", ctl->met_nc_level, 0);
15479 NC_DEF_VAR("iews", NC_FLOAT, 3, dimid2,
15480 "Instantaneous eastward turbulent surface stress", "N m**-2",
15481 ctl->met_nc_level, 0);
15482 NC_DEF_VAR("inss", NC_FLOAT, 3, dimid2,
15483 "Instantaneous northward turbulent surface stress", "N m**-2",
15484 ctl->met_nc_level, 0);
15485 NC_DEF_VAR("ishf", NC_FLOAT, 3, dimid2,
15486 "Instantaneous surface sensible heat flux", "W m**-2",
15487 ctl->met_nc_level, 0);
15488 NC_DEF_VAR("lsm", NC_FLOAT, 3, dimid2, "Land/sea mask", "-",
15489 ctl->met_nc_level, 0);
15490 NC_DEF_VAR("sstk", NC_FLOAT, 3, dimid2, "Sea surface temperature", "K",
15491 ctl->met_nc_level, 0);
15492 NC_DEF_VAR("blp", NC_FLOAT, 3, dimid2, "Boundary layer pressure", "Pa",
15493 ctl->met_nc_level, 0);
15494 NC_DEF_VAR("pt", NC_FLOAT, 3, dimid2, "Tropopause pressure", "Pa",
15495 ctl->met_nc_level, 0);
15496 NC_DEF_VAR("tt", NC_FLOAT, 3, dimid2, "Tropopause temperature", "K",
15497 ctl->met_nc_level, 0);
15498 NC_DEF_VAR("zt", NC_FLOAT, 3, dimid2, "Tropopause height", "m",
15499 ctl->met_nc_level, 0);
15500 NC_DEF_VAR("h2ot", NC_FLOAT, 3, dimid2, "Tropopause water vapor", "ppv",
15501 ctl->met_nc_level, 0);
15502 NC_DEF_VAR("pct", NC_FLOAT, 3, dimid2, "Cloud top pressure", "Pa",
15503 ctl->met_nc_level, 0);
15504 NC_DEF_VAR("pcb", NC_FLOAT, 3, dimid2, "Cloud bottom pressure", "Pa",
15505 ctl->met_nc_level, 0);
15506 NC_DEF_VAR("cl", NC_FLOAT, 3, dimid2, "Total column cloud water",
15507 "kg m**2", ctl->met_nc_level, 0);
15508 NC_DEF_VAR("plcl", NC_FLOAT, 3, dimid2,
15509 "Pressure at lifted condensation level (LCL)", "Pa",
15510 ctl->met_nc_level, 0);
15511 NC_DEF_VAR("plfc", NC_FLOAT, 3, dimid2,
15512 "Pressure at level of free convection (LFC)", "Pa",
15513 ctl->met_nc_level, 0);
15514 NC_DEF_VAR("pel", NC_FLOAT, 3, dimid2,
15515 "Pressure at equilibrium level (EL)", "Pa", ctl->met_nc_level,
15516 0);
15517 NC_DEF_VAR("cape", NC_FLOAT, 3, dimid2,
15518 "Convective available potential energy", "J kg**-1",
15519 ctl->met_nc_level, 0);
15520 NC_DEF_VAR("cin", NC_FLOAT, 3, dimid2, "Convective inhibition",
15521 "J kg**-1", ctl->met_nc_level, 0);
15522 NC_DEF_VAR("o3c", NC_FLOAT, 3, dimid2, "Total column ozone", "DU",
15523 ctl->met_nc_level, 0);
15524
15525 /* Define level data... */
15526 int dimid3[4] = { tid, levid, latid, lonid };
15527 NC_DEF_VAR("t", NC_FLOAT, 4, dimid3, "Temperature", "K",
15528 ctl->met_nc_level, ctl->met_nc_quant);
15529 NC_DEF_VAR("u", NC_FLOAT, 4, dimid3, "U velocity", "m s**-1",
15530 ctl->met_nc_level, ctl->met_nc_quant);
15531 NC_DEF_VAR("v", NC_FLOAT, 4, dimid3, "V velocity", "m s**-1",
15532 ctl->met_nc_level, ctl->met_nc_quant);
15533 NC_DEF_VAR("w", NC_FLOAT, 4, dimid3, "Vertical velocity", "Pa s**-1",
15534 ctl->met_nc_level, ctl->met_nc_quant);
15535 NC_DEF_VAR("q", NC_FLOAT, 4, dimid3, "Specific humidity", "kg kg**-1",
15536 ctl->met_nc_level, ctl->met_nc_quant);
15537 NC_DEF_VAR("o3", NC_FLOAT, 4, dimid3, "Ozone mass mixing ratio",
15538 "kg kg**-1", ctl->met_nc_level, ctl->met_nc_quant);
15539 NC_DEF_VAR("clwc", NC_FLOAT, 4, dimid3, "Cloud liquid water content",
15540 "kg kg**-1", ctl->met_nc_level, ctl->met_nc_quant);
15541 NC_DEF_VAR("crwc", NC_FLOAT, 4, dimid3, "Cloud rain water content",
15542 "kg kg**-1", ctl->met_nc_level, ctl->met_nc_quant);
15543 NC_DEF_VAR("ciwc", NC_FLOAT, 4, dimid3, "Cloud ice water content",
15544 "kg kg**-1", ctl->met_nc_level, ctl->met_nc_quant);
15545 NC_DEF_VAR("cswc", NC_FLOAT, 4, dimid3, "Cloud snow water content",
15546 "kg kg**-1", ctl->met_nc_level, ctl->met_nc_quant);
15547 NC_DEF_VAR("cc", NC_FLOAT, 4, dimid3, "Cloud cover", "-",
15548 ctl->met_nc_level, ctl->met_nc_quant);
15549
15550 /* End definitions... */
15551 NC(nc_enddef(ncid));
15552
15553 /* Write grid data... */
15554 NC_PUT_DOUBLE("time", &met->time, 0);
15555
15556 if (met->coord_type == 0) {
15557 NC_PUT_DOUBLE("lon", met->lon, 0);
15558 NC_PUT_DOUBLE("lat", met->lat, 0);
15559 } else {
15560 NC_PUT_DOUBLE("x", met->lon, 0);
15561 NC_PUT_DOUBLE("y", met->lat, 0);
15562 }
15563
15564 double phelp[EP];
15565 for (int ip = 0; ip < met->np; ip++)
15566 phelp[ip] = 100. * met->p[ip];
15567 NC_PUT_DOUBLE("lev", phelp, 0);
15568
15569 /* Write surface data... */
15570 write_met_nc_2d(ncid, "sp", met, met->ps, 100.0f);
15571 write_met_nc_2d(ncid, "z", met, met->zs, (float) (1000. * G0));
15572 write_met_nc_2d(ncid, "t2m", met, met->ts, 1.0f);
15573 write_met_nc_2d(ncid, "u10m", met, met->us, 1.0f);
15574 write_met_nc_2d(ncid, "v10m", met, met->vs, 1.0f);
15575 write_met_nc_2d(ncid, "iews", met, met->ess, 1.0f);
15576 write_met_nc_2d(ncid, "inss", met, met->nss, 1.0f);
15577 write_met_nc_2d(ncid, "ishf", met, met->shf, 1.0f);
15578 write_met_nc_2d(ncid, "lsm", met, met->lsm, 1.0f);
15579 write_met_nc_2d(ncid, "sstk", met, met->sst, 1.0f);
15580 write_met_nc_2d(ncid, "blp", met, met->pbl, 100.0f);
15581 write_met_nc_2d(ncid, "pt", met, met->pt, 100.0f);
15582 write_met_nc_2d(ncid, "tt", met, met->tt, 1.0f);
15583 write_met_nc_2d(ncid, "zt", met, met->zt, 1000.0f);
15584 write_met_nc_2d(ncid, "h2ot", met, met->h2ot, 1.0f);
15585 write_met_nc_2d(ncid, "pct", met, met->pct, 100.0f);
15586 write_met_nc_2d(ncid, "pcb", met, met->pcb, 100.0f);
15587 write_met_nc_2d(ncid, "cl", met, met->cl, 1.0f);
15588 write_met_nc_2d(ncid, "plcl", met, met->plcl, 100.0f);
15589 write_met_nc_2d(ncid, "plfc", met, met->plfc, 100.0f);
15590 write_met_nc_2d(ncid, "pel", met, met->pel, 100.0f);
15591 write_met_nc_2d(ncid, "cape", met, met->cape, 1.0f);
15592 write_met_nc_2d(ncid, "cin", met, met->cin, 1.0f);
15593 write_met_nc_2d(ncid, "o3c", met, met->o3c, 1.0f);
15594
15595 /* Write level data... */
15596 write_met_nc_3d(ncid, "t", met, met->t, 1.0f);
15597 write_met_nc_3d(ncid, "u", met, met->u, 1.0f);
15598 write_met_nc_3d(ncid, "v", met, met->v, 1.0f);
15599 write_met_nc_3d(ncid, "w", met, met->w, 100.0f);
15600 write_met_nc_3d(ncid, "q", met, met->h2o, (float) (MH2O / MA));
15601 write_met_nc_3d(ncid, "o3", met, met->o3, (float) (MO3 / MA));
15602 write_met_nc_3d(ncid, "clwc", met, met->lwc, 1.0f);
15603 write_met_nc_3d(ncid, "crwc", met, met->rwc, 1.0f);
15604 write_met_nc_3d(ncid, "ciwc", met, met->iwc, 1.0f);
15605 write_met_nc_3d(ncid, "cswc", met, met->swc, 1.0f);
15606 write_met_nc_3d(ncid, "cc", met, met->cc, 1.0f);
15607
15608 /* Close file... */
15609 NC(nc_close(ncid));
15610}
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:15614
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:15644
Here is the call graph for this function:

◆ write_met_nc_2d()

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.

This function writes a 2D meteorological variable, stored in the array var, to a NetCDF file with the specified variable name. The data is scaled by a factor scl before being written. The function handles memory allocation for the data copy, scaling, and freeing the allocated memory after writing the data to the NetCDF file.

Parameters
ncidThe NetCDF file ID. This is an integer that identifies the NetCDF file where the data will be written. It is assumed that this file has already been opened for writing.
varnameA pointer to a string containing the name of the variable in the NetCDF file where the data will be stored.
metA pointer to a structure of type met_t that contains metadata about the meteorological field, including the dimensions nx (number of points in x-direction) and ny (number of points in y-direction).
varA 2D array of dimensions EX x EY containing the meteorological data to be written. The data is provided in the format var[ix][iy], where ix is the index in the x-direction and iy is the index in the y-direction.
sclA scaling factor applied to each element in the var array before writing to the NetCDF file.
Author
Lars Hoffmann

Definition at line 15614 of file mptrac.c.

15619 {
15620
15621 int varid;
15622 size_t start[4], count[4];
15623
15624 /* Allocate... */
15625 float *help;
15626 ALLOC(help, float,
15627 EX * EY);
15628
15629 /* Copy data... */
15630 for (int ix = 0; ix < met->nx; ix++)
15631 for (int iy = 0; iy < met->ny; iy++)
15632 help[ARRAY_2D(iy, ix, met->nx)] = scl * var[ix][iy];
15633
15634 /* Write data... */
15635 LOG(2, "Write 2-D variable: %s (netCDF)", varname);
15636 NC_PUT_FLOAT(varname, help, 0);
15637
15638 /* Free... */
15639 free(help);
15640}
#define NC_PUT_FLOAT(varname, ptr, hyperslab)
Write a float array to a NetCDF file.
Definition mptrac.h:1665

◆ write_met_nc_3d()

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.

This function writes a 3D meteorological variable, stored in the array var, to a NetCDF file with the specified variable name. The data is scaled by a factor scl before being written. The function handles memory allocation for the data copy, scaling, and freeing the allocated memory after writing the data to the NetCDF file.

Parameters
ncidThe NetCDF file ID. This is an integer that identifies the NetCDF file where the data will be written. It is assumed that this file has already been opened for writing.
varnameA pointer to a string containing the name of the variable in the NetCDF file where the data will be stored.
metA pointer to a structure of type met_t that contains metadata about the meteorological field, including the dimensions nx (number of points in the x-direction), ny (number of points in the y-direction), and np (number of points in the third dimension, e.g., pressure levels).
varA 3D array of dimensions EX x EY x EP containing the meteorological data to be written. The data is provided in the format var[ix][iy][ip], where ix is the index in the x-direction, iy is the index in the y-direction, and ip is the index in the third dimension (e.g., vertical levels).
sclA scaling factor applied to each element in the var array before writing to the NetCDF file.
Author
Lars Hoffmann

Definition at line 15644 of file mptrac.c.

15649 {
15650
15651 int varid;
15652 size_t start[4], count[4];
15653
15654 /* Allocate... */
15655 float *help;
15656 ALLOC(help, float,
15657 EX * EY * EP);
15658
15659 /* Copy data... */
15660 for (int ix = 0; ix < met->nx; ix++)
15661 for (int iy = 0; iy < met->ny; iy++)
15662 for (int ip = 0; ip < met->np; ip++)
15663 help[ARRAY_3D(ip, iy, met->ny, ix, met->nx)] = scl * var[ix][iy][ip];
15664
15665 /* Write data... */
15666 LOG(2, "Write 3-D variable: %s (netCDF)", varname);
15667 NC_PUT_FLOAT(varname, help, 0);
15668
15669 /* Free... */
15670 free(help);
15671}

◆ write_prof()

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.

The write_prof function writes profile data to a file specified by the filename parameter. It takes control parameters (ctl), two meteorological data structures (met0 and met1), an atmospheric data structure (atm), and a time value (t) as input.

Parameters
filenameA string representing the filename where the profile data will be written.
ctlA pointer to a ctl_t structure containing control parameters.
met0A pointer to a met_t structure representing the first set of meteorological data.
met1A pointer to a met_t structure representing the second set of meteorological data.
atmA pointer to an atm_t structure representing atmospheric data.
tA double value representing the time at which the profile data is being written.

The function performs the following steps:

  • Initializes variables and allocates memory if it's the start of the simulation.
  • Reads observation data and creates a new output file if necessary.
  • Writes header information to the output file.
  • Sets grid box size and vertical coordinates.
  • Processes observations and model data within the specified time interval.
  • Calculates and writes output data for each grid cell.
  • Finalizes by closing the output file and freeing allocated memory if it's the end of the simulation.
Note
This function writes profile data to a file, including time, altitude, coordinates, atmospheric properties, observed data, and the number of observations.
Author
Lars Hoffmann

Definition at line 15675 of file mptrac.c.

15681 {
15682
15683 if (ctl->met_coord_type != 0)
15684 ERRMSG("Only lat/lon grid supported");
15685
15686 static FILE *out;
15687
15688 static double *mass, *obsmean, *rt, *rz, *rlon, *rlat, *robs, *area,
15689 dz, dlon, dlat, *lon, *lat, *z, *press, temp, vmr, h2o, o3;
15690
15691 static int nobs, *obscount, ip, okay;
15692
15693 /* Set timer... */
15694 SELECT_TIMER("WRITE_PROF", "OUTPUT");
15695
15696 /* Init... */
15697 if (t == ctl->t_start) {
15698
15699 /* Check quantity index for mass... */
15700 if (ctl->qnt_m < 0)
15701 ERRMSG("Need quantity mass!");
15702
15703 /* Check molar mass... */
15704 if (ctl->molmass <= 0)
15705 ERRMSG("Specify molar mass!");
15706
15707 /* Allocate... */
15708 ALLOC(lon, double,
15709 ctl->prof_nx);
15710 ALLOC(lat, double,
15711 ctl->prof_ny);
15712 ALLOC(area, double,
15713 ctl->prof_ny);
15714 ALLOC(z, double,
15715 ctl->prof_nz);
15716 ALLOC(press, double,
15717 ctl->prof_nz);
15718 ALLOC(rt, double,
15719 NOBS);
15720 ALLOC(rz, double,
15721 NOBS);
15722 ALLOC(rlon, double,
15723 NOBS);
15724 ALLOC(rlat, double,
15725 NOBS);
15726 ALLOC(robs, double,
15727 NOBS);
15728
15729 /* Read observation data... */
15730 read_obs(ctl->prof_obsfile, ctl, rt, rz, rlon, rlat, robs, &nobs);
15731
15732 /* Create new output file... */
15733 LOG(1, "Write profile data: %s", filename);
15734 if (!(out = fopen(filename, "w")))
15735 ERRMSG("Cannot create file!");
15736
15737 /* Write header... */
15738 fprintf(out,
15739 "# $1 = time [s]\n"
15740 "# $2 = altitude [km]\n"
15741 "# $3 = longitude [deg]\n"
15742 "# $4 = latitude [deg]\n"
15743 "# $5 = pressure [hPa]\n"
15744 "# $6 = temperature [K]\n"
15745 "# $7 = volume mixing ratio [ppv]\n"
15746 "# $8 = H2O volume mixing ratio [ppv]\n"
15747 "# $9 = O3 volume mixing ratio [ppv]\n"
15748 "# $10 = observed BT index [K]\n"
15749 "# $11 = number of observations\n");
15750
15751 /* Set grid box size... */
15752 dz = (ctl->prof_z1 - ctl->prof_z0) / ctl->prof_nz;
15753 dlon = (ctl->prof_lon1 - ctl->prof_lon0) / ctl->prof_nx;
15754 dlat = (ctl->prof_lat1 - ctl->prof_lat0) / ctl->prof_ny;
15755
15756 /* Set vertical coordinates... */
15757 for (int iz = 0; iz < ctl->prof_nz; iz++) {
15758 z[iz] = ctl->prof_z0 + dz * (iz + 0.5);
15759 press[iz] = P(z[iz]);
15760 }
15761
15762 /* Set horizontal coordinates... */
15763 for (int ix = 0; ix < ctl->prof_nx; ix++)
15764 lon[ix] = ctl->prof_lon0 + dlon * (ix + 0.5);
15765 for (int iy = 0; iy < ctl->prof_ny; iy++) {
15766 lat[iy] = ctl->prof_lat0 + dlat * (iy + 0.5);
15767 area[iy] = dlat * dlon * SQR(RE * M_PI / 180.) * cos(DEG2RAD(lat[iy]));
15768 }
15769 }
15770
15771 /* Set time interval... */
15772 const double t0 = t - 0.5 * ctl->dt_mod;
15773 const double t1 = t + 0.5 * ctl->dt_mod;
15774
15775 /* Allocate... */
15776 ALLOC(mass, double,
15777 ctl->prof_nx * ctl->prof_ny * ctl->prof_nz);
15778 ALLOC(obsmean, double,
15779 ctl->prof_nx * ctl->prof_ny);
15780 ALLOC(obscount, int,
15781 ctl->prof_nx * ctl->prof_ny);
15782
15783 /* Loop over observations... */
15784 for (int i = 0; i < nobs; i++) {
15785
15786 /* Check time... */
15787 if (rt[i] < t0)
15788 continue;
15789 else if (rt[i] >= t1)
15790 break;
15791
15792 /* Check observation data... */
15793 if (!isfinite(robs[i]))
15794 continue;
15795
15796 /* Check grid boundaries and calculate indices... */
15797 if (rlon[i] < ctl->prof_lon0 || rlon[i] >= ctl->prof_lon1
15798 || rlat[i] < ctl->prof_lat0 || rlat[i] >= ctl->prof_lat1)
15799 continue;
15800 const int ix = (int) ((rlon[i] - ctl->prof_lon0) / dlon);
15801 const int iy = (int) ((rlat[i] - ctl->prof_lat0) / dlat);
15802 if (ix >= ctl->prof_nx || iy >= ctl->prof_ny)
15803 continue;
15804
15805 /* Get mean observation index... */
15806 const int idx = ARRAY_2D(ix, iy, ctl->prof_ny);
15807 obsmean[idx] += robs[i];
15808 obscount[idx]++;
15809 }
15810
15811 /* Analyze model data... */
15812 for (ip = 0; ip < atm->np; ip++) {
15813
15814 /* Check time... */
15815 if (atm->time[ip] < t0 || atm->time[ip] > t1)
15816 continue;
15817
15818 /* Check grid boundaries and get indices... */
15819 const double zpart = Z(atm->p[ip]);
15820 if (atm->lon[ip] < ctl->prof_lon0
15821 || atm->lon[ip] >= ctl->prof_lon1
15822 || atm->lat[ip] < ctl->prof_lat0
15823 || atm->lat[ip] >= ctl->prof_lat1
15824 || zpart < ctl->prof_z0 || zpart >= ctl->prof_z1)
15825 continue;
15826 const int ix = (int) ((atm->lon[ip] - ctl->prof_lon0) / dlon);
15827 const int iy = (int) ((atm->lat[ip] - ctl->prof_lat0) / dlat);
15828 const int iz = (int) ((zpart - ctl->prof_z0) / dz);
15829 if (ix >= ctl->prof_nx || iy >= ctl->prof_ny || iz >= ctl->prof_nz)
15830 continue;
15831
15832 /* Get total mass in grid cell... */
15833 const int idx = ARRAY_3D(ix, iy, ctl->prof_ny, iz, ctl->prof_nz);
15834 mass[idx] += atm->q[ctl->qnt_m][ip];
15835 }
15836
15837 /* Extract profiles... */
15838 for (int ix = 0; ix < ctl->prof_nx; ix++)
15839 for (int iy = 0; iy < ctl->prof_ny; iy++) {
15840 int idx2 = ARRAY_2D(ix, iy, ctl->prof_ny);
15841 if (obscount[idx2] > 0) {
15842
15843 /* Check profile... */
15844 okay = 0;
15845 for (int iz = 0; iz < ctl->prof_nz; iz++) {
15846 int idx3 = ARRAY_3D(ix, iy, ctl->prof_ny, iz, ctl->prof_nz);
15847 if (mass[idx3] > 0) {
15848 okay = 1;
15849 break;
15850 }
15851 }
15852 if (!okay)
15853 continue;
15854
15855 /* Write output... */
15856 fprintf(out, "\n");
15857
15858 /* Loop over altitudes... */
15859 for (int iz = 0; iz < ctl->prof_nz; iz++) {
15860
15861 /* Get temperature, water vapor, and ozone... */
15863 intpol_met_time_3d(met0, met0->t, met1, met1->t, t, press[iz],
15864 lon[ix], lat[iy], &temp, ci, cw, 1);
15865 intpol_met_time_3d(met0, met0->h2o, met1, met1->h2o, t, press[iz],
15866 lon[ix], lat[iy], &h2o, ci, cw, 0);
15867 intpol_met_time_3d(met0, met0->o3, met1, met1->o3, t, press[iz],
15868 lon[ix], lat[iy], &o3, ci, cw, 0);
15869
15870 /* Calculate volume mixing ratio... */
15871 const int idx3 = ARRAY_3D(ix, iy, ctl->prof_ny, iz, ctl->prof_nz);
15872 vmr = MA / ctl->molmass * mass[idx3]
15873 / (RHO(press[iz], temp) * area[iy] * dz * 1e9);
15874
15875 /* Write output... */
15876 fprintf(out, "%.2f %g %g %g %g %g %g %g %g %g %d\n",
15877 t, z[iz], lon[ix], lat[iy], press[iz], temp, vmr, h2o, o3,
15878 obsmean[idx2] / obscount[idx2], obscount[idx2]);
15879 }
15880 }
15881 }
15882
15883 /* Free... */
15884 free(mass);
15885 free(obsmean);
15886 free(obscount);
15887
15888 /* Finalize... */
15889 if (t == ctl->t_stop) {
15890
15891 /* Close output file... */
15892 fclose(out);
15893
15894 /* Free... */
15895 free(lon);
15896 free(lat);
15897 free(area);
15898 free(z);
15899 free(press);
15900 free(rt);
15901 free(rz);
15902 free(rlon);
15903 free(rlat);
15904 free(robs);
15905 }
15906}
Here is the call graph for this function:

◆ write_sample()

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.

The write_sample function writes sample data to a file specified by the filename parameter. It takes control parameters (ctl), two meteorological data structures (met0 and met1), an atmospheric data structure (atm), and a time value (t) as input.

Parameters
filenameA string representing the filename where the sample data will be written.
ctlA pointer to a ctl_t structure containing control parameters.
met0A pointer to a met_t structure representing the first set of meteorological data.
met1A pointer to a met_t structure representing the second set of meteorological data.
atmA pointer to an atm_t structure representing atmospheric data.
tA double value representing the time at which the sample data is being written.

The function performs the following steps:

  • Initializes variables and allocates memory if it's the start of the simulation.
  • Reads observation data and kernel data if necessary.
  • Creates a new output file and writes header information to it.
  • Sets latitude range, squared radius, and area.
  • Processes observations and calculates sample data within the specified time interval.
  • Writes output data for each observation.
  • Finalizes by closing the output file and freeing allocated memory if it's the end of the simulation.
Note
This function writes sample data to a file, including time, altitude, coordinates, surface area, layer depth, number of particles, column density, volume mixing ratio, and observed data.
Author
Lars Hoffmann

Definition at line 15910 of file mptrac.c.

15916 {
15917
15918 if (ctl->met_coord_type != 0)
15919 ERRMSG("Only lat/lon grid supported");
15920
15921 static FILE *out;
15922
15923 static double area, dlat, rmax2, *rt, *rz, *rlon, *rlat, *robs, kz[EP],
15924 kw[EP];
15925
15926 static int nobs, nk;
15927
15928 /* Set timer... */
15929 SELECT_TIMER("WRITE_SAMPLE", "OUTPUT");
15930
15931 /* Init... */
15932 if (t == ctl->t_start) {
15933
15934 /* Allocate... */
15935 ALLOC(rt, double,
15936 NOBS);
15937 ALLOC(rz, double,
15938 NOBS);
15939 ALLOC(rlon, double,
15940 NOBS);
15941 ALLOC(rlat, double,
15942 NOBS);
15943 ALLOC(robs, double,
15944 NOBS);
15945
15946 /* Read observation data... */
15947 read_obs(ctl->sample_obsfile, ctl, rt, rz, rlon, rlat, robs, &nobs);
15948
15949 /* Read kernel data... */
15950 if (ctl->sample_kernel[0] != '-')
15951 read_kernel(ctl->sample_kernel, kz, kw, &nk);
15952
15953 /* Create output file... */
15954 LOG(1, "Write sample data: %s", filename);
15955 if (!(out = fopen(filename, "w")))
15956 ERRMSG("Cannot create file!");
15957
15958 /* Write header... */
15959 fprintf(out,
15960 "# $1 = time [s]\n"
15961 "# $2 = altitude [km]\n"
15962 "# $3 = longitude [deg]\n"
15963 "# $4 = latitude [deg]\n"
15964 "# $5 = surface area [km^2]\n"
15965 "# $6 = layer depth [km]\n"
15966 "# $7 = number of particles [1]\n"
15967 "# $8 = column density [kg/m^2]\n"
15968 "# $9 = volume mixing ratio [ppv]\n"
15969 "# $10 = observed BT index [K]\n\n");
15970
15971 /* Set latitude range, squared radius, and area... */
15972 dlat = DY2DEG(ctl->sample_dx);
15973 rmax2 = SQR(ctl->sample_dx);
15974 area = M_PI * rmax2;
15975 }
15976
15977 /* Set time interval for output... */
15978 const double t0 = t - 0.5 * ctl->dt_mod;
15979 const double t1 = t + 0.5 * ctl->dt_mod;
15980
15981 /* Loop over observations... */
15982 for (int i = 0; i < nobs; i++) {
15983
15984 /* Check time... */
15985 if (rt[i] < t0)
15986 continue;
15987 else if (rt[i] >= t1)
15988 break;
15989
15990 /* Calculate Cartesian coordinates... */
15991 double x0[3];
15992 geo2cart(0, rlon[i], rlat[i], x0);
15993
15994 /* Set pressure range... */
15995 const double rp = P(rz[i]);
15996 const double ptop = P(rz[i] + ctl->sample_dz);
15997 const double pbot = P(rz[i] - ctl->sample_dz);
15998
15999 /* Init... */
16000 double mass = 0;
16001 int np = 0;
16002
16003 /* Loop over air parcels... */
16004 //#pragma omp parallel for default(shared) reduction(+:mass,np)
16005 for (int ip = 0; ip < atm->np; ip++) {
16006
16007 /* Check time... */
16008 if (atm->time[ip] < t0 || atm->time[ip] > t1)
16009 continue;
16010
16011 /* Check latitude... */
16012 if (fabs(rlat[i] - atm->lat[ip]) > dlat)
16013 continue;
16014
16015 /* Check horizontal distance... */
16016 double x1[3];
16017 geo2cart(0, atm->lon[ip], atm->lat[ip], x1);
16018 if (DIST2(x0, x1) > rmax2)
16019 continue;
16020
16021 /* Check pressure... */
16022 if (ctl->sample_dz > 0)
16023 if (atm->p[ip] > pbot || atm->p[ip] < ptop)
16024 continue;
16025
16026 /* Add mass... */
16027 if (ctl->qnt_m >= 0)
16028 mass +=
16029 kernel_weight(kz, kw, nk, atm->p[ip]) * atm->q[ctl->qnt_m][ip];
16030 np++;
16031 }
16032
16033 /* Calculate column density... */
16034 const double cd = mass / (1e6 * area);
16035
16036 /* Calculate volume mixing ratio... */
16037 double vmr = 0;
16038 if (ctl->molmass > 0 && ctl->sample_dz > 0) {
16039 if (mass > 0) {
16040
16041 /* Get temperature... */
16042 double temp;
16044 intpol_met_time_3d(met0, met0->t, met1, met1->t, rt[i], rp,
16045 rlon[i], rlat[i], &temp, ci, cw, 1);
16046
16047 /* Calculate volume mixing ratio... */
16048 vmr = MA / ctl->molmass * cd / (RHO(rp, temp) * ctl->sample_dz * 1e3);
16049 }
16050 } else
16051 vmr = NAN;
16052
16053 /* Write output... */
16054 fprintf(out, "%.2f %g %g %g %g %g %d %g %g %g\n", rt[i], rz[i],
16055 rlon[i], rlat[i], area, ctl->sample_dz, np, cd, vmr, robs[i]);
16056 }
16057
16058 /* Finalize...... */
16059 if (t == ctl->t_stop) {
16060
16061 /* Close output file... */
16062 fclose(out);
16063
16064 /* Free... */
16065 free(rt);
16066 free(rz);
16067 free(rlon);
16068 free(rlat);
16069 free(robs);
16070 }
16071}
Here is the call graph for this function:

◆ write_station()

void write_station ( const char * filename,
const ctl_t * ctl,
atm_t * atm,
const double t )

Writes station data to a specified file.

The write_station function writes station data to a file specified by the filename parameter. It takes control parameters (ctl), an atmospheric data structure (atm), and a time value (t) as input.

Parameters
filenameA string representing the filename where the station data will be written.
ctlA pointer to a ctl_t structure containing control parameters.
atmA pointer to an atm_t structure representing atmospheric data.
tA double value representing the time at which the station data is being written.

The function performs the following steps:

  • Initializes variables and opens a new file if it's the start of the simulation.
  • Writes header information to the output file.
  • Sets geolocation and search radius for station data.
  • Processes air parcels and writes station data within the specified time interval and search radius.
  • Writes station data for each air parcel satisfying the criteria.
  • Closes the output file if it's the end of the simulation.
Note
This function writes station data to a file, including time, altitude, longitude, latitude, and additional quantities specified in the control parameters.
Author
Lars Hoffmann

Definition at line 16075 of file mptrac.c.

16079 {
16080
16081 if (ctl->met_coord_type != 0)
16082 ERRMSG("Only lat/lon grid supported");
16083
16084 static FILE *out;
16085
16086 static double rmax2, x0[3], x1[3];
16087
16088 /* Set timer... */
16089 SELECT_TIMER("WRITE_STATION", "OUTPUT");
16090
16091 /* Init... */
16092 if (t == ctl->t_start) {
16093
16094 /* Write info... */
16095 LOG(1, "Write station data: %s", filename);
16096
16097 /* Create new file... */
16098 if (!(out = fopen(filename, "w")))
16099 ERRMSG("Cannot create file!");
16100
16101 /* Write header... */
16102 fprintf(out,
16103 "# $1 = time [s]\n"
16104 "# $2 = altitude [km]\n"
16105 "# $3 = longitude [deg]\n" "# $4 = latitude [deg]\n");
16106 for (int iq = 0; iq < ctl->nq; iq++)
16107 fprintf(out, "# $%i = %s [%s]\n", (iq + 5),
16108 ctl->qnt_name[iq], ctl->qnt_unit[iq]);
16109 fprintf(out, "\n");
16110
16111 /* Set geolocation and search radius... */
16112 geo2cart(0, ctl->stat_lon, ctl->stat_lat, x0);
16113 rmax2 = SQR(ctl->stat_r);
16114 }
16115
16116 /* Set time interval for output... */
16117 const double t0 = t - 0.5 * ctl->dt_mod;
16118 const double t1 = t + 0.5 * ctl->dt_mod;
16119
16120 /* Count number of newly set station flags... */
16121 int nflag = 0;
16122
16123 /* Loop over air parcels... */
16124 for (int ip = 0; ip < atm->np; ip++) {
16125
16126 /* Check time... */
16127 if (atm->time[ip] < t0 || atm->time[ip] > t1)
16128 continue;
16129
16130 /* Check time range for station output... */
16131 if (atm->time[ip] < ctl->stat_t0 || atm->time[ip] > ctl->stat_t1)
16132 continue;
16133
16134 /* Check station flag... */
16135 if (ctl->qnt_stat >= 0)
16136 if ((int) atm->q[ctl->qnt_stat][ip])
16137 continue;
16138
16139 /* Get Cartesian coordinates... */
16140 geo2cart(0, atm->lon[ip], atm->lat[ip], x1);
16141
16142 /* Check horizontal distance... */
16143 if (DIST2(x0, x1) > rmax2)
16144 continue;
16145
16146 /* Set station flag... */
16147 if (ctl->qnt_stat >= 0) {
16148 atm->q[ctl->qnt_stat][ip] = 1;
16149 nflag += 1;
16150 }
16151
16152 /* Write data... */
16153 fprintf(out, "%.2f %g %g %g",
16154 atm->time[ip], Z(atm->p[ip]), atm->lon[ip], atm->lat[ip]);
16155 for (int iq = 0; iq < ctl->nq; iq++) {
16156 fprintf(out, " ");
16157 fprintf(out, ctl->qnt_format[iq], atm->q[iq][ip]);
16158 }
16159 fprintf(out, "\n");
16160 }
16161
16162 /* During the run, the parcel state lives on the GPU, see mptrac_init().
16163 Therefore, push the station flags written above back to the device;
16164 otherwise, they are invisible to the next timesteps and get discarded
16165 by the next mptrac_update_host() within mptrac_write_output(). */
16166#ifdef _OPENACC
16167 if (nflag > 0) {
16168#pragma acc update device(atm->q[ctl->qnt_stat][0:atm->np])
16169 }
16170#else
16171 (void) nflag;
16172#endif
16173
16174 /* Close file... */
16175 if (t == ctl->t_stop)
16176 fclose(out);
16177}
Here is the call graph for this function:

◆ write_vtk()

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.

The write_vtk function writes VTK data to a file specified by the filename parameter. It takes control parameters (ctl), an atmospheric data structure (atm), and a time value (t) as input.

Parameters
filenameA string representing the filename where the VTK data will be written.
ctlA pointer to a ctl_t structure containing control parameters.
atmA pointer to an atm_t structure representing atmospheric data.
tA double value representing the time at which the VTK data is being written.

The function performs the following steps:

  • Sets a timer and logs information about writing VTK data.
  • Sets a time interval for output based on the specified time and control parameters.
  • Creates a new file and checks if the file creation was successful.
  • Counts the number of data points to be written.
  • Writes the VTK header, including metadata.
  • Writes point coordinates based on the sphere or Cartesian coordinate system.
  • Writes point data for each quantity specified in the control parameters.
  • Closes the output file.
Note
This function writes VTK data in ASCII format, including point coordinates and associated scalar data for visualization purposes.
Author
Lars Hoffmann

Definition at line 16181 of file mptrac.c.

16185 {
16186
16187 if (ctl->met_coord_type != 0)
16188 ERRMSG("Only lat/lon grid supported");
16189
16190 FILE *out;
16191
16192 /* Set timer... */
16193 SELECT_TIMER("WRITE_VTK", "OUTPUT");
16194
16195 /* Write info... */
16196 LOG(1, "Write VTK data: %s", filename);
16197
16198 /* Set time interval for output... */
16199 const double t0 = t - 0.5 * ctl->dt_mod;
16200 const double t1 = t + 0.5 * ctl->dt_mod;
16201
16202 /* Create file... */
16203 if (!(out = fopen(filename, "w")))
16204 ERRMSG("Cannot create file!");
16205
16206 /* Count data points... */
16207 int np = 0;
16208 for (int ip = 0; ip < atm->np; ip += ctl->vtk_stride) {
16209 if (atm->time[ip] < t0 || atm->time[ip] > t1)
16210 continue;
16211 np++;
16212 }
16213
16214 /* Write header... */
16215 fprintf(out,
16216 "# vtk DataFile Version 3.0\n"
16217 "vtk output\n" "ASCII\n" "DATASET POLYDATA\n");
16218
16219 /* Write point coordinates... */
16220 fprintf(out, "POINTS %d float\n", np);
16221 if (ctl->vtk_sphere) {
16222 for (int ip = 0; ip < atm->np; ip += ctl->vtk_stride) {
16223 if (atm->time[ip] < t0 || atm->time[ip] > t1)
16224 continue;
16225 const double radius = (RE + Z(atm->p[ip]) * ctl->vtk_scale
16226 + ctl->vtk_offset) / RE;
16227 const double coslat = cos(DEG2RAD(atm->lat[ip]));
16228 const double x = radius * coslat * cos(DEG2RAD(atm->lon[ip]));
16229 const double y = radius * coslat * sin(DEG2RAD(atm->lon[ip]));
16230 const double z = radius * sin(DEG2RAD(atm->lat[ip]));
16231 fprintf(out, "%g %g %g\n", x, y, z);
16232 }
16233 } else
16234 for (int ip = 0; ip < atm->np; ip += ctl->vtk_stride) {
16235 if (atm->time[ip] < t0 || atm->time[ip] > t1)
16236 continue;
16237 fprintf(out, "%g %g %g\n", atm->lon[ip], atm->lat[ip],
16238 Z(atm->p[ip]) * ctl->vtk_scale + ctl->vtk_offset);
16239 }
16240
16241 /* Write point data... */
16242 fprintf(out, "POINT_DATA %d\n", np);
16243 for (int iq = 0; iq < ctl->nq; iq++) {
16244 fprintf(out, "SCALARS %s float 1\n" "LOOKUP_TABLE default\n",
16245 ctl->qnt_name[iq]);
16246 for (int ip = 0; ip < atm->np; ip += ctl->vtk_stride) {
16247 if (atm->time[ip] < t0 || atm->time[ip] > t1)
16248 continue;
16249 fprintf(out, "%g\n", atm->q[iq][ip]);
16250 }
16251 }
16252
16253 /* Close file... */
16254 fclose(out);
16255}

Variable Documentation

◆ geometry

unsigned long long* geometry

Definition at line 10796 of file mptrac.c.

◆ global

float* global

Definition at line 10797 of file mptrac.c.

◆ packed

float * packed

Definition at line 10797 of file mptrac.c.

◆ local

float * local

Definition at line 10797 of file mptrac.c.

◆ global_elements

size_t global_elements

Definition at line 10798 of file mptrac.c.

◆ packed_elements

size_t packed_elements

Definition at line 10798 of file mptrac.c.

◆ local_elements

size_t local_elements

Definition at line 10798 of file mptrac.c.

◆ rank

int rank

Definition at line 10799 of file mptrac.c.

◆ size

int size

Definition at line 10799 of file mptrac.c.