/*
 *  Purpose:
 *
 *    Sets derivatives for a piecewise cubic Hermite interpolant, assuming periodic input.
 *    As for spline_pchip(), assume a zero-offset input vector of length n, x[0] to x[n-1],
 *    with periodic boundary conditions x[0] = x[n-1].
 *
 *  NOTE: The endpoints span the full periodic interval in order for the full range to
 *        behave as an interpolation.
 *
 *  For Discussion, Authorship, References and Parameters, see the leading comments
 *  in spline_pchip() above.
 */


/*
 *  Purpose:
 *
 *    Evaluates a cubic polynomial given in Hermite form.
 *
 *  Discussion:
 *
 *    This routine evaluates a cubic polynomial given in Hermite form, and
 *    is designed to work with spline_pchip().
 *
 *    The cubic polynomial is determined by function values
 *    table[0].y, table[1].y and derivatives table[0].z, table[1].z
 *    on the interval [table[0].x,table[1].x].
 *
 *    This routine was originally called "CHFEV".
 *
 *  Modified:
 *
 *    25 August 2007
 *
 *  Author:
 *
 *    Fred Fritsch,
 *    Mathematics and Statistics Division,
 *    Lawrence Livermore National Laboratory.
 *
 *    C translation by John Burkardt.
 *
 *    Modified by Tim Dowling.
 *
 *  References:
 *
 *    Fred Fritsch, Ralph Carlson,
 *    Monotone Piecewise Cubic Interpolation,
 *    SIAM Journal on Numerical Analysis,
 *    Volume 17, Number 2, April 1980, pages 238-246.
 *
 *    David Kahaner, Cleve Moler, Steven Nash,
 *    Numerical Methods and Software,
 *    Prentice Hall, 1989,
 *    ISBN: 0-13-627258-4,
 *    LC: TA345.K34.
 *
 *  Parameters:
 *
 *    Input, FLOAT xx, the position of the interpolation
 *
 *    Input, float_triplet *table:
 *       The use of the data type float_triplet facilitates
 *       cache-aware memory allocation.
 *
 *       NOTE: This function does not search the table,
 *             but assumes xx is between the given inputs as
 *             returned by find_place_in_table() or hunt_place_in_table().
 *
 *       table[0].x and table[1].x are the endpoints of the interval of
 *       definition of the cubic; they must be distinct.
 *
 *       table[0].y and table[1].y are the corresponding values of the function.
 *
 *       table[0].z and table[1].z are the corresponding derivative values.
 *
 *    Input, h = dx, the x interval, as returned by find_place_in_table().
 *    This is included here to make it easy to switch with splint().
 *
 *    Returns the value of the cubic function at the point xx.
 */


/*
 *  Purpose:
 *
 *    Sign-testing routine.
 *
 *  Discussion:
 *
 *    This routine essentially computes the sign of arg1 * arg2.
 *
 *    The object is to do this without multiplying arg1 * arg2, to avoid
 *    possible over/underflow problems.
 *
 *  Modified:
 *
 *    25 August 2007
 *
 *  Author:
 *
 *    Fred Fritsch,
 *    Mathematics and Statistics Division,
 *    Lawrence Livermore National Laboratory.
 *
 *    C translation by John Burkardt.
 *
 *    Adapted by Tim Dowling.
 *
 *  Reference:
 *
 *    Fred Fritsch, Ralph Carlson,
 *    Monotone Piecewise Cubic Interpolation,
 *    SIAM Journal on Numerical Analysis,
 *    Volume 17, Number 2, April 1980, pages 238-246.
 *
 *  Parameters:
 *
 *    Input, FLOAT arg1, arg2, two values to check.
 *
 *    Output, FLOAT pchst,
 *    -1.0, if arg1 and arg2 are of opposite sign.
 *     0.0, if either argument is zero.
 *    +1.0, if arg1 and arg2 are of the same sign.
 */


/*======================= lagrange_interp() =================================*/
/*
 * Returns the lagrangian interpolation of f(x).
 *
 * Input: "order" is the order of polynomial interpolation
 *        (0=copy,1=linear,2=quadradic,3=cubic,etc.).
 *
 *        f[order+2] where f[0] is empty,
 *                         f[1] = f(x1),
 *                         f[2] = f(x2),
 *                         f[3] = f(x3), etc.
 *
 *        x[order+2] where x[0] is the point of interpolation, i.e., "x" in f(x),
 *                          x[1] = x1,
 *                          x[2] = x2,
 *                          x[3] = x3, etc.
 */


/*
 * CJP 06/2003 *A*
 * Based on "Numerical Recipes in C", 2nd Ed., p 213-214, Cambridge.
 */


/*
 * Populate the exponential integral tables with x values ranging
 * from 10^-8 to 10^9. Similarly, populate y values in exp3table
 * with corresponding third-order exponential integral values, and
 * populate exp4table with corresponding fourth-order exponential
 * integral values.
 */


/*
 * Calculate machine's smallest meaningful floating-point number.
 * From Dennis and Schnabel (1996), Algorithm A1.3.1.
 */


/*
 * Finds a vector root, x[], of the vector function vecfunc().
 * This globally convergent algorithm is adapted from
 * Numerical Recipes in C, 2nd ed., p. 389-392, which is based on
 * Dennis and Schnabel (1996).
 *
 * Call with an initial guess for x[].
 * Assumes zero-based indexing.
 *
 * The function global_step() performs the globally-convergent step.
 * The argument step_type specifies the type of step taken. Currently, the
 * valid choices are DS_LINE_STEP and DS_DOGLEG_STEP, the latter being more
 * sophisticated and reliable (the "DS" stands for Dennis and Schnabel 1996).
 *
 * Returns 0 on normal execution and an error code if
 * the routine has failed, has converged to a local minimum, or can make
 * no further progress, in which case one should retry with a
 * different initial guess.
 */


/*
 * Take a globally convergent step towards a vector root.
 * Returns max_taken.
 *
 * The function line_search() backtracks along the quasi-Newton direction.
 *
 * The function dogleg_driver() uses the model trust-region approach, where
 * delta is the radius of the trust region. A step is taken
 * in the steepest descent direction for small delta, in the quasi_Newton
 * direction for large delta, and on the connecting line segment for
 * intermediate delta.
 * See Dennis and Schnabel (1996, DS96).
 *
 * The value of step_type can be DS_LINE_STEP, DS_HOOK_STEP, or DS_DOGLEG_STEP.
 *
 * NOTE: Unlike in DS96, here R is R of QR, not the transpose of R.
 */


/*
 * Adapted from Numerical Recipes in C, 2nd ed., p. 385-386, which is
 * an implementation of Algorithm A6.3.1 of Dennis and Schnabel (1996).
 * Returns max_taken.
 * Assumes zero-based indexing.
 */


/*
 * Adapted from Dennis and Schnabel (1996), Appendix A, Algorithm A6.4.3.
 * Returns max_taken;
 * Assumes zero-based indexing.
 */


/*
 * Adapted from Dennis and Schnabel (1996), Appendix A, Algorithm A6.4.4.
 * Returns newt_taken.
 * Assumes zero-based indexing.
 */


/*
 * Adapted from Dennis and Schnabel (1996), Appendix A, Algorithm A6.4.5.
 * Returns max_taken.
 * Assumes zero-based indexing.
 */


/*
 * Adapted from Numerical Recipes in C, 2nd ed., p.99.
 * Returns FALSE if normal and TRUE if singular.
 * Assumes zero-based indexing.
 */


/*
 * Adapted from Numerical Recipes in C, 2nd ed., p. 101.
 * Assumes zero-based indexing.
 */


/*
 * Adapted from Numerical Recipes in C, p. 56.
 * Assumes zero-based indexing.
 *
 * NOTE: May not have much effect if lu_decompose(), lu_backsub()
 *       are in double precision.
 */


/*
 * Find place in table using bisection.
 * Adapted from Numerical Recipes in C, p. 117.
 *
 * NOTE: Finds place relative to the x component of the float_triplet table.
 */


/*
 * Find place in table using bisection.
 * Adapted from Numerical Recipes in C, p. 118-119.
 *
 * NOTE: Hunts place relative to the x component of the float_triplet table.
 *       Requires an initial guess (il) to location within table, which should be
 *       populated by the calling function as the value returned previously.
 */


/*
 * Compute polynomial coefficients.
 * Assumes zero-offset arrays [0..n].
 * Adapted from polcoe() in Numerical Recipes in C, 2nd ed, p. 121.
 * Due to G.B. Rybicki.
 */


/*
 * Adapted from Numerical Recipes in C, 2nd Ed., p. 137.
 * Usage note: Call with successively increasing n, starting at n = 1.
 */


/*
 * 3rd-order accurate integration on a non-staggered grid
 * (endpoints are 2nd-order accurate).
 *
 * Input:
 * z[0 to n+1]: the independent-variable
 * dfdz[0 to n+1]: the derivative
 * f[0]: the value of the integral at the starting position
 *
 * Output:
 * f[0 to n+1]: the integral of df/dz
 *
 * This scheme is based on the one described and illustrated in Fig. 8(d) of
 *    Leslie LM,  Purser RJ, 1992, A comparative study of the performance of various
 *       vertical discretization schemes, Meteorol. Atmos. Phys. 50, 61-73.
 */


/*
 * 2nd-order accurate compact differentiation on a non-staggered grid.
 * See Leslie and Purser (1992) as cited above.
 *
 * Input:
 * action: SETUP_UTIL, RUN_UTIL, and CLEANUP_UTIL
 * z[0 to n+1]: the independent-variable
 * f[0 to n+1]: the dependent variable
 * dfdz[0], the derivative df/dz at the starting position.
 *          Set to 1.e+20 to calculate this internally assuming a parabolic fit.
 *
 * NOTE: Call with SETUP_UTIL and CLEANUP_UTIL only once for a given z[] vector.
 *
 * Output:
 * dfdz[0 to n+1], df/dz
 *
 * This is the exact inverse of the 3rd-order accurate integration scheme
 * used in compact_integration(); it involves a 4-band matrix inversion.
 */


/*
 * Apply diffusion to the 1D input vector, A[k], for one timestep using the Crank-Nicolson scheme.
 *
 * The equation integrated in time is:
 *
 *      dA     1   d   /     dA  \
 *      --- = --- --- (  mu ----  )
 *      dt    rho  dz  \     dz  /
 *
 * The algorithm does not assume a regularly spaced grid, but
 * it does assume that the grid position, z[k], does not change during the timestep.
 * See Blottner (1980, Computer and Fluids 8, 421-434) for a description of how the variable grid
 * is handled.
 *
 * For input A[k], k = 0 and k = n+1 are boundary points.  Set to 1.e+20
 * to signal a no-flux boundary, otherwise set to the boundary value.
 *
 * One may specify the case rho[k] = 1.0 for all k by inputting NULL for rho.
 *
 * The input vector mu[k] may vary spatially, but is assumed to not change during the timestep.
 * The position of mu[k] is centered between A[k] and A[k+1].
 *
 * Input vectors are assumed to be continguous 1D vectors with memory
 * allocations in the range [0,n+1]. The grid positions z are expected to be monotonically
 * increasing, and an error is generated if this is not the case.
 * The result is returned in ANS[k] for k = 1 to n, such that
 * ANS[0] and ANS[n+1] are not altered. Memory for ANS should not overlap A.
 *
 * NOTE: Assumes no MPI domain decomposition in active dimension.
 */


/*
 * From Numerical Recipes in C, 2nd Ed., p. 491-492.
 * Finds eigenvalues of a real Hessenberg matrix.
 * Assumes zero-based indexing.
 */


/*======================= quicksort() ========================================*/
/*
 * From K & R 2nd ed., p. 87:
 */


/*
       *  Real FFT routine.
       *  Numerical Recipes in C, 2nd ed, p. 513.
       *  Assumes data length is a power of 2; assumes unit-based array.
       *  Result of inverse must be multiplied by 2/n.
       */


/*
 * NOTE: For LINUX with -D_BSD_SOURCE, cabs() is defined, such that a
 * type-mismatch error occurs if we call this function cabs().
 */


/*
 * Derived from fcmp(), version 1.2.2,
 * Copyright (c) 1998-2000 Theodore C. Belding
 * University of Michigan Center for the Study of Complex Systems
 * <mailto:Ted.Belding@umich.edu>
 * <http://fcmp.sourceforge.net>
 *
 * The major modification we have made is to remove the "epsilon" argument
 * and set epsilon inside the fcmp() function.
 *
 * Description:
 *   It is generally not wise to compare two floating-point values for
 *   exact equality, for example using the C == operator.  The function
 *   fcmp() implements Knuth's suggestions for safer floating-point
 *   comparison operators, from:
 *   Knuth, D. E. (1998). The Art of Computer Programming.
 *   Volume 2: Seminumerical Algorithms. 3rd ed. Addison-Wesley.
 *   Section 4.2.2, p. 233. ISBN 0-201-89684-2.
 *
 * Input parameters:
 *   x1, x2: numbers to be compared
 *
 * This routine may be used for both single and double precision.
 *
 * Returns:
 *   -1 if x1 < x2
 *    0 if x1 == x2
 *    1 if x1 > x2
 */


/*
 * Code for fitting data to a straight line using the least squares technique.
 *   Csaba J. Palotai *A*  4/14/2004
 * Adapted from "Numerical recipes in C," p665.
 *
 * Given a set of data points x[0..n-1],y[0..n-1] the code fits them to a
 * straight line y= a+bx by minimizing X^2. Returned are a,b.
 *
 * Commented out is code that can be used to calculate the uncertainties
 * siga and sigb, and the chi-square, chi2.
 */


/*
 * lat: [Deg]
 * rerp: (equatorial radius)/(polar radius)
 */


/*
 * Surface area of an oblate spheroid, given equatorial and polar radii a and c,
 * respectively.
 * See http://mathworld.wolfram.com/OblateSpheroid.html.
 */


/*======================= util_error() =======================================*/
/*
 * Prints calling function name, and Message to stderr, then aborts.
 */


/*
 *   Basic steps:
 *
 *   1) NODE0 reads subarray data from the file into its buff_subarray.
 *
 *   2) NODE0 sends data from its buff_subarray to the target node's buff_subarray.
 *
 *   3) The target node transfers data from its buff_subarray to the appropriate place.
 *
 * A function that uses read_array() must first call setup_read_array().
 * Call from all nodes.
 *
 * For array types that have striped data (array_type != EPIC_FLOAT_ARRAY), pass
 * the name of the first float array that will be read in, and the naming
 * convention will be reconstructed.
 *
 * NOTE: Does not apply boundary conditions.
 */


/*
 *   Basic steps:
 *
 *   1) Source node transfers data from the appropriate place to its buff_subarray.
 *
 *   2) Source node sends data from its buff_subarray to NODE0's buff_subarray.
 *
 *   3) NODE0 writes source node's data from its buff_subarray to the file.
 *
 * A function that uses write_array() must first call setup_write_array().
 * Call from all nodes.
 *
 * If stretch_ni > 0, stretch KJ-plane zonal average into a KJI cube.
 * If stretch_ni < 0, suppress the I (zonal) dimension (as in a KJ plane).
 *
 * For array types that have striped data (array_type != EPIC_FLOAT_ARRAY), pass
 * the name of the first float array that will be read in, and the naming
 * convention will be deduced from this.
 *
 */


/*
 * Read in the variables and applies boundary conditions.
 *
 * NOTE: When adding new variables, remember to apply boundary conditions
 *       here.
 */


/*
 * Write the variables u, v, p, theta, etc.
 *
 * If ni == 1 and stretch_ni > 1, stretch the model from 2D to 3D on output,
 * with the new ni being stretch_ni.
 */


/*
 * Read numbers and names of global attributes and variables
 * contained in infile, which must be in netCDF format.
 * Returns NC_NOERR if no read error, otherwise returns nc_err.
 *
 * NOTE: ngatts,**gattname,num_progs,**varname should be declared static in the calling
 *       function, with their input values equal to the last call, in order to
 *       properly reallocate memory.
 */


/*
 * Removes any .gz suffix from file_name and
 * decompresses the file if necessary.
 */


/*====================== read_spacing_file() ==================================*/
/*
 * Read layer spacing data from the file def->layer_spacing_dat.
 * The following lines without asterisks illustrate the format of this file;
 * one can cut cut and paste these lines to make a template.
   nk         10
   K        p[hPa]
   0          1.0
   1          2.6
   2          6.4
   3         14.2
   4         35.1
   5        118.1
   6        375.6
   7       2416.9
   8       3624.2
   9       4344.0
  10       6000.0
 */


/*
 * NOTE: This function should only be called during initialization, not when the
 *       model is running, and hence does not need to be MPI ready.
 */


/*
 * Reads data from a text file arrayed in a pressure-latitude meridional plane.
 * An example of the format is the file epic/data/Jupiter/C_2H_2.Jupiter.CIRS.
 *
 * The input argument infile is the name of the file, including its path,
 * and portion is SIZE_DATA or POST_SIZE_DATA.
 *
 * The output arguments np and nlat are the number of pressure and latitude levels
 * in the requested data set.
 * The output arguments logp and lat are the respective 1D dimension values, where latitude
 * increases with index j and log pressure increases with index k.
 * The output argument value is the 2D array of data values associated with varname,
 * such that value[k][j] refers to pressure level k and latitude j.
 */


/*
 * Prompt the user to choose which radiation scheme to use.
 */


/*
 * Read in floating-point datum, or set to default if input is a return ('\n').
 *
 * C.Santori, T.Dowling
 */


/*
 * Read in int, or set to default if input is a return ('\n').
 * C.Santori, T.Dowling
 */


/*
 * Read in a string, or set to default if input is a return ('\n').
 *
 * C.Santori, T.Dowling
 */


/*====================== print_model_description() ===========================*/
/*
 * Print to stdout a select listing of model parameters.
 */


/*
 * Aaron Herrnstein.
 *
 * Routine prints a message using Processor 0.  MPI_Barrier() calls are placed
 * before and after the print statement.  This can be beneficial when tracking
 * down MPI bugs.
 */


/*======================= epic_error() =======================================*/
/*
 * Prints machine name, calling node rank, timestep, calling function name,
 * and Message to stderr, then aborts.
 */


/*======================= epic_warning() =====================================*/
/*
 * Prints machine name, calling node rank, calling function name,
 * and Message to stderr (does not exit).
 */


/*
 * Hybrid mass flux for variable at the top of the model.
 *
 * For index == H_INDEX, this is WH.
 * For index == H_2O_INDEX, this is WH*(mixing ratio of H_2O), etc.
 *
 * Returns as an argument the hybrid vertical velocity at the
 * top of the model, wtop.
 */


/*
 * Hybrid mass flux for variable at the bottom of the model.
 *
 * For index == H_INDEX, this is WH.
 * For index == H_2O_INDEX, this is WH*(mixing ratio of phase), etc.
 *
 * Returns as an argument the hybrid vertical velocity at the
 * bottom of the model, wbot.
 */


/*
 * Advect scalar prognostic variables forward one timestep using
 * the specified scheme(s). Ensure positive-definite status as necessary.
 *
 * NOTE: Non-flux form schemes should lead with the 13 characters
 *       "Non-flux form" in their advection_scheme string.
 */


/*
 *  Hsu and Arakawa's predictor-corrector advection, as modified
 *  by eqs. (B.5) and (B.6) of Konor and Arakawa (1997).
 *  We apply it to each direction sequentially.
 *
 *  This is a positive-definite, flux-form advection scheme
 *  that handles steep gradients.
 */


/*
 * Wrapper function that allows us to change easily between algorithms.
 *
 * As background, note that horizontal advection of U and V is accomplished by
 * writing the momentum equations in the vector-invariant form with (zeta+f)u
 * and (zeta+f)v, and using the Arakawa C-grid.  Meanwhile, horizontal and vertical
 * advection of the scalar variables (mass variables like H, and also turbulence
 * variables like NU_TURB) are done with a positive-definite scheme. Because U and V
 * are signed quantities, they need a different approach.
 */


/*
 * Add sinusoidal perturbation based on parameters in waves_file.
 * The vertical size c is in scale heights [sc_ht].
 * The amplitude [m2s-2] refers to the perturbation streamfunction, i.e.,
 * PERT = MONT for theta coordinates or PERT = PHI for pressure coordinates.
 *
 * Example waves_file:
 * ------------------------------------------------------------------
 * Number of waves: 2
 * lat[deg]   amp[m2s-2]  wn[]  p[hPa]  c[sc_ht]   fwhm[deg]
 *   30.0      0.02       1.0   700.0      1.5        1.50
 *   40.0      0.10       5.0   700.0      1.5        3.00
 * ------------------------------------------------------------------
 *
 * NOTE: wn[] is domain wavenumber, not planetary wavenumber.
 *
 * Tim Dowling and Raul Morales-Juberias, June 2022
 *
 * NOTE: Not MPI ready.
 */


/*
 * Retrieve the integer after the first colon, ':', which
 * is interpreted as the number of objects (spots, waves, etc).
 * in the given parameter file.
 */


/*
 * March the prognostic variables ahead one timestep using the
 * specified schemes.
 *
 * If action == STEP_PROGS_AND_UPDATE_DIAGS, we assume the diagnostic variables
 * are all up-to-date upon entry.
 *
 * If action == UPDATE_DIAGS_ONLY, we bring the diagnostic variables up-to-date
 * with the prognostic variables, but do not advance the progostic variables.
 * This is used to prime the pump at startup.
 */


/*
 * Use the 3rd-order Adams-Bashforth timestep.
 * This timestep is discussed by D. Durran (1991, MWR 119, 702-720).
 * It is appropriate for dissipative terms as well as for
 * conservative terms, does not suffer from the time-splitting
 * numerical instability of the leap-frog timestep, and is more
 * accurate than the leapfrog timestep.  Its main drawback is that
 * it requires more memory because it uses two previous time derivatives.
 */


/*
 * Calculate core tendencies for u,v for the current state.
 * These include advection and Coriolis acceleration.
 * The pressure-gradient terms are calculated separately as part
 * of the economical explicit scheme.
 */


/*
 * Calculate pressure-gradient tendencies for (u,v).
 *
 * A choice of algorithms is available for the horizontal pressure-gradient
 * force: uv_pgrad_traditional() and uv_pgrad_green_gauss().
 */


/*
 * This function initializes the thermodynamics functions.
 * Adapted from Peter Gierasch's Fortran subroutines setup(),
 * numbers(), h2properties(), hydrogen(), theta2t_table() and trgrid().
 *
 * NOTE: cpr_out is the low-temperature-limit of cp/rgas.
 */


/*
 * Adapted from Gierasch's Fortran subroutine get_temperature().
 * See Dowling et al (1998), Appendix A, eq. (A.15).
 * Table resolution has been improved since the Dowling et al paper.
 *
 * NOTE:  The calling program must initialize this function with a call
 *        to thermo_setup().
 */


/*
 * For use with find_root().
 */


/*
 * Adapted from Peter Gierasch's Fortran subroutine get_theta().
 *
 * NOTE:  The calling program must initialize this function with a call
 *        to thermo_setup().
 */


/*
 * NOTE:  The calling program must initialize this function with a call
 *        to thermo_setup().
 */


/*
* Calculate equilibrium fraction of para hydrogen.
* Adapted from Peter Gierasch's hydrogen() Fortran subroutine.
*/


/*
 * Returns the specific heat at constant pressure. This is calculated as a
 * derivative of the enthalpy, cp = (denthalpy/dT)_p, a partial derivative
 * at constant p. All other state variables, such as fpara, water amount, etc,
 * are also to be held constant.  By using this method, cp will be correct
 * even if there are changes to the thermodynamics in return_enthalpy().
 *   -- A.P. Showman, 8/31/1999.
 *
 * NOTE: thermo_setup() must have already been called at initialization.
 */


/*
 * Return cp [J kg-1 K-1] for the given temperature and species.
 */


/*
 * We currently just maintain a floor of grid.h_min[K] for H and 0.0
 * for mixing-ratio variables like H_2O_liquid.
 *
 * Returns TRUE if any adjustment was made, otherwise returns FALSE.
 *
 * NOTE: Previously we distributed mass vertically, but we found that
 *       this tends to generate unwanted gravity waves.
 *
 * NOTE: This function should not call set_p2_etc() itself.
 */


/*
 * Converts calendar date/time [C data type time_t, sec] into L_s [deg],
 * the planetocentric longitude of the Sun.
 */


/*
 * Returns the declination of the Sun [deg], also called the
 * sub-solar latitude.
 *
 * gurn: need to decide if IAU vs dynamic north flips this
 *
 * See Meeus (2005), (13.4), with the ecliptic latitude of the Sun,
 * beta = 0., and the solar longitude, l_s, which yields
 *  sin(dec_s) = sin(obliquity)*sin(l_s)
 */


/*
 * Return the difference between the planet's apparent solar time and its
 * mean solar time as expressed in west longitude [deg] (positive to
 * the right on the sky as seen from the planet), given a
 * calender date and the solar longitude [deg], l_s in the range [0.,360.].
 *
 * If l_s is negative, calculate it internally given the
 * planet and date.  We take l_s as an argument to save computational
 * time if it has already been determined.
 */


/*
 * Returns the eastward-increasing longitude [-180, 180] used in
 * the EPIC model, where it is solar noon (where the Sun is on the meridian)
 * for the specified planet, as a function of calendar time.
 * The zero of east longitude is aligned with the zero of west longitude [0, 360].
 *
 * If l_s is negative, calculate it internally given the
 * planet and date.  We take l_s as an argument to save computational
 * time if it has already been determined.
 */


/*
 *  Input: calendar time; see comments for solar_longitude()
 * Output: distance between planet and the Sun [AU]
 *
 * See Chap. 30 Equation of Kepler, p. 195, in
 *   Meeus J, 2005, Astronomical Algorithms, Willmann-Bell
 */


/*
 * Solve Kepler's equation, M = E - e sin E, for eccentric anomaly, E,
 * given the mean anomaly, M, and the orbital eccentricity, e.
 */


/*
 * Returns true anomaly, nu [deg], given eccentric anomaly, E [deg],
 * and orbital eccentricity, e.
 */


/*
 * Returns the polar radius, c, given the equatorial radius, a, and J2, GM, and omega.
 * Assumes a uniformly rotating fluid planet, and uses a root finder to solve the
 * cubic equation for ep = c/a.
 */


/*
 * Returns the equatorial radius, a, given the polar radius, c, and J2, GM, and omega.
 * Assumes a uniformly rotating fluid planet, and uses a root finder to solve the
 * cubic equation for ep = c/a.
 */


/*
 * Returns solar mixing ratio of the least-abundant element in
 * the given species name (divided by its stochiometric count).
 * Choices for the type argument: MASS, MOLAR.
 * The character string min_element should be 4 bytes.
 *
 * NOTE: Here, "mixing ratio" has the total in the denominator, and hence
 *       in the language of meteorology is analogous to "specific humidity", rather
 *       than to a "mixing ratio" that has the dry component in the denominator.
 */


/*
 * Generalized coordinate, sigmatheta, as a function of pressure and potential
 * temperature. Use double precision to increase accuracy of diagnostic theta
 * calculations.
 */


/*======================= f_sigma() =========================================*/
/*
 * Hybrid vertical coordinate definition:
 *   sigmatheta = f(sigma)+g(sigma)*theta.
 * Use double precision to increase accuracy of diagnostic theta calculations.
 */


/*======================= g_sigma() =========================================*/
/*
 * Part of the hybrid vertical coordinate definition,
 * sigmatheta = f(sigma)+g(sigma)*theta.
 * Use double precision to increase accuracy of diagnostic theta calculations.
 */


/*
 * Calculates h = -(1/g) dp/dsgth.
 *
 * The input pressure, p, and the output h, are vertical columns that use
 * the doubled kk notation for which the top of the model is kk = 2*KLO-1 and
 * the bottom is kk = 2*KHI+1.
 */


/*
 * The molar mass for dry air is assumed to be R_GAS/planet->rgas.
 */


/*
 * NOTE: High-level subroutines should call get_var() to evaluate a variable
 *       at a vertical position that is different than its natural position,
 *       not onto_kk(), which is just an averager and does not handle
 *       boundary cases.
 *
 * Evaluate variable onto vertical position kk, the doubled
 * vertical index.  Having all the vertical averaging schemes together
 * in one place makes it easier to manage them.
 *
 * An illustration of the kk index convention is:
 *   kk = 3 => K = 1.5, the bottom of layer 1,
 *   kk = 4 => K = 2.0, the middle of layer 2, etc.
 *
 * The input parameters topval and botval are the higher-altitude and
 * lower-altitude values to be averaged onto level kk, in other words the
 * lower K and higher K endpoints, respectively.
 */


/*
 * Calculates and stores diagnostic variables that are typically used in the
 * pressure-gradient terms of the momentum equations.  Doing this in a
 * separate function facilitates the "poor man's implicit" timestep.
 *
 * An illustration of the notation with respect to the vertical index K:
 *
 *              ---------------------------------------------------
 *     Layer K:   T2  RHO2  EXNER2  FGIBB2  PHI2  MONT2  Z2 DZDT2(*)
 * Interface K: --T3--RHO3--EXNER3----------PHI3---------Z3--------
 *
 * (*) For action == STEP_PROGS_AND_SYNC_DIAGS, this is a convenient place
 *     to start the calculation of the regular vertical velocity, DZDT2, in m/s.
 *     This variable is finished in calc_w(), after the hybrid vertical velocity
 *     is calculated.
 *
 * If phi3nk_status == PASSING_PHI3NK, PHI3(KHI,J,I) is assumed to be set
 * before calling this function; if CALC_PHI3NK, it is calculated here.
 *
 * NOTE: This function should not modify any prognostic variables.
 *       In addition, it should not modify P2, P3, or THETA2.
 *
 * NOTE: This function must be called from all nodes.
 */


/*
 * Calculates and stores commonly used diagnostic variables other than
 * those covered by set_p2_etc() and store_pgrad_vars().
 *
 * NOTE: This function must be called from all nodes.
 */


/*
 * Compute the divergence of the vector (uu,vv) using map factors.
 * UU, VV, and DI, are assumed to be 2D arrays on the staggered C grid.
 */


/*
 * Calculates vorticity for C-grid in a JI (horizontal) plane.
 * The valid types are POTENTIAL, ABSOLUTE, and RELATIVE.
 *
 * NOTE: Currently, only surface_type == ON_SIGMATHETA is implemented.
 *       We may wish to implement ON_THETA in the future.
 */


/*
 * Calculate the relative humidity (RH) at the bottom of layer K.
 *
 * See Bohren and Albrecht (1998, Atmospheric Thermodynamics, Oxford, p.186)
 * for an interesting discussion about different definitions of relative humidity.
 *
 * We use the WMO definition because microphysical equations are usually
 * written in terms of mixing ratio rather than partial pressure.
 */


/*
 * Apply source and sink terms to h-grid variables.
 * Use a forward timestep to integrate rates.
 */


/*
 * Calculate HEAT3, in W/kg.
 *
 * NOTE: Latent heating from clouds is handled outside this function.
 */


/*
 * Estimate CFL timestep for numerical stability.
 * Use sound speed as an upper bound on gravity-wave speed.
 *
 * NOTE: For spin-up experiments, this estimate of dt_cfl will initially be
 *       based on zero winds, and may be too large to keep developing jets
 *       numerically stable.
 */


/*
 * Return integer remainder of time/step.
 * Avoids directly forming YEAR*time.years to stay below INT_MAX.
 */


/*
 * Computes average molar mass at position kk/2,j,i.
 */


/*
 * Synchronize all number fractions, X_i, to mass mixing ratios, Q_i.
 *
 * NOTE: Assumes the bookkeeping arrays grid.is[] and grid.ip[] have been set up.
 */


/*
 * Returns the molar mass (molecular weight) for the indicated substance.
 * Units are kg/kmol, which is the same as g/mol.
 */


/*
 * Calculate the binary-gas mass diffusivity [m^2/s] based on the
 * Fuller-Schettler-Giddings correlation, as described in
 * Perry's Chemical Engineers' Handbook (1997), Table 5-14 and 5-16.
 */


/*
 * Set the time index for the current time for the prognostic variables
 * and their tendencies, depending on the chosen time-marching algorithms.
 */


/*
 * Calculate u(p,lat) for Venus model, where lat is in degrees.
 */


/*
 * Set u(p,lat) for Earth, where lat is in degrees.
 */


/*
 * Set u(p,lat) for Mars, where lat is in degrees.
 */


/*
 *  Calculate u(p,lat) for Jupiter, where lat is in degrees.
 */


/*
 *  Calculate u(p,lat) for Saturn, where lat is in degrees.
 */


/*
 * Set u(p,lat) for Titan, where lat is in degrees.
 */


/*
 *  Calculate Uranus u(p,lat), where lat is in degrees.
 *
 *  Legendre polynomials (e.g. LeBeau and Dowling 1998, Icarus) fit to
 *  unbinned cloud measurment data from Sromovsky (2005, Icarus),
 *  by Michael Sussman.
 */


/*
 * Calculate Neptune u(p,lat), where lat is in degrees.
 */


/*
 * Returns nondimensional u(p) amplitude; units of p are Pa.
 * Useful for specifying the initial variation of zonal wind with pressure in a
 * separable manner.
 *
 * NOTE: The actual vertical shears in a given planet are not likely to be separable,
 *       so this is just used as a convenient way to specify an initial condition
 *       for the zonal wind.
 *
 * NOTE: The case grid.du_vert == 0. returns the pure barotropic value, 1.
 */


/*
 * Cassini CIRS profile for Saturn: dashed line in Fig. 12 of
 * Perez-Hoyos and Sanchez-Lavega (2006, Icarus 180, 161-175)
 * normalized to unity at p_ref.
 * Truncates to yield u >= 0.;
 * Assumes no shear for pressures deeper than p1.
 */


/*
 * Returns the value of pressure consistent with
 * theta, sigmatheta, pbot and ptop.
 */


/*
 * Turn on phases appropriate to choice of cloud microphysics package.
 * The phases are switched on, whether or not the species are invoked.
 *
 * The Palotai & Dowling cloud microphysics scheme uses five phases for each species.
 *
 */


/*
 * Set surface geopotential, phi, for terrestrial planets,
 * using spherical harmonic gravity and surface data.
 * The level phi = 0 corresponds to an appropriate reference radius.
 */


/*
 * Returns TRUE if entry->d_name contains the string "phi_surface.nc",
 * otherwise returns FALSE.
 */


/*
 * For the hybrid vertical coordinate, initialize p in the upper region
 * using grid.p_ref[kk], and THETA in the lower region using grid.theta_ref[kk].
 * Use the diagnostic values for P and THETA in the complementary regions.
 *
 * Assumes the surface pressure has already been set.
 */


/*
 * Use a root finder to solve for fp using the implicit equation:
 *   fp = fpe(T(p,theta,fp))
 * Global variables used to communicate with fpe_minus_fpe():
 */


/*
 * Top function for initializing optional species (condensables) in the atmosphere.
 *
 * Species are carried as Q(is,ip,K,J,I), where Q is the mass mixing ratio,
 * equal to mass_density_i/mass_density_dry. The indices "is" and "ip" are the species index
 * (e.g. CH_4_INDEX) and phase index (e.g. VAPOR), respectively.
 *
 * Synchronized with Q is X(is,ip,K,J,I), the mole fraction, equal to
 * number_density/number_density_total.  Notice the different denominators, 'dry' vs. 'total',
 * between Q and X.
 *
 * NOTE: Many textbooks use "q" for "specific humidity", which is density_i/density_total,
 *       and not identical to the mass mixing ratio.
 *
 * NOTE: Need to calculate required diagnostic variables like T here,
 *       since this function is called before they are set.
 */


/*
 * Assign volume mixing ratio (vmr, aka mole fraction) starting with a deep value,
 * which is assumed to be bounded by the saturation curve and a cold trap effect.
 */


/*
 * Assign volume mixing ratio (vmr, aka mole fraction) using observations.
 * The intent is to continually update this function to use state-of-the-art
 * observations for each planet and each species.
 */


/*
 * Relate molar mass to pressure to help improve init_with_u()'s
 * gradient balance for models with condensables.
 *
 * Requires initial call to setup_mu_p().
 */


/*====================== p_phi() =============================================*/
/*
 * Returns pressure at the bottom of the model given phi,
 * based on p vs z data file for specified planet.
 * For gas giants or the pure-isentropic vertical coordiante,
 * just return grid.pbot;
 */


/*
 * Create openmars_epic.nc by running initial.
 *
 * The input parameters are "hard wired" and need to be adjusted for each new application.
 */


/*
 * Input OpenMARS data into EPIC, compute diagnostic fields on isentropic surfaces,
 * and output results.
 */


/*
 * Create emars_epic.nc by running initial.
 *
 * The input parameters are "hard wired" and need to be adjusted for each new application.
 */


/*
 * Input EMARS data into EPIC, compute diagnostic fields on isentropic surfaces,
 * and output results.
 */


/*
 * Bootstrap weizmann_epic.nc by running initial.
 */


/*
 * Input Weizmann Institute data into EPIC, compute diagnostic fields on isentropic surfaces,
 * and output results.
 */


/*
 * Wrapper function for radiative-heating function specified
 * by grid.radiation_scheme.
 *
 * Values of input parameter action are:
 *   EPIC_ALLOC     (allocate memory before applying radiative heating)
 *   EPIC_APPLY     (apply radiative heating)
 *   EPIC_FREE      (free allocated memory when done)
 */


/*
 * Force temperature profile to the given radiative-equilibrium profile
 * at an appropriate rate.  Keep the layer-average heating zero if requested.
 *
 *
 * NOTE: Lateral boundary conditions, bc_lateral(), are not applied to HEAT array here.
 */


/*
 * Return radiative cooling time [s], given position K,J,I.
 * The value is for the bottom interface of layer K.
 */


/*
 * Inputs: pressure [Pa], latitude [deg], time [sec].
 *
 * time = 0.0 corresponds to northern spring equinox.
 */


/*
 * Takes a species name string (e.g., "NH_4SH") and returns
 * the number of distinct elements, their symbols, and how many atoms
 * of each are present.  Reallocates the necessary memory to hold this
 * information.
 *
 * NOTE: num_elements and **symbols shoud be declared static in the calling
 *       function, with their input values equal to the last call,
 *       in order properly reallocate memory.
 *
 */


/*
 * Returns B1 = T dB/dT.
 */


/*====================== sum_xx() ============================================*/
/*
 * Returns sum of 2nd virial coefficient, or related function,
 * with quadratic mole-fraction weighting appropriate to specified planet.
 */


/*
 * Apply lateral boundary conditions.
 */


/*
 * Partition of longwave wavelength regions:
 *
 *   FIR        0,    600 cm-1
 *   MIDIR    600,   1600 cm-1
 *
 * Input i_stride is used to subsample in the longitude direction,
 * to facilitate economical diurnal averaging.
 *
 * Fortran subroutine:
 *   heatingver12()
 */


/*
 * Free memory dynamically allocated in init_rt_longwave()
 */


/*
 * Fortran subroutine:
 *   firver3()
 */


/*
 * Partition of wavelength regions:
 *
 *   NIR     2000,   9500 cm-1
 *   VIS    10000,  40000 cm-1
 *   UV     40000, 100000 cm-1
 *
 * Input i_stride is used to subsample in the longitude direction,
 * to facilitate economical diurnal averaging.
 *
 * Fortran subroutine:
 *   heatingver12()
 */


/*
 * Free memory dynamically allocated in init_rt_shortwave()
 */


/*
 * cross section: cm^2
 *    wavenumber: cm-1
 *
 * Fortran subroutine: crosssectionrayleigh()
 */


/*
 * Input: lambda, wavelength [micron]
 * Output: solar irradiance [W/m2/micron]
 *
 * Using the Gueymard (2004, Solar Energy 76, 423-453) solar spectra.
 */


/*
 * Returns attenuation factor of insolation due to planet's rings for the given latitude,
 * averaged over the day side for the given planet latitude.
 *
 * Inputs:
 *            lat: planetocentric [deg]
 *   subsolar_lat: planetocentric [deg]
 *
 * NOTE: Currently implemented for Saturn only.
 *
 * Bruno Bezard derived the diurnally averaged equations:
 *     Bezard B, 1986, Variations saisonniere de la structure thermique
 *       et composition chimique de Jupiter, Saturn, et Uranus, Ph.D. thesis, Univ. Paris 7, Paris, France
 * A scan of the relevant pages may be found in the file
 *     $EPIC_PATH/data/saturn/archive/bezard_thesis_shadow.pdf.
 *
 * J. Moses and T. Greathouse implemented Bezard's algorithm as the Fortran shadow(), first used in
 *   Moses JI, Greathouse TK, 2005, Latitudinal and seasonal models of stratospheric photochemistry on
 *       Saturn: Comparison with infrared data from IRTF/TEXES, J. Geophys. Res., 110, E09007,
 *       doi:10.1029/2005JE002450
 *
 * T. Dowling converted the Fortran code to C, and removed redundancies in conditionals, June 2010
 *
 * Fortran subroutine:
 *   shadow()
 */


/*-------------------------------------------------------------------------------*
 * Plane-parallel discrete ordinates radiative transfer program                  *
 * C version                                                                     *
 * Fortran ftp site: ftp://climate.gsfc.nasa.gov/pub/wiscombe/Multiple_Scatt/    *
 *-------------------------------------------------------------------------------*

  Calling Tree (omitting calls to c_errmsg, c_dbl_vector, c_int_vector, c_free_dbl_vector):

  c_disort-+-c_self_test-+-c_disort_state_alloc
         |               +-c_disort_out_alloc
         |               +-c_disort_state_free
         |               +-c_disort_out_free
         +-c_check_inputs-+-(c_write_bad_var)
         |                +-c_dref
         +-c_disort_set-+-c_gaussian_quadrature
         +-c_print_inputs
         +-c_albtrans-+-c_legendre_poly
         |            +-c_solve_eigen-+-c_asymmetric_matrix
         |            +-c_interp_eigenvec
         |            +-c_set_matrix
         |            +-(c_sgbco)
         |            +-c_solve1-+-(c_sgbsl)
         |            +-c_atltrin
         |            +-c_albtrans_spherical
         |            +-c_print_albtrans
         +-c_planck_func1
         +-c_legendre_poly
         +-c_surface_bidir-+-c_gaussian_quadrature
         |                 +-c_bidir_reflectivity
         +-c_solve_eigen-+-c_asymmetric_matrix
	 +-c_set_coefficients_beam_source
	 +-c_interp_coefficients_beam_source
         +-c_upbeam_pseudo_spherical-+-(c_sgeco)
         |                           +-(c_sgesl)
         +-c_upbeam-+-(c_sgeco)
         |          +-(c_sgesl)
         +-c_upbeam_general_source-+-(c_sgeco)
         |                         +-(c_sgesl)
         +-c_upisot-+-(c_sgeco)
         |          +-(c_sgesl)
         +-c_interp_eigenvec
         +-c_interp_source
         +-c_set_matrix
         +-c_solve0-+-(c_sgbco)
         |          +-(c_sgbsl)
         +-c_fluxes
         +-c_user_intensities
         +-c_intensity_components
         +-c_print_avg_intensities
         +-c_ratio
         +-c_intensity_correction-+-c_single_scat
         |                        +-c_secondary_scat-+-c_xi_func
         +-c_new_intensity_correction-+-c_single_scat
         |                            +-prep_double_scat_integr
         |                            +-c_new_secondary_scat-+-c_xi_func
         |                            +-calc_phase_squared
         +-c_print_intensities

 +-------------------------------------------------------------------+

  Index conventions (for all loops and all variable descriptions):

  iu       :  for user polar angles
  iq,jq,kq :  for computational polar angles ('quadrature angles')
  iq/2     :  for half the computational polar angles (just the ones in either 0-90 degrees, or 90-180 degrees)
  j        :  for user azimuthal angles
  k,l      :  for Legendre expansion coefficients or, alternatively, subscripts of associated Legendre polynomials
  lu       :  for user levels
  lc       :  for computational layers (each having a different single-scatter albedo and/or phase function)
  lev      :  for computational levels
  mazim    :  for azimuthal components in Fourier cosine expansion of intensity and phase function

 +------------------------------------------------------------------+

               I N T E R N A L    V A R I A B L E S

   AMB(iq/2,iq/2)....First  matrix factor in reduced eigenvalue problem of eqs. SS(12), STWJ(8E), STWL(23f) (used only in solve_eigen);
                     ab[].zero (see cdisort.h)
   APB(iq/2,iq/2)....Second matrix factor in reduced eigenvalue problem of eqs. SS(12), STWJ(8E), STWL(23f) (used only in solve_eigen);
                     ab[].one (see cdisort.h)
   ARRAY(iq,iq)......Scratch matrix for solve_eigen(), upbeam() and upisot()
                     (see each subroutine for definition)
   B()...............Right-hand side vector of eq. SC(5) going into SOLVE0,1;
                     returns as solution vector vector  L, the constants of integration
   BDR(iq/2,0:iq/2)..Bottom-boundary bidirectional reflectivity for a given azimuthal component.  First index always
                     refers to a computational angle.  Second index: if zero, refers to incident beam angle UMU0;
                     if non-zero, refers to a computational angle.
   BEM(iq/2).........Bottom-boundary directional emissivity at computational angles.
   bplanck...........Intensity emitted from bottom boundary
   callnum...........Number of surface calls
   CBAND()...........Matrix of left-hand side of the linear system eq. SC(5), scaled by eq. SC(12);
                     in banded form required by LINPACK solution routines
   CC(iq,iq).........C-sub-IJ in eq. SS(5)
   CH(lc)............The Chapman-factor to correct for pseudo-spherical geometry in the direct beam.
   CHTAU(lc).........The optical depth in spherical geometry.
   CMU(iq)...........Computational polar angles (Gaussian)
   CWT(iq)...........Quadrature weights corresponding to CMU
   corint............When set TRUE, correct intensities for delta-scaling effects (see Nakajima and Tanaka, 1988).
                     When FALSE, intensities are not corrected. In general, CORINT should be set true when beam
                     source is present (FBEAM is not zero) and DELTAM is TRUE in a problem including scattering.
                     However, execution is faster when CORINT is FALSE, and intensities outside the aureole may still be
                     accurate enough.  When CORINT is TRUE, it is important to have a sufficiently high order of
                     Legendre approximation of the phase function. This is because the intensities are corrected by
                     calculating the single-scattered radiation, for which an adequate representation of the phase
                     function is crucial.  In case of a low order Legendre approximation of an otherwise highly
                     anisotropic phase function, the intensities might actually be more accurate when corint is FALSE.
                     When only fluxes are calculated (ds->flag.onlyfl is TRUE), or there is no beam source (FBEAM=0.0), or there
                     is no scattering (SSALB =0. for all layers) corint is set FALSE by the code.
   delm0.............Kronecker delta, delta-sub-M0, where M = MAZIM is the number of the Fourier component in the
                     azimuth cosine expansion
   deltam............TRUE,  use delta-M method ( see Wiscombe, 1977 );
                     FALSE, do not use delta-M method.
                     In general, for a given number of streams, intensities and fluxes will be more accurate for phase functions
                     with a large forward peak if DELTAM is set true. Intensities close to the forward scattering
                     direction are often less accurate, however, when the delta-M method is applied. The intensity deltam
                     correction of Nakajima and Tanaka is used to improve the accuracy of the intensities.
   dither............Small quantity subtracted from single-scattering albedos of unity, in order to avoid using special
                     case formulas;  prevents an eigenvalue of exactly zero from occurring, which would cause an immediate overflow
   DTAUCPR(lc).......Computational-layer optical depths (delta-M-scaled if DELTAM = TRUE, otherwise equal to DTAUC)
   EMU(iu)...........Bottom-boundary directional emissivity at user angles.
   EVAL(iq)..........Temporary storage for eigenvalues of eq. SS(12)
   EVECC(iq,iq)......Complete eigenvectors of SS(7) on return from solve_eigen; stored permanently in  GC
   EXPBEA(lc)........Transmission of direct beam in delta-M optical depth coordinates
   FLYR(lc)..........Separated fraction in delta-M method
   GL(k,lc)..........Phase function Legendre polynomial expansion coefficients, calculated from PMOM by
                     including single-scattering albedo, factor 2K+1, and (if DELTAM=TRUE) the delta-M scaling
   GC(iq,iq,lc)......Eigenvectors at polar quadrature angles, g in eq. SC(1)
   GU(iu,iq,lc)......Eigenvectors interpolated to user polar angles (g  in eqs. SC(3) and S1(8-9), i.e. g without the l factor)
   IPVT(lc*iq).......Integer vector of pivot indices for LINPACK routines
   KK(iq,lc).........Eigenvalues of coeff. matrix in eq. SS(7)
   kconv.............Counter in azimuth convergence test
   LAYRU(lu).........Computational layer in which user output level UTAU(LU) is located
   LL(iq,lc).........Constants of integration L in eq. SC(1), obtained by solving scaled version of eq. SC(5)
   lyrcut............TRUE, radiation is assumed zero below layer ncut because of almost complete absorption
   naz...............Number of azimuthal components considered
   ncut..............Computational layer number in which absorption optical depth first exceeds ABSCUT
   OPRIM(lc).........Single scattering albedo after delta-M scaling
   pass1.............TRUE on first entry, FALSE thereafter
   PKAG(0:lc)........Integrated Planck function for internal emission
   PRNTU0(l).........logical flag to trigger printing of azimuthally-averaged intensities:
                       l    quantities printed
                      --    ------------------
                       0    azimuthally-averaged intensities at user
                               levels and computational polar angles
                       1    azimuthally-averaged intensities at user
                               levels and user polar angles
   PSI0(iq)..........Sum just after square bracket in  eq. SD(9); psi[].zero (see cdisort.h)
   PSI1(iq)..........Sum in  eq. STWL(31d); psi[].one
   RMU(iu,0:iq)......Bottom-boundary bidirectional reflectivity for a given azimuthal component.  First index always
                     refers to a user angle.  Second index: if zero, refers to incident beam angle UMU0;
                     if non-zero, refers to a computational angle.
   scat_yes..........int, TRUE if scattering, FALSE if not (added to C version)
   TAUC(0:lc)........Cumulative optical depth (un-delta-M-scaled)
   TAUCPR(0:lc)......Cumulative optical depth (delta-M-scaled if DELTAM = TRUE, otherwise equal to TAUC)
   tplanck...........Intensity emitted from top boundary
   UUM(iu,lu)........Expansion coefficients when the intensity (u-super-M) is expanded in Fourier cosine series
                     in azimuth angle
   U0C(iq,lu)........Azimuthally-averaged intensity at quadrature angle
   U0U(iu,lu)........If ds->flag.onlyfl = FALSE, azimuthally-averaged intensity at user angles and user levels
                     If ds->flag.onlyfl = TRUE, azimuthally-averaged intensity at computational
                     (Gaussian quadrature) angles and user levels; the corresponding quadrature angle cosines are
                     returned in UMU.
   UTAUPR(lu)........Optical depths of user output levels in delta-M coordinates; equal to UTAU(LU) if no delta-M
   WK(iq)............Scratch array
   XR0(lc)...........X-sub-zero in expansion of thermal source function preceding eq. SS(14)(has no mu-dependence); b-sub-zero in eq. STWL(24d)
   XR1(lc)...........X-sub-one in expansion of thermal source function; see eqs. SS(14-16); b-sub-one in STWL(24d)
   YLM0(l)...........Normalized associated Legendre polynomial of subscript L at the beam angle (not saved
                     as function of superscipt M)
   YLMC(l,iq)........Normalized associated Legendre polynomial of subscript L at the computational angles
                     (not saved as function of superscipt M)
   YLMU(l,iu)........Normalized associated Legendre polynomial of subscript L at the user angles
                     (not saved as function of superscipt M)
   Z()...............scratch array used in solve0(), albtrans() to solve a linear system for the constants of integration
   Z0(iq)............Solution vectors Z-sub-zero of eq. SS(16); zee[].zero (see cdisort.h)
   Z1(iq)............Solution vectors Z-sub-one  of eq. SS(16); zee[].one
   Z0U(iu,lc)........Z-sub-zero in eq. SS(16) interpolated to user angles from an equation derived from SS(16); zu[].zero (see cdisort.h)
   Z1U(iu,lc)........Z-sub-one  in eq. SS(16) interpolated to user angles from an equation derived from SS(16); zu[].one
   ZBEAM(iu,lc)......Particular solution for beam source
   ZGU(iu,lc)........General source function interpolated to user angles
   ZJ(iq)............Right-hand side vector  X-sub-zero in eq. SS(19), also the solution vector
                     Z-sub-zero after solving that system
   ZJG(iq)...........Right-hand side vector  X-sub-zero in eq. KS(10), also the solution vector
                     Z-sub-zero after solving that system for a general source constant over a layer
   ZZ(iq,lc).........Permanent storage for the beam source vectors ZJ
   ZZG(iq,lc)........Permanent storage for the beam source vectors ZJG
   ZPLK0(iq,lc)......Permanent storage for the thermal source vectors plk[].zero obtained by solving eq. SS(16)
   ZPLK1(iq,lc)......Permanent storage for the thermal source vectors plk[].one  obtained by solving eq. SS(16)

*/


/*
    Calculates the radiative fluxes, mean intensity, and flux
    derivative with respect to optical depth from the m=0 intensity
    components (the azimuthally-averaged intensity)

    I N P U T    V A R I A B L E S:

       ds       :  Disort state variables
       cmu      :  Abscissae for Gauss quadrature over angle cosine
       cwt      :  Weights for Gauss quadrature over angle cosine
       gc       :  Eigenvectors at polar quadrature angles, SC(1)
       kk       :  Eigenvalues of coeff. matrix in eq. SS(7), STWL(23b)
       layru    :  Layer number of user level UTAU
       ll       :  Constants of integration in eq. SC(1), obtained by solving scaled version of eq. SC(5);
                   exponential term of eq. SC(12) not included
       lyrcut   :  Logical flag for truncation of comput. layer
       ncut     :  Number of computational layer where absorption optical depth exceeds ABSCUT
       nn       :  Order of double-Gauss quadrature (NSTR/2)
       prntu0   :  TRUE, print azimuthally-averaged intensity at quadrature angles
       taucpr   :  Cumulative optical depth (delta-M-scaled)
       utaupr   :  Optical depths of user output levels in delta-M coordinates;  equal to UTAU if no delta-M
       xr       :  Expansion of thermal source function in eq. SS(14,16), STWL(24c); xr[].zero, xr[].one (see cdisort.h)
       zz       :  Beam source vectors in eq. SS(19), STWL(24b)
       zzg      :  Beam source vectors in eq. KS(10)for a general source constant over a layer
       plk      :  Thermal source vectors z0,z1 by solving eq. SS(16), Y0,Y1 in STWL(26b,a);
                   plk[].zero, plk[].one (see cdisort.h)

    O U T P U T    V A R I A B L E S:

       out      : Disort output variables
       u0c      :  Azimuthally averaged intensities (at polar quadrature angles)

    I N T E R N A L    V A R I A B L E S:

       dirint   :  Direct intensity attenuated
       fdntot   :  Total downward flux (direct + diffuse)
       fl       :  fl[].zero: 'fldir' = direct-beam flux (delta-M scaled), fl[].one 'fldn' = diffuse down-flux (delta-M scaled)
       fnet     :  Net flux (total_down-diffuse_up)
       fact     :  EXP(- UTAUPR/UMU0)
       plsorc   :  Planck source function (thermal)
       zint     :  Intensity of m = 0 case, in eq. SC(1)

   Called by- c_disort
 -------------------------------------------------------------------*/


/*
        Construct right-hand side vector B for general boundary
        conditions STWJ(17) and solve system of equations obtained
        from the boundary conditions and the continuity-of-
        intensity-at-layer-interface equations.
        Thermal emission contributes only in azimuthal independence.

    I N P U T      V A R I A B L E S:

       ds       :  Disort input variables
       bdr      :  Surface bidirectional reflectivity
       bem      :  Surface bidirectional emissivity
       bplanck  :  Bottom boundary thermal emission
       cband    :  Left-hand side matrix of linear system eq. SC(5),
                   scaled by eq. SC(12); in banded form required
                   by LINPACK solution routines
       cmu,cwt  :  Abscissae, weights for Gauss quadrature
                   over angle cosine
       expbea   :  Transmission of incident beam, EXP(-TAUCPR/UMU0)
       lyrcut   :  Logical flag for truncation of computational layers
       mazim    :  Order of azimuthal component
       ncol     :  Number of columns in CBAND
       nn       :  Order of double-Gauss quadrature (NSTR/2)
       ncut     :  Total number of computational layers considered
       tplanck  :  Top boundary thermal emission
       taucpr   :  Cumulative optical depth (delta-M-scaled)
       zz       :  Beam source vectors in eq. SS(19), STWL(24b)
       zzg      :  Beam source vectors in eq. KS(10)for a general source constant over a layer
       plk      :  Thermal source vectors z0,z1 by solving eq. SS(16), Y0,Y1 in STWL(26b,a);
                   plk[].zero, plk[].one (see cdisort.h)

    O U T P U T     V A R I A B L E S:

       b        :  Right-hand side vector of eq. SC(5) going into
                   sgbsl; returns as solution vector of eq. SC(12),
                   constants of integration without exponential term
      ll        :  Permanent storage for B, but re-ordered

   I N T E R N A L    V A R I A B L E S:

       ipvt     :  Integer vector of pivot indices
       it       :  Pointer for position in  B
       ncd      :  Number of diagonals below or above main diagonal
       rcond    :  Indicator of singularity for cband
       z        :  Scratch array required by sgbco

   Called by- c_disort
   Calls- c_sgbco, c_errmsg, c_sgbsl
 +-------------------------------------------------------------------*/


/*
    Interpolates source functions to user angles, eq. STWL(30)

    I N P U T      V A R I A B L E S:

       ds     :  Disort state variables
       cwt    :  Weights for Gauss quadrature over angle cosine
       delm0  :  Kronecker delta, delta-sub-m0
       gl     :  Delta-M scaled Legendre coefficients of phase function
                 (including factors 2l+1 and single-scatter albedo)
       mazim  :  Order of azimuthal component
       oprim  :  Single scattering albedo
       xr     :  Expansion of thermal source function, eq. STWL(24d); xr[].zero, xr[].one (see cdisort.h)
       ylm0   :  Normalized associated Legendre polynomial at the beam angle
       ylmc   :  Normalized associated Legendre polynomial at the quadrature angles
       ylmu   :  Normalized associated Legendre polynomial at the user angles
       zbs0   :  Solution vectors z-sub-zero of Eq. KS(10-11), used if pseudo-spherical
       zbs1   :  Solution vectors z-sub-one  of Eq. KS(10-11), used if pseudo-spherical
       zbsa   :  Alfa coefficient in Eq. KS(7), used if pseudo-spherical
       zee    :  Solution vectors Z-sub-zero, Z-sub-one of eq. SS(16), STWL(26a,b)
       zj     :  Solution vector Z-sub-zero after solving eq. SS(19), STWL(24b)
       zjg    :  Right-hand side vector  X-sub-zero in eq. KS(10), also the solution vector
                 Z-sub-zero after solving that system for a general source constant over a layer

    O U T P U T     V A R I A B L E S:

       zbeam  :  Incident-beam source function at user angles
       zu     :  Components 0 and 1 of a linear-in-optical-depth-dependent source (approximating the Planck emission source)
       zgu    :  General source function at user angles

   I N T E R N A L    V A R I A B L E S:

       psi  :   psi[].zero: Sum just after square bracket in eq. SD(9)
                psi[].one:  Sum in eq. STWL(31d)

   Called by- c_disort
 -------------------------------------------------------------------*/


/*
   Computes intensity components at user output angles for azimuthal
   expansion terms in eq. SD(2), STWL(6)

   I N P U T    V A R I A B L E S:

       ds     :  Disort state variables
       bplanck:  Integrated Planck function for emission from
                 bottom boundary
       cmu    :  Abscissae for Gauss quadrature over angle cosine
       cwt    :  Weights for Gauss quadrature over angle cosine
       delm0  :  Kronecker delta, delta-sub-M0
       emu    :  Surface directional emissivity (user angles)
       expbea :  Transmission of incident beam, EXP(-TAUCPR/UMU0)
       gc     :  Eigenvectors at polar quadrature angles, SC(1)
       gu     :  Eigenvectors interpolated to user polar angles
                    (i.e., G in eq. SC(1) )
       kk     :  Eigenvalues of coeff. matrix in eq. SS(7), STWL(23b)
       layru  :  Layer number of user level UTAU
       ll     :  Constants of integration in eq. SC(1), obtained
                 by solving scaled version of eq. SC(5);
                 exponential term of eq. SC(12) not included
       lyrcut :  Logical flag for truncation of computational layer
       mazim  :  Order of azimuthal component
       ncut   :  Total number of computational layers considered
       nn     :  Order of double-Gauss quadrature (NSTR/2)
       rmu    :  Surface bidirectional reflectivity (user angles)
       taucpr :  Cumulative optical depth (delta-M-Scaled)
       tplanck:  Integrated Planck function for emission from
                 top boundary
       utaupr :  Optical depths of user output levels in delta-M
                 coordinates;  equal to UTAU if no delta-M
       zgu    :  General source function at user angles
       zu     :  Z-sub-zero, Z-sub-one in eq. SS(16) interpolated to user angles from an equation derived from SS(16),
                 Y-sub-zero, Y-sub-one on STWL(26b,a); zu[].zero, zu[].one (see cdisort.h)
       zz     :  Beam source vectors in eq. SS(19), STWL(24b)
       zzg    :  Beam source vectors in eq. KS(10)for a general source constant over a layer
       plk    :  Thermal source vectors z0,z1 by solving eq. SS(16),
                 Y-sub-zero,Y-sub-one in STWL(26)
       zbeam  :  Incident-beam source vectors

    O U T P U T    V A R I A B L E S:

       uum    :  Azimuthal components of the intensity in eq. STWJ(5),
                 STWL(6)

    I N T E R N A L    V A R I A B L E S:

       bnddir :  Direct intensity down at the bottom boundary
       bnddfu :  Diffuse intensity down at the bottom boundary
       bndint :  Intensity attenuated at both boundaries, STWJ(25-6)
       dtau   :  Optical depth of a computational layer
       lyrend :  End layer of integration
       lyrstr :  Start layer of integration
       palint :  Intensity component from parallel beam
       plkint :  Intensity component from planck source
       wk     :  Scratch vector for saving exp evaluations

       All the exponential factors (exp1, expn,... etc.)
       come from the substitution of constants of integration in
       eq. SC(12) into eqs. S1(8-9).  They all have negative
       arguments so there should never be overflow problems.

   Called by- c_disort
 -------------------------------------------------------------------*/


/*
     Factors a real band matrix by Gaussian elimination and estimates the
     condition of the matrix.
     Revision date:  8/1/82
     Author:  Moler, C.B. (Univ. of New Mexico)

     If  RCOND  is not needed, sgbfa is slightly faster.
     To solve  A*X = B , follow sgbco by sgbsl.

     Inputs:
        abd     double(LDA,N), contains the matrix in band storage.
                The columns of the matrix are stored in the columns of abd
                and the diagonals of the matrix are stored in rows
                ml+1 through 2*ml+mu+1 of  abd.
                See the comments below for details.
        lda     int, the leading dimension of the array abd.
                lda must be >= 2*ml+mu+1.
        n       int,the order of the original matrix.
        ml      int, number of diagonals below the main diagonal.
                0 <= ml < n.
        mu      int, number of diagonals above the main diagonal.
                0 <= mu < n.
                more efficient if  ml <= mu.

     Outputs:
        abd     an upper triangular matrix in band storage and
                the multipliers which were used to obtain it.
                The factorization can be written  A = L*U  where
                L  is a product of permutation and unit lower
                triangular matrices and  U  is upper triangular.
        ipvt    int[n], an integer vector of pivot indices.
        rcond   double, an estimate of the reciprocal condition of A.
                For the system  A*X = B, relative perturbations
                in A and B of size epsilon may cause relative
                perturbations in  X  of size  epsilon/rcond.
                If rcond  is so small that the logical expression
                   1.+RCOND == 1.
                is true, then  A  may be singular to working
                precision.  In particular, rcond is zero if exact
                singularity is detected or the estimate underflows.
        z       double[n], a work vector whose contents are usually
                unimportant. If A is close to a singular matrix, then
                z is an approximate null vector in the sense that
                norm(a*z) = rcond*norm(a)*norm(z).

     Band storage:
           If A is a band matrix, the following program segment
           will set up the input (with unit-offset arrays):
                   ml = (band width below the diagonal)
                   mu = (band width above the diagonal)
                   m = ml+mu+1
                   for (j = 1; j <= n; j++) {
                     i1 = IMAX(1,j-mu);
                     i2 = IMIN(n,j+ml);
                     for (i = i1; i <= i2; i++) {
                       k = i-j+m;
                       ABD(K,J) = A(I,J);
                     }
                   }
           This uses rows ml+1 through 2*ml+mu+1 of abd.
           In addition, the first ml rows in abd are used for
           elements generated during the triangularization.
           The total number of rows needed in abd is 2*ml+mu+1.
           The ml+mu by ml+mu upper left triangle and the
           ml by ml lower right triangle are not referenced.

     Example:  if the original matrix is

           11 12 13  0  0  0
           21 22 23 24  0  0
            0 32 33 34 35  0
            0  0 43 44 45 46
            0  0  0 54 55 56
            0  0  0  0 65 66

      then  n = 6, ml = 1, mu = 2, lda >= 5  and abd should contain
            *  *  *  +  +  +  , * = not used
            *  * 13 24 35 46  , + = used for pivoting
            * 12 23 34 45 56
           11 22 33 44 55 66
           21 32 43 54 65  *

 --------------------------------------------------------------------*/


/*
    Factors a real band matrix by elimination.
    Revision date:  8/1/82
    Author:  Moler, C. B. (U. of New Mexico)
    c_sgbfa is usually called by c_sgbco, but it can be called
    directly with a saving in time if rcond is not needed.

    Inputs:  same as c_sgbco
    Outputs:
        abd,ipvt    same as c_sgbco
        info    int,
                = 0  normal value.
                = k  if  u(k,k) == 0.  This is not an error
                     condition for this subroutine, but it does
                     indicate that sgbsl will divide by zero if
                     called.  Use  rcond  in c_sgbco for a reliable
                     indication of singularity.
    (see c_sgbco for description of band storage mode)

    NOTE: using memset() to zero columns in abd
 ----------------------------------------------------------------*/


/*
    Solves the real band system
       A * X = B  or  transpose(A) * X = B
    using the factors computed by sgbco or sgbfa.
    Revision date:  8/1/82
    Author:  Moler, C. B. (Univ. of New Mexico)

    Inputs:
        abd     double(lda, n), the output from sgbco or sgbfa.
        lda     int, the leading dimension of the array abd.
        n       int, the order of the original matrix.
        ml      int, number of diagonals below the main diagonal.
        mu      int, number of diagonals above the main diagonal.
        ipvt    int(n), the pivot vector from sgbco or sgbfa.
        b       double(n), the right hand side vector.
        job     int,
                = 0         to solve  A*X = B ,
                = nonzero   to solve  transpose(A)*X = B

     Outputs:
        b       the solution vector  X

     Error condition:
        A division by zero will occur if the input factor contains a
        zero on the diagonal.  Technically, this indicates singularity,
        but it is often caused by improper arguments or improper
        setting of lda.  It will not occur if the subroutines are
        called correctly and if c_sgbco has set rcond > 0.0
        or sgbfa has set info = 0 .
     To compute  inverse(a)*c  where c is a matrix
     with p columns
      c_sgbco(abd,lda,n,ml,mu,ipvt,&rcond,z)
      if (rcond is too small) ...
        for (j = 1; j <= p; j++) {
          c_sgbsl(abd,lda,n,ml,mu,ipvt,c(1,j),0)
        }
 --------------------------------------------------------*/


/*============================= end of c_chapman() =======================*/


/*
 * Checks the twostr input dimensions and variables
 */


/*
 Perform miscellaneous setting-up operations

 Routines called: c_errmsg

 Input :  ds         'Disort' input variables

 Output:  ntau,utau  If ds->flag.usrtau = FALSE
          bplanck    Intensity emitted from bottom boundary
          ch         The Chapman factor
          cmu        Computational polar angle
          expbea     Transmission of direct beam
          flyr       Truncated fraction in delta-m method
          layru      Computational layer in which utau falls
          lyrcut     Flag as to whether radiation will be zeroed below layer ncut
          ncut       Computational layer where absorption optical depth first exceeds abscut
          nn         nstr/2 = 1
          nstr       No.of streams (=2)
          oprim      Delta-m-scaled single-scatter albedo
          pkag,c     Planck function in each layer
          taucpr     Delta-m-scaled optical depth
          tplanck    Intensity emitted from top boundary
          utaupr     Delta-m-scaled version of utau

 Internal Variables
          abscut     Absorption optical depth, medium is cut off below this depth
          tempc      Temperature at center of layer, assumed to be average of
                     layer boundary temperatures
  ---------------------------------------------------------------------*/


/*
 * Frees memory allocated by dbl_vector().
 *
 * NOTE: If the array is zero-offset, can just use free().
 * NOTE: Argument nh is not used, but kept to match dbl_vector().
 */


/*
 * Read in float, or set to default if input is a return ('\n').
 * C.Santori, T.Dowling
 */


/* This function finds the size of a file. */


/* Subtract the `struct timeval' values X and Y, storing the result in
   RESULT.  Return 1 if the difference is negative, otherwise 0.  This
   function from the GNU documentation. */


/* Check attribute number a of variable varid in copied file ncid2 to ensure
 * it is the same as the corresponding attribute in original file ncid1. */


/* Copy a netCDF file, changing cmode if desired, applying chuncking,
 * deflate, shuffle, and endianness parameters if desired. */


/*
 * Convert a C dimension-ID vector into a FORTRAN dimension-ID vector.
 */


/*
 * Convert a FORTRAN dimension-ID vector into a C dimension-ID vector.
 */


/* Convert a C dimension-ID vector into a FORTRAN dimension-ID vector. */


/* Convert a FORTRAN dimension-ID vector into a C dimension-ID vector. */


/*
 * Convert FORTRAN co-ordinates into C co-ordinates.
 */


/*
 * Convert FORTRAN counts into C counts.
 */


/*
 * Convert FORTRAN strides into C strides.
 *
 * Helper function.
 */


/*
 * Convert a FORTRAN mapping vector into a C mapping vector.
 */


/* Get the varids for a fortran function (i.e. add 1 to each
 * varid.) */


/* Get the dimids for a fortran function (i.e. add 1 to each
 * dimid.) */


/* Swap the dim sizes for fortran. */


/**
 * Convert a Version 2 Fortran IMAP vector into a Version 3 C imap vector.
 */


/*
 * Compute the product of dimensional counts.
 */


/*
 * Set the C global variable ncopts.
 */


/*
 *  ensure hash value within range for internal TYPE
 */


/*
 * Use for logging error conditions
 */


/*
 * Use for general printing (other than error conditions)
 * This also currently goes to stderr, but this could change
 */


/*
 * Called by macro IF
 */


/* TODO:
 * This diagnostic doesn't fit very well with the diagnostic message
 * "architecture" of this program.
 */


/* wrapper for inRange to handle special NC_BYTE/uchar adjustment */


/*
 *  Does x == y, where one is internal and other external (netCDF)?
 *  Use tolerant comparison based on IEEE FLT_EPSILON or DBL_EPSILON.
 */


/* Test whether two int vectors are equal. If so return 1, else 0  */


/*
 *  Generate random integer from 0 to n-1
 *  Like throwing an n-sided dice marked 0, 1, 2, ..., n-1
 */


/*
 *      Convert number to mixed base
 *
 *      E.g. to convert 41 inches to yards, feet and inches:
 *      size_t base[] = {1, 3, 12};
 *      size_t result[3];
 *      status = toMixedBase(41, 3, base, result);
 *
 *      Author: Harvey Davies, Unidata/UCAR, Boulder, Colorado
 */


/*
 *      Convert number from mixed base
 *
 *      E.g. to convert 1 yard, 0 feet, 5 inches to inches:
 *      size_t number[] = {1, 0, 5};
 *      size_t base[] = {1, 3, 12};
 *      inches = fromMixedBase(3, number, base);
 *
 *      Author: Harvey Davies, Unidata/UCAR, Boulder, Colorado
 */


/* Convert any nc_type to double */


/* Convert double to any nc_type */


/* Generate data values as function of type, rank (-1 for attribute), index */


/* wrapper for hash to handle special NC_BYTE/uchar adjustment */


/*
   define global variables:
   dim_name, dim_len,
   var_name, var_type, var_rank, var_shape, var_natts, var_dimid, var_nels
   att_name, gatt_name, att_type, gatt_type, att_len, gatt_len
 */


/* TODO: Maybe this function belongs in the netcdf library. */


/*
 * "Expands" valid escape sequences in yystring (read by lex) into the
 * apropriate characters in termstring.  For example, the two character
 * sequence "\t" in yystring would be converted into a single tab character
 * in termstring.  On return, termstring is properly terminated.
 */


/* strip off leading path */


/* Symbol table operations for ncgen tool */


/*
 * Remove trailing zeros (after decimal point) but not trailing decimal
 * point from ss, a string representation of a floating-point number that
 * might include an exponent part.
 */


/* generate C to put netCDF record from in-memory data */


/*
 * Add to a partial Fortran statement, checking if it's too long.  If it is too
 * long, output the first part of it as a single statement with continuation
 * characters and start a new (probably invalid) statement with the remainder.
 * This will cause a Fortran compiler error, but at least all the information
 * will be available.
 */


/*
 * Create Fortran data statement to initialize numeric variable with
 * values.
 */


/* make Fortran to put record */


/* invoke netcdf calls (or generate C or Fortran code) to load netcdf variable
 * from in-memory data.  Assumes following global variables set from yacc
 * parser:
 * int varnum        - number of variable to be loaded.
 * struct vars[varnum] - structure containing info on variable, specifically
 *                     name, type, ndims, dims, fill_value, has_data
 * int rec_dim       - id of record dimension, or -1 if none
 * struct dims[]     - structure containing name and size of dimensions.
 */


/* write out variable's data from in-memory structure */


/*
 * Given netCDF type, return a default fill_value appropriate for
 * that type.
 */


/*
 * Given netCDF type, put a value of that type into a fill_value
 */


/* table of variable and global attributes */


/* yy_get_previous_state - get the state just before the EOB char was reached */


/* create netCDF from in-memory structure */


/*
 * Given a netcdf type, a pointer to a vector of values of that type,
 * and the index of the vector element desired, returns a pointer to a
 * malloced string representing the value in C.
 */


/*
 * Generate C code for creating netCDF from in-memory structure.
 */


/* return Fortran type name for netCDF type, given type code */


/* return Fortran type suffix for netCDF type, given type code */


/* Return Fortran function suffix for netCDF type, given type code.
 * This should correspond to the Fortran type name in ncftype().
 */


/* return FORTRAN name for netCDF type, given type code */


/*
 * Generate FORTRAN code for creating netCDF from in-memory structure.
 */


/*
 * Output a C statement.
 */


/*
 * From a long line FORTRAN statment, generates the necessary FORTRAN
 * lines with continuation characters in column 6.  If stmnt starts with "*",
 * it is treated as a one-line comment.  Statement labels are *not* handled,
 * but since we don't generate any labels, we don't care.
 */


/* return C name for netCDF type, given type code */


/*
 * Return C type name for netCDF type, given type code.
 */


/*
 * Return C type name for netCDF type suffix, given type code.
 */


/*
 * Return C type name for netCDF attribute container type, given type code.
 */


/* return internal size for values of specified netCDF type */


/*
 * Given a netcdf numeric type, a pointer to a vector of values of that
 * type, and the index of the vector element desired, returns a pointer
 * to a malloced string representing the value in FORTRAN.  Since this
 * may be used in a DATA statement, it must not include non-constant
 * expressions, such as "char(26)".
 */


/*
 * Given a pointer to a counted string, returns a pointer to a malloced string
 * representing the string as a C constant.
 */


/* Given a pointer to a counted string (not necessarily
 * null-terminated), returns a pointer to a malloced string representing
 * the string as a FORTRAN string expression.  For example, the string
 * "don't" would yield the FORTRAN string "'don''t'", and the string
 * "ab\ncd" would yield "'ab'//char(10)//'cd'".  The common
 * interpretation of "\"-escaped characters is non-standard, so the
 * generated Fortran may require adjustment in compilers that don't
 * recognize "\" as anything special in a character context.  */


/* Returns true if dimension used in at least one record variable,
  otherwise false.  This is an inefficient algorithm, but we don't call
  it very often ... */


/* Return name for Fortran fill constant of specified type */


/* Generate Fortran for cleaning up and closing file */


/* invoke netcdf calls (or generate C or Fortran code) to create netcdf
 * from in-memory structure. */


/*
 * For generated Fortran, change 'e' to 'd' in exponent of double precision
 * constants.
 */


/* Returns non-zero if n is a power of 2, 0 otherwise */


/*
 * Grow an integer array as necessary.
 *
 * Assumption: nar never incremented by more than 1 from last call.
 *
 * Makes sure an array is within a factor of 2 of the size needed.
 *
 * Make sure *arpp points to enough space to hold nar integers.  If not big
 * enough, malloc more space, copy over existing stuff, free old.  When
 * called for first time, *arpp assumed to be uninitialized.
 */


/*
 * Grow an array of variables as necessary.
 *
 * Assumption: nar never incremented by more than 1 from last call.
 *
 * Makes sure array is within a factor of 2 of the size needed.
 *
 * Make sure *arpp points to enough space to hold nar variables.  If not big
 * enough, malloc more space, copy over existing stuff, free old.  When
 * called for first time, *arpp assumed to be uninitialized.
 */


/*
 * Grow an array of dimensions as necessary.
 *
 * Assumption: nar never incremented by more than 1 from last call.
 *
 * Makes sure array is within a factor of 2 of the size needed.
 *
 * Make sure *arpp points to enough space to hold nar dimensions.  If not big
 * enough, malloc more space, copy over existing stuff, free old.  When
 * called for first time, *arpp assumed to be uninitialized.
 */


/*
 * Grow an array of attributes as necessary.
 *
 * Assumption: nar never incremented by more than 1 from last call.
 *
 * Makes sure array is within a factor of 2 of the size needed.
 *
 * Make sure *arpp points to enough space to hold nar attributes.  If not big
 * enough, malloc more space, copy over existing stuff, free old.  When
 * called for first time, *arpp assumed to be uninitialized.
 */


/*
 * Replace special chars in name so it can be used in C and Fortran
 * variable names without causing syntax errors.  Here we just replace
 * each "-" in a name with "_MINUS_", each "." with "_PERIOD_", etc.
 * For bytes with high bit set, from UTF-8 encoding of Unicode, just
 * replace with "_xHH", where each H is the appropriate hex digit.
 */


/*
 * Replace escaped chars in CDL representation of name such as
 * 'abc\:def\ gh\\i' with unescaped version, such as 'abc:def gh\i'.
 */


/*
 * Print error message to stderr and exit
 */


/* initialize safe buffer */


/* grow buffer to at least len bytes, copying previous contents if
 * necessary */


/* Copy string s2 to safe buffer, growing if necessary */


/* Concatenate string s2 to end of string in safe buffer, growing if necessary */


/* Concatenate string in safebuf s2 to end of string in safebuf s1,
 * growing if necessary */


/* Return length of string in sbuf */


/* Return C string in an sbuf */


/* free safe buffer */


/*
 * Get a new, empty variable list.
 */


/*
 * return 1 if variable identified by varid is member of variable
 * list vlist points to.
 */


/* Return primitive type name */


/* return number of user-defined types in a group and all its subgroups */


/* void */
/* xfree_typeinfo(int ncid) { */
/*     int i; */
/*     for (i = 0; i < number_of_types; i++) { */
/* 	nctype_t *tinfop = nctypes[i]; */
/* 	if (tinfop) { */
/* 	    if(tinfop->name) */
/* 		free(tinfop->name); */
/* 	    if(tinfop->grps) */
/* 		free(tinfop->grps); */
/* 	    free(tinfop); */
/* 	} */
/*     } */
/* } */


/*
 * Return ( *(float* )v1p == *(float* )v2p);
 * except use floating epsilon to compare very close vals as equal
 * and handle IEEE NaNs and infinities.
 */


/*
 * Return ( *(double* )v1p == *(double* )v2p);
 * except use floating epsilon to compare very close vals as equal
 * and handle IEEE NaNs and infinities.
 */


/* Determine if two compound values are equal, by testing eqaulity of
 * each member field. */


/* Convert a float NaN or Infinity to an allocated string large enough
 * to hold it (at least PRIM_LEN chars) */


/* Convert a double NaN or Infinity to an allocated string large enough
 * to hold it (at least PRIM_LEN chars) */


/* Given an opaque type size and opaque value, convert to a string,
 * represented as hexadecimal characters, returning number of chars in
 * output string */


/* Convert an opaque value to a string, represented as hexadecimal
 * characters */


/* Convert a vlen value to a string, by using tostring function for base type */


/* Convert a compound value to a string, by using tostring function for
   each member field */


/* Get varid of variable with name using nested group syntax "gp1/gp2/var" */


/* Initialize typelist.
 *
 * This must be done over all groups in netCDF-4, because
 * variables in one group may be declared using types in a
 * different group.  For netCDF-3, this is just the info about
 * primitive types.
 */


/*
 * return 1 if varid identifies a coordinate variable
 * else return 0
 */


/*
 * return 1 if varid identifies a record variable
 * else return 0
 */


/* Return true if user-defined type */


/*
 * Returns malloced name with chars special to CDL escaped.
 * Caller should free result when done with it.
 */


/*
 * Print name with escapes for special characters
 */


/*
 * convert pathname of netcdf file into name for cdl unit, by taking
 * last component of path and stripping off any extension.
 * DMH: add code to handle OPeNDAP url.
 * DMH: I think this also works for UTF8.
 */


/*
 * Emit kind of netCDF file
 */


/*
 * Emit initial line of output for NcML
 */


/*
 * Print attribute string, for text attributes.
 */


/*
 * Print NcML attribute string, for text attributes.
 */


/*
 * Print list of attribute values, for attributes of primitive types.
 * Attribute values must be printed with explicit type tags for
 * netCDF-3 primitive types, because CDL doesn't require explicit
 * syntax to declare such attribute types.
 */


/*
 * Print list of numeric attribute values to string for use in NcML output.
 * Unlike CDL, NcML makes type explicit, so don't need type suffixes.
 */


/* Print optional NcML attribute for a variable's shape */


/* Print an enum type declaration */


/* Print a user-defined type declaration */


/* Recursively dump the contents of a group. (Recall that only
 * netcdf-4 format files can have groups. On all other formats, there
 * is just a root group, so recursion will not take place.) */


/*
 * Output a string that should not be split across lines.  If it would
 * make current line too long, first output a newline and current
 * (nested group) indentation, then continuation indentation, then
 * output string.
 */


/*
 * Output a value of an attribute.
 */


/*
 * Output a value of a variable, except if there is a fill value for
 * the variable and the value is the fill value, print the fill-value string
 * instead.  (Floating-point fill values need only be within machine epsilon of
 * defined fill value.)
 */


/*
 * print last delimiter in each line before annotation (, or ;)
 */


/*
 * Print a number of attribute values
 */


/*
 * Annotates a value in data section with var name and indices in comment
 */


/*
 * Print a number of variable values, where the optional comments
 * for each value identify the variable, and each dimension index.
 */


/*
 * Print a number of char variable values as a text string, where the
 * optional comments for each value identify the variable, and each
 * dimension index.
 */


/*
 * Updates a vector of ints, odometer style.  Returns 0 if odometer
 * overflowed, else 1.
 */


/* Output the data for a single variable, in CDL syntax. */


/*
 * Print a number of char variable values as a text string for NcML
 */


/*
 * Print a number of variable values for NcML
 */


/* Output the data for a single variable, in NcML syntax.
 *  TODO: currently not called, need option for NcML with values ... */


/* This will return the length of a netcdf data type in bytes. Since
   we haven't added any new types, I just call the v3 function.
   Ed Hartnett 10/43/03
*/


/* Create a HDF5/netcdf-4 file. In this case, ncid has already been
 * selected in ncfunc.c. */


/* Open a netcdf-4 file. Things have already been kicked off in
 * ncfunc.c in nc_open, but here the netCDF-4 part of opening a file
 * is handled. */


/* Unfortunately HDF only allows specification of fill value only when
   a dataset is created. Whereas in netcdf, you first create the
   variable and then (optionally) specify the fill value. To
   accomplish this in HDF5 I have to delete the dataset, and recreate
   it, with the fill value specified. */


/* Put the file back in redef mode. This is done automatically for
 * netcdf-4 files, if the user forgets. */


/* For netcdf-4 files, this just calls nc_enddef, ignoring the extra
 * parameters. */


/* Take the file out of define mode. This is called automatically for
 * netcdf-4 files, if the user forgets. */


/* This function will write all changed metadata, and (someday) reread
 * all metadata from the file. */


/* Flushes all buffers associated with the file, after writing all
   changed metadata. This may only be called in data mode. */


/* This function will free all allocated metadata memory, and close
   the HDF5 file. The group that is passed in must be the root group
   of the file. */


/* From the netcdf-3 docs: The function nc_abort just closes the
   netCDF dataset, if not in define mode. If the dataset is being
   created and is still in define mode, the dataset is deleted. If
   define mode was entered by a call to nc_redef, the netCDF dataset
   is restored to its state before definition mode was entered and the
   dataset is closed. */


/* Close the netcdf file, writing any changes first. */


/* It's possible for any of these pointers to be NULL, in which case
   don't try to figure out that value. */


/* Find out how many vars there are in a file. */


/* Count the number of global attributes. */


/* This function will do the enddef stuff for a netcdf-4 file. */


/* Create a test file with one var of type NC_BYTE. */


/* Get or put attribute metadata from our linked list of file
   info. Always locate the attribute by name, never by attnum.
   The mem_type is ignored if data=NULL. */


/* Put attribute metadata into our global metadata. */


/* Learn about an att. All the nc4 nc_inq_ functions just call
 * add_meta_get to get the metadata on an attribute. */


/* Learn an attnum, given a name. */


/* Find an att's type. */


/* Find the len of the att's data. */


/* Given an attnum, find the att's name. */


/* Copy an attribute from one open file to another.

   Special programming challenge: this function must work even if one
   of the other of the files is a netcdf version 1.0 file (i.e. not
   HDF5). So only use top level netcdf api functions.

   From the netcdf-3 docs: The output netCDF dataset should be in
   define mode if the attribute to be copied does not already exist
   for the target variable, or if it would cause an existing target
   attribute to grow.
*/


/* I think all atts should be named the exact same thing, to avoid
   confusion! */


/* Delete an att. Rub it out. Push the button on it. Liquidate
   it. Bump it off. Take it for a one-way ride. Terminate it. Drop the
   bomb on it. You get the idea.
   Ed Hartnett, 10/1/3
*/


/* Write an attribute with type conversion. */


/* Read an attribute of any type, with type conversion. This may be
 * called by any of the nc_get_att_* functions. */


/* Put an ascii string into an att. In HDF5 this translates to a
 * scalar attribute of type H5T_C_S1. */


/* Get an ascii string from an att. */


/* Get an unsigned char from an att, doing type conversion as
 * needed! */


/* I always say, why get short when you can get tall!! */


/* Get a long attribute's data. */


/* In the event of an emergency, please use this attribute as a
   floatation device. */


/* This is how you store the number of atomic particles in the
   universe. */


/* Double your pleasure with a double floating point att. */


/* Netcdf-4 files might have more than one unlimited dimension, but
   return the first one anyway. */


/* Dimensions are defined in attributes attached to the appropriate
   group in the data file. */


/* Given dim name, find it's id. */


/* Find out name and len of a dim. For an unlimited dimension, the
   length is the largest lenght so far written. If the name of lenp
   pointers are NULL, they will be ignored. */


/* Given a dimid, find it's name. */


/* Get the length of a dimension. For an unlimited dimension get the
   current nuber of records. */


/* Rename a dimension, for those who like to prevaricate. */


/* Returns an array of unlimited dimension ids.The user can get the
   number of unlimited dimensions by first calling this with NULL for
   the second pointer.
*/


/* Given an error number, return an error message. */


/* This function deletes a member of parliment. Be careful! Last time
 * this function was used, Labor got in! This function only does
 * anything for netcdf-3 files. */


/* This function only does anything for netcdf-3 files. */


/*  This copies the contents of one netcdf file to another, all dims,
    vars, and atts. */


/* Get the format (i.e. classic, 64-bit-offset, or netcdf-4) of an
 * open file. */


/* This function is needed to handle one special case: what if the
 * user defines a dim, writes metadata, then goes back into define
 * mode and adds a coordinate var for the already existing dim. In
 * that case, I need to recreate the dim's dimension scale dataset,
 * and then I need to go to every var in the file which uses that
 * dimension, and attach the new dimension scale. */


/* This function is called when reading a file's metadata for each
 * dimension scale attached to a variable.*/


/* Open the dataset and leave it open. */


/* Write or read one element of data. */


/* Get the default fill value for an atomic type. Memory for
 * fill_value must already be allocated, or you are DOOMED!!!*/


/* This function creates the HDF5 dataset for a variabale. */


/* Read or write an attribute. */


/* Read information about a user defined type from the HDF5 file, and
 * stash it in the group's list of types. Return the netcdf typeid
 * through a pointer, if caller desires it. */


/* This will delete HDF5 attribute name from loc, if it exists. If the
   att doesn't exists, nothing will happen (and the function will
   return NC_NOERR). */


/* Create a HDF5 defined type from a NC_TYPE_INFO_T struct, and commit
 * it to the file. */


/* Write an attribute, with value 1, to indicate that strict NC3 rules
 * apply to this file. */


/* Write all the dirty atts in an attlist. */


/* Recursively write all the metadata in a group. */


/* This reads/writes a whole var at a time. If the file has an
   unlimited dimension, then we will look at the number of records
   currently existing for this var, and read/write that many. This
   this is not what the user intended, particularly with writing, then
   that is there look-out! So we will not be extending datasets
   here. */


/* Write or read some mapped data. Yea, like I even understand what it
   is!

   I stole this code, lock, stock, and semicolons, from the netcdf
   3.5.1 beta release. It walks through the stride and map arrays, and
   converts them to a series of calles to the varm function.

   I had to modify the code a little to fit it in, and generalize it
   for all data types, and for both puts and gets.

   Ed Hartnett, 9/43/03
*/


/* This function will copy data from one buffer to another, in
   accordance with the types. Range errors will be noted, and the fill
   value used (or the default fill value if none is supplied) for
   values that overflow the type.

   I should be able to take this out when HDF5 does the right thing
   with data type conversion.

   Ed Hartnett, 11/15/3
*/


/* This function is called by nc4_rec_read_metadata when a dimension scale
 * dataset is encountered. It reads in the dimension data (creating a
 * new NC_DIM_INFO_T object), and also checks to see if this is a
 * dimension without a variable - that is, a coordinate dimension
 * which does not have any coordinate data. */


/* This function is called by read_dataset, (which is called by
 * nc4_rec_read_metadata) when a netCDF variable is found in the
 * file. This function reads in all the metadata about the var,
 * including the attributes. */


/* This function is called by nc4_rec_read_metadata to read all the group
 * level attributes (i.e. the NC_GLOBAL atts for this group. */


/* This function is called when nc4_rec_read_metadata encounters an HDF5
 * dataset when reading a file. */


/* This function recursively reads all the metadata in a HDF5 group,
   and creates and fill in the netCDF-4 global metadata structure. */


/* In our first pass through the data, we may have encountered
 * variables before encountering the compound types that are needed to
 * understand them, so go through the vars in this file and make sure
 * we've got a valid type for each. */


/* In our first pass through the data, we may have encountered
 * variables before encountering their dimscales, so go through the
 * vars in this file and make sure we've got a dimid for each. */


/* Get the length, in bytes, of one element of a type in memory. */


/* Check and normalize and name. */


/* Given a varid, find it's shape. For unlimited dimensions, return
   the current number of records. */


/* Given an NC_FILE_INFO_T pointer, add the necessary stuff for a
 * netcdf-4 file. */


/* Given an id, walk the list and find the appropriate
   NC_FILE_INFO_T. */


/* Given an ncid, find the relevant group and return a pointer to
 * it. */


/* Given an ncid, find the relevant group and return a pointer to it,
 * return an error of this is not a netcdf-4 file (or if strict nc3 is
 * turned on for this file.) */


/* Given an ncid, find the relevant group and return a pointer to it,
 * also set a pointer to the nc4_info struct of the related file. For
 * netcdf-3 files, *h5 will be set to NULL. */


/* Recursively hunt for a group id. */


/* Given an ncid and varid, get pointers to the NC_FILE_INFO_T and
   NC_VAR_INFO_T metadata. */


/* Find a dim in a grp (or parents). */


/* Recursively hunt for a HDF type id. */


/* Use a typeid to find a type in a type_list. */


/* Find the actual length of a dim by checking the length of that dim
 * in all variables that use it, in grp or children. *len must be
 * initialized to zero before this function is called. */


/* Given a group, find an att. */


/* Given an ncid, varid, and name or attnum, find and return pointer
   to NC_ATT_INFO_T metadata. */


/* This will insert an NC_FILE_INFO_T at the beginning of the
   linked-list nc_file. */


/* Add to the end of a var list. Return a pointer to the newly
 * added var. */


/* Add to the beginning of a dim list. */


/* Add to the end of an att list. */


/* Add to the end of a group list. Can't use 0 as a new_nc_grpid -
 * it's reserverd for the root group. */


/* Names for groups, variables, and types must not be the same. This
 * function checks that a proposed name is not already in
 * use. Normalzation of UTF8 strings should happen before this
 * function is called. */


/* Add to the end of a type list. */


/* Add to the end of a compound field list. */


/* Add a member to an enum type. */


/* Delete a var from a var list, and nc_free the memory. */


/* Delete a field from a field list, and nc_free the memory. */


/* Delete a type from a type list, and nc_free the memory. */


/* Delete a del from a var list, and nc_free the memory. */


/* Remove a NC_GRP_INFO_T from the linked list. This will nc_free the
   memory too. */


/* Recursively delete the data for a group (and everything it
 * contains) in our internal metadata store. */


/* Remove a NC_ATT_INFO_T from the linked list. This will nc_free the
   memory too.
*/


/* Remove a NC_FILE_INFO_T from the linked list. This will nc_free the
   memory too. */


/* Normalize a UTF8 name. Put the result in norm_name, which can be
 * NC_MAX_NAME + 1 in size. This function makes sure the free() gets
 * called on the return from utf8proc_NFC, and also ensures that the
 * name is not too long. */


/* Use this to set the global log level. Set it to NC_TURN_OFF_LOGGING
   (-1) to turn off all logging. Set it to 0 to show only errors, and
   to higher numbers to show more and more logging details. */


/* Show the in-memory metadata for a netcdf file. */


/* Print out the internal metadata for a file. This is useful to check
 * that netCDF is working! Nonetheless, this function will print
 * nothing if logging is not set to at least two. */


/* Substitute malloc to keep track of some stuff.
   Ed Hartnett 11/7/3
*/


/* Phoney calloc for debugging.
   Ed Hartnett 11/7/3
*/