[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: 20020410: gribtonc CDLs for AVN and could CDL for LAPS work



Tiziana,

I got a proto version working but there were bugs in gribtonc.  I,m
attaching the 3 files plus the the cdl.  Insert the code into the dir and
do a make then try it out.  I started with a another cdl so could you
check for vars that are not used and then comment them out with '//'. Let
me know what you find out.

Thanks,
Robb...

===============================================================================
Robb Kambic                                Unidata Program Center
Software Engineer III                      Univ. Corp for Atmospheric Research
address@hidden             WWW: http://www.unidata.ucar.edu/
===============================================================================
netcdf avn-x {          // 126 Wave, 18 Layer Spectral Model Aviation Run
                        // on expanded quasi-regular "thinned" grids

dimensions:

        record = UNLIMITED ;    // (reference time, forecast time)
        level = 26 ;

        lat = 181 ;             // latitude
        lon = 360 ;             // longitude
        lpdg = 2 ;              // boundary layer levels
        fhg = 2 ;               // fixed height above ground levels
        fh = 3 ;                // fixed height above ground levels
        soil_lpdg = 6 ;         // soil boundary layer levels
        ls_all = 7 ;            // whole atmosphere layer
        sigma = 1 ;             // sigma level
        datetime_len = 21 ;     // string length for datetime strings
        nmodels = 3 ;           // both AVN and SSIAVN models
        accum = 2 ;             // time range for accumulations
        nav = 1 ;               // For navigation.  Variables that use
                                // this dimension define a mapping between
                                // (lat, lon) indices and (lat, lon) coords.

        nav_len = 100 ;         // max string length for navigation strings

variables:

        double  reftime(record) ;       // reference time of the model
                reftime:long_name = "reference time" ;
                reftime:units = "hours since 1992-1-1" ;

        double  valtime(record) ;       // forecast time ("valid" time)
                valtime:long_name = "valid time" ;
                valtime:units = "hours since 1992-1-1" ;

        :record = "reftime, valtime" ;  // "dimension attribute" -- means
                                        // (reftime, valtime) uniquely
                                        // determine record

        char    datetime(record, datetime_len) ; // derived from reftime
                datetime:long_name = "reference date and time" ;
                // units YYYY-MM-DD hh:mm:ssZ  (ISO 8601)

        float   valtime_offset(record) ; // derived as valtime-reftime
                valtime_offset:long_name = "hours from reference time" ;
                valtime_offset:units = "hours" ;

        float   level(level) ;
                level:long_name = "level" ;
                level:units = "hectopascals" ;

        // (soil_lpdg_bot, soil_lpdg_top) uniquely determines soil_lpdg
                
        float   soil_lpdg_bot(soil_lpdg) ;
                soil_lpdg_bot:long_name = "bottom level of boundary layer 
between 2 levels from ground to levels" ;
                //soil_lpdg_bot:units = "centimeters" ;
                
        float   soil_lpdg_top(soil_lpdg) ;
                soil_lpdg_top:long_name = "top level of boundary layer between 
2 levels from ground to levels" ;
                //soil_lpdg_top:units = "centimeters" ;

        :lpdg = "lpdg_bot, lpdg_top" ; // (lpdg_bot, lpdg_top) uniquely
                                       // determines lpdg
                
        float   lpdg_bot(lpdg) ;
                lpdg_bot:long_name = "bottom level of boundary layer between 2 
levels at specified pressure differences from ground to levels" ;
                lpdg_bot:units = "hectopascals" ;
                
        float   lpdg_top(lpdg) ;
                lpdg_top:long_name = "top level of boundary layer between 2 
levels at specified pressure differences from ground to levels" ;
                lpdg_top:units = "hectopascals" ;

        float   fhg(fhg) ;               // fixed height above ground
                fhg:long_name = "fixed height above ground" ;
                fhg:units = "meters" ;

        float   fh(fh) ;                 // fixed height above ground
                fh:long_name = "fixed height above ground" ;
                fh:units = "meters" ;

        :ls_all = "ls_all_bot, ls_all_top" ;
                
        float   ls_all_bot(ls_all) ;
                ls_all_bot:long_name = "bottom level of atmosphere between 2 
sigma levels" ;
                ls_all_bot:units = "" ;
                
        float   ls_all_top(ls_all) ;
                ls_all_top:long_name = "top level of atmosphere between 2 sigma 
levels" ;
                ls_all_top:units = "" ;

        float   sigma(sigma) ;           // fixed height above ground
                sigma:long_name = "sigma level" ;
                sigma:units = "" ;       // dimensionless

        long    model_id(nmodels) ;
                model_id:long_name = "generating process ID number" ;

        // The following lat and lon coordinate variables are redundant,
        // since the navigation variables provide the necessary information.
        // The extra information is included here for human readability.

        float   lat(lat) ;
                lat:long_name = "latitude" ;
                lat:units = "degrees_north" ;

        float   lon(lon) ;
                lon:long_name = "longitude" ;
                lon:units = "degrees_east" ;

        // navigation variables all use nav dimension

        char    nav_model(nav, nav_len) ;        // navigation parameterization
                nav_model:long_name = "navigation model name" ;

        int     grid_type_code(nav) ;
                grid_type_code:long_name = "GRIB-1 GDS data representation 
type" ;

        char    grid_type(nav, nav_len) ;
                grid_type:long_name = "GRIB-1 grid type" ;

        char    grid_name(nav, nav_len) ;
                grid_name:long_name = "grid name" ;

        int     grid_center(nav) ;
                grid_center:long_name = "GRIB-1 originating center ID" ;

        int     grid_number(nav) ;
                grid_number:long_name = "GRIB-1 catalogued grid numbers" ;
                grid_number:_FillValue = -9999 ;

        char    i_dim(nav, nav_len) ;
                i_dim:long_name = "longitude dimension name" ;

        char    j_dim(nav, nav_len) ;
                j_dim:long_name = "latitude dimension name" ;

        int     Ni(nav) ;
                Ni:long_name = "number of points along a latitude circle" ;

        int     Nj(nav) ;
                Nj:long_name =  "number of points along a longitude circle" ;

        float   La1(nav) ;
                La1:long_name = "latitude of first grid point" ;
                La1:units = "degrees_north" ;

        float   Lo1(nav) ;
                Lo1:long_name = "longitude of first grid point" ;
                Lo1:units = "degrees_east" ;

        float   La2(nav) ;
                La2:long_name = "latitude of last grid point" ;
                La2:units = "degrees_north" ;

        float   Lo2(nav) ;
                Lo2:long_name = "longitude of last grid point" ;
                Lo2:units = "degrees_east" ;

        float   Di(nav) ;
                Di:long_name = "Longitudinal direction increment" ;
                Di:units = "degrees" ;

        float   Dj(nav) ;
                Dj:long_name = "Latitudinal direction increment" ;
                Dj:units = "degrees" ;

        byte    ResCompFlag(nav) ;
                ResCompFlag:long_name = "resolution and component flags" ;

        // end of navigation variables

        float   P(record, lat, lon) ;
                P:long_name = "pressure" ;
                P:units = "Pa" ;
                P:_FillValue = -9999.f ;
                P:navigation = "nav" ;

        float   P_maxwind(record, lat, lon) ;
                P_maxwind:long_name = "pressure at maximum wind" ;
                P_maxwind:units = "Pa" ;
                P_maxwind:_FillValue = -9999.f ;
                P_maxwind:navigation = "nav" ;

        float   P_trop(record, lat, lon) ;
                P_trop:long_name = "pressure at tropopause" ;
                P_trop:units = "Pa" ;
                P_trop:_FillValue = -9999.f ;
                P_trop:navigation = "nav" ;

        float   P_msl(record, lat, lon) ;
                P_msl:long_name = "pressure reduced to MSL" ;
                P_msl:units = "Pa" ;
                P_msl:_FillValue = -9999.f ;
                P_msl:navigation = "nav" ;

        float   P_sfc(record, lat, lon) ;
                P_sfc:long_name = "pressure at surface" ;
                P_sfc:units = "Pa" ;
                P_sfc:_FillValue = -9999.f ;
                P_sfc:navigation = "nav" ;

        float   RH(record, level, lat, lon) ;
                RH:long_name = "relative humidity" ;
                RH:units = "percent" ;
                RH:_FillValue = -9999.f ;
                RH:navigation = "nav" ;

        float   T(record, level, lat, lon) ;
                T:long_name = "temperature" ;
                T:units = "degK" ;
                T:_FillValue = -9999.f ;
                T:navigation = "nav" ;

        float   T_maxwind(record, lat, lon) ;
                T_maxwind:long_name = "temperature at maxwind" ;
                T_maxwind:units = "degK" ;
                T_maxwind:_FillValue = -9999.f ;
                T_maxwind:navigation = "nav" ;

        float   T_trop(record, lat, lon) ;
                T_trop:long_name = "temperature at tropopause" ;
                T_trop:units = "degK" ;
                T_trop:_FillValue = -9999.f ;
                T_trop:navigation = "nav" ;

        float   T_sigma(record, sigma, lat, lon) ;
                T_sigma:long_name = "temperature" ;
                T_sigma:units = "degK" ;
                T_sigma:_FillValue = -9999.f ;
                T_sigma:navigation = "nav" ;

        float   RH_sigma(record, sigma, lat, lon) ;
                RH_sigma:long_name = "relative humidity at sigma level" ;
                RH_sigma:units = "percent" ;
                RH_sigma:_FillValue = -9999.f ;
                RH_sigma:navigation = "nav" ;

        float   u_sigma(record, sigma, lat, lon ) ;
                u_sigma:long_name = "u-component of wind" ;
                u_sigma:units = "m/s" ;
                u_sigma:_FillValue = -9999.f ;
                u_sigma:navigation = "nav" ;

        float   v_sigma(record, sigma, lat, lon ) ;
                v_sigma:long_name = "v-component of wind" ;
                v_sigma:units = "m/s" ;
                v_sigma:_FillValue = -9999.f ;
                v_sigma:navigation = "nav" ;

        float   theta_sigma(record, sigma, lat, lon) ;
                theta_sigma:long_name = "Potential temperature" ;
                theta_sigma:units = "degK" ;
                theta_sigma:_FillValue = -9999.f ;
                theta_sigma:navigation = "nav" ;

        float   omega_sigma(record, sigma, lat, lon) ;
                omega_sigma:long_name = "pressure vertical velocity" ;
                omega_sigma:units = "Pa/s" ;
                omega_sigma:_FillValue = -9999.f ;
                omega_sigma:navigation = "nav" ;        // georeference info

        float   Z(record, level, lat, lon) ;
                Z:long_name = "geopotential height" ;
                Z:units = "gp m" ;
                Z:_FillValue = -9999.f ;
                Z:navigation = "nav" ;  // georeference info

        float   Z_maxwind(record, lat, lon) ;
                Z_maxwind:long_name = "geopotential height at maxwind" ;
                Z_maxwind:units = "gp m" ;
                Z_maxwind:_FillValue = -9999.f ;
                Z_maxwind:navigation = "nav" ;

        float   Z_trop(record, lat, lon) ;
                Z_trop:long_name = "geopotential height at tropopause" ;
                Z_trop:units = "gp m" ;
                Z_trop:_FillValue = -9999.f ;
                Z_trop:navigation = "nav" ;

        float   T_lpdg(record, lpdg, lat, lon) ;
                T_lpdg:long_name = "temperature in boundary layer" ;
                T_lpdg:units = "degK" ;
                T_lpdg:_FillValue = -9999.f ;
                T_lpdg:navigation = "nav" ;

        float   RH_lpdg(record, lpdg, lat, lon) ;
                RH_lpdg:long_name = "relative humidity in boundary layer" ;
                RH_lpdg:units = "percent" ;
                RH_lpdg:_FillValue = -9999.f ;
                RH_lpdg:navigation = "nav" ;

        float   cin_lpdg(record, lpdg, lat, lon ) ;
                cin_lpdg:long_name = "boundary convective inhibition" ;
                cin_lpdg:units = "J/kg" ;
                cin_lpdg:_FillValue = -9999.f ;
                cin_lpdg:navigation = "nav" ;

        float   spec_hum_lpdg(record, lpdg, lat, lon ) ;
                spec_hum_lpdg:long_name = "specific humidity" ;
                spec_hum_lpdg:units = "kg/kg" ;
                spec_hum_lpdg:_FillValue = -9999.f ;
                spec_hum_lpdg:navigation = "nav" ;

        float   cape_lpdg(record, lpdg, lat, lon ) ;
                cape_lpdg:long_name = "boundary convective available potential 
energy" ;
                cape_lpdg:units = "J/kg" ;
                cape_lpdg:_FillValue = -9999.f ;
                cape_lpdg:navigation = "nav" ;

        float   u_lpdg(record, lpdg, lat, lon) ;
                u_lpdg:long_name = "u-component of wind in boundary layer" ;
                u_lpdg:units = "meters/second" ;
                u_lpdg:_FillValue = -9999.f ;
                u_lpdg:navigation = "nav" ;

        float   v_lpdg(record, lpdg, lat, lon) ;
                v_lpdg:long_name = "v-component of wind in boundary layer" ;
                v_lpdg:units = "meters/second" ;
                v_lpdg:_FillValue = -9999.f ;
                v_lpdg:navigation = "nav" ;

        float   u(record, level, lat, lon) ;
                u:long_name = "u-component of wind" ;
                u:units = "meters/second" ;
                u:_FillValue = -9999.f ;
                u:navigation = "nav" ;

        float   u_maxwind(record, lat, lon) ;
                u_maxwind:long_name = "u-component of wind at max wind" ;
                u_maxwind:units = "meters/second" ;
                u_maxwind:_FillValue = -9999.f ;
                u_maxwind:navigation = "nav" ;

        float   u_trop(record, lat, lon) ;
                u_trop:long_name = "u-component of wind at tropopause" ;
                u_trop:units = "meters/second" ;
                u_trop:_FillValue = -9999.f ;
                u_trop:navigation = "nav" ;

        float   v(record, level, lat, lon) ;
                v:long_name = "v-component of wind" ;
                v:units = "meters/second" ;
                v:_FillValue = -9999.f ;
                v:navigation = "nav" ;

        float   v_maxwind(record, lat, lon) ;
                v_maxwind:long_name = "v-component of wind at max wind" ;
                v_maxwind:units = "meters/second" ;
                v_maxwind:_FillValue = -9999.f ;
                v_maxwind:navigation = "nav" ;

        float   v_trop(record, lat, lon) ;
                v_trop:long_name = "v-component of wind at tropopause" ;
                v_trop:units = "meters/second" ;
                v_trop:_FillValue = -9999.f ;
                v_trop:navigation = "nav" ;

        float   u_fhg(record, fhg, lat, lon) ;
                u_fhg:long_name = "u-component of wind at fixed height above 
ground" ;
                u_fhg:units = "meters/second" ;
                u_fhg:_FillValue = -9999.f ;
                u_fhg:navigation = "nav" ;

        float   v_fhg(record, fhg, lat, lon) ;
                v_fhg:long_name = "v-component of wind at fixed height above 
ground" ;
                v_fhg:units = "meters/second" ;
                v_fhg:_FillValue = -9999.f ;
                v_fhg:navigation = "nav" ;

        float   RH_fhg(record, fhg, lat, lon) ;
                RH_fhg:long_name = "relative humidity at fixed height above 
ground" ;
                RH_fhg:units = "percent" ;
                RH_fhg:_FillValue = -9999.f ;
                RH_fhg:navigation = "nav" ;

        float   spec_hum_fhg(record, fhg, lat, lon) ;
                spec_hum_fhg:long_name = "specific humidity at fixed height 
above ground" ;
                spec_hum_fhg:units = "kg/kg" ;
                spec_hum_fhg:_FillValue = -9999.f ;
                spec_hum_fhg:navigation = "nav" ;

        float   T_fhg(record, fhg, lat, lon) ;
                T_fhg:long_name = "temperature at fixed height above ground" ;
                T_fhg:units = "degK" ;
                T_fhg:_FillValue = -9999.f ;
                T_fhg:navigation = "nav" ;

        float   Tmax_fhg(record, fhg, lat, lon) ;
                Tmax_fhg:long_name = " maximum temperature at fixed height 
above ground" ;
                Tmax_fhg:units = "degK" ;
                Tmax_fhg:_FillValue = -9999.f ;
                Tmax_fhg:navigation = "nav" ;

        float   Tmin_fhg(record, fhg, lat, lon) ;
                Tmin_fhg:long_name = " minimum temperature at fixed height 
above ground" ;
                Tmin_fhg:units = "degK" ;
                Tmin_fhg:_FillValue = -9999.f ;
                Tmin_fhg:navigation = "nav" ;

        float   u_fh(record, fh, lat, lon) ;
                u_fh:long_name = "u-component of wind at fixed height above 
ground" ;
                u_fh:units = "meters/second" ;
                u_fh:_FillValue = -9999.f ;
                u_fh:navigation = "nav" ;

        float   v_fh(record, fh, lat, lon) ;
                v_fh:long_name = "v-component of wind at fixed height above 
ground" ;
                v_fh:units = "meters/second" ;
                v_fh:_FillValue = -9999.f ;
                v_fh:navigation = "nav" ;

        float   RH_fh(record, fh, lat, lon) ;
                RH_fh:long_name = "relative humidity at fixed height above 
ground" ;
                RH_fh:units = "percent" ;
                RH_fh:_FillValue = -9999.f ;
                RH_fh:navigation = "nav" ;

        float   T_fh(record, fh, lat, lon) ;
                T_fh:long_name = "temperature at fixed height above ground" ;
                T_fh:units = "degK" ;
                T_fh:_FillValue = -9999.f ;
                T_fh:navigation = "nav" ;

        float   RH_ls(record, ls_all, lat, lon) ;
                RH_ls:long_name = "relative humidity" ;
                RH_ls:units = "percent" ;
                RH_ls:_FillValue = -9999.f ;
                RH_ls:navigation = "nav" ;

        float   PRECIP(record, lat, lon) ;
                PRECIP:long_name = "total precipitation over accumulation 
interval" ;
                PRECIP:units = "kg/m2" ;
                PRECIP:_FillValue = -9999.f ;
                PRECIP:navigation = "nav" ;

        float   PRECIP_accum_times(record, accum) ;
                PRECIP_accum_times:long_name = "precipitation accumulation 
interval" ;
                PRECIP_accum_times:units = "hours" ;
                PRECIP_accum_times:_FillValue = -9999.f ;

        float   precip_cn(record, lat, lon) ;
                precip_cn:long_name = "convective precipitation over 
accumulation interval" ;
                precip_cn:units = "kg/m2" ;
                precip_cn:_FillValue = -9999.f ;
                precip_cn:navigation = "nav" ;

        float   precip_cn_accum_times(record, accum) ;
                precip_cn_accum_times:long_name = "convective precipitation 
accumulation interval" ;
                precip_cn_accum_times:units = "hours" ;
                precip_cn_accum_times:_FillValue = -9999.f ;

        float   precip_rt(record, lat, lon ) ;
                precip_rt:long_name = "precipitation rate" ;
                precip_rt:units = "kg/m2/s" ;
                precip_rt:_FillValue = -9999.f ;
                precip_rt:navigation = "nav" ;

        float   watr(record, lat, lon ) ;
                watr:long_name = "water runoff" ;
                watr:units = "kg/m2" ;
                watr:_FillValue = -9999.f ;
                watr:navigation = "nav" ;

        float   pr_water_atm(record, lat, lon ) ; // entire atmosphere as 
single layer
                pr_water_atm:long_name = "precipitable water" ;
                pr_water_atm:units = "kg/m2" ;
                pr_water_atm:_FillValue = -9999.f ;
                pr_water_atm:navigation = "nav" ;

        float   cprat(record, lat, lon) ;
                cprat:long_name = "Convective precipitation rate" ;
                cprat:units = "kg/m2/sec" ;
                cprat:_FillValue = -9999.f ;
                cprat:navigation = "nav" ;

        float   crain(record, lat, lon ) ;
                crain:long_name = "Categorical rain" ;
                crain:_FillValue = -9999.f ;
                crain:navigation = "nav" ;

        float   cfrzrn(record, lat, lon ) ;
                cfrzrn:long_name = "Categorical freezing rain" ;
                cfrzrn:_FillValue = -9999.f ;
                cfrzrn:navigation = "nav" ;

        float   csnow(record, lat, lon ) ;
                csnow:long_name = "Categorical snow" ;
                csnow:_FillValue = -9999.f ;
                csnow:navigation = "nav" ;

        float   ice_conc(record, lat, lon ) ;
                ice_conc:long_name = "Ice concentration" ;
                ice_conc:_FillValue = -9999.f ;
                ice_conc:navigation = "nav" ;

        float   LI(record, lat, lon ) ;
                LI:long_name = "lifted index" ;
                LI:units = "degK" ;
                LI:_FillValue = -9999.f ;       // To fill grid corners
                LI:navigation = "nav" ;

        float   T_sfc(record, lat, lon) ;
                T_sfc:long_name = "surface temperature" ;
                T_sfc:units = "degK" ;
                T_sfc:_FillValue = -9999.f ;
                T_sfc:navigation = "nav" ;

        float   Z_sfc(record, lat, lon) ;
                Z_sfc:long_name = "terrain" ;
                Z_sfc:units = "gp m" ;
                Z_sfc:_FillValue = -9999.f ;
                Z_sfc:navigation = "nav" ;

        float   sen_ht_sfc(record, lat, lon ) ;
                sen_ht_sfc:long_name = "Sensible heat net flux" ;
                sen_ht_sfc:units = "W / m2" ;
                sen_ht_sfc:_FillValue = -9999.f ;
                sen_ht_sfc:navigation = "nav" ;

        float   cin_sfc(record, lat, lon ) ;
                cin_sfc:long_name = "surface convective inhibition" ;
                cin_sfc:units = "J/kg" ;
                cin_sfc:_FillValue = -9999.f ;
                cin_sfc:navigation = "nav" ;

        float   uswrf_sfc(record, lat, lon ) ;
                uswrf_sfc:long_name = "Upward short wave rad. flux" ;
                uswrf_sfc:units = "W/m2" ;
                uswrf_sfc:_FillValue = -9999.f ;
                uswrf_sfc:navigation = "nav" ;

        float   dswrf_sfc(record, lat, lon ) ;
                dswrf_sfc:long_name = "Downward short wave rad. flux" ;
                dswrf_sfc:units = "W/m2" ;
                dswrf_sfc:_FillValue = -9999.f ;
                dswrf_sfc:navigation = "nav" ;

        float   ulwrf_sfc(record, lat, lon ) ;
                ulwrf_sfc:long_name = "Upward long wave rad. flux" ;
                ulwrf_sfc:units = "W / m2" ;
                ulwrf_sfc:_FillValue = -9999.f ;
                ulwrf_sfc:navigation = "nav" ;

        float   dlwrf_sfc(record, lat, lon ) ;
                dlwrf_sfc:long_name = "Downward long wave rad. flux" ;
                dlwrf_sfc:units = "W / m2" ;
                dlwrf_sfc:_FillValue = -9999.f ;
                dlwrf_sfc:navigation = "nav" ;

        float   land_mask_sfc(record, lat, lon ) ;
                land_mask_sfc:long_name = "Land-Sea mask" ;
                land_mask_sfc:units = "bit" ;
                land_mask_sfc:_FillValue = -9999.f ;
                land_mask_sfc:navigation = "nav" ;

        float   albedo_sfc(record, lat, lon ) ;
                albedo_sfc:long_name = "Albedo" ;
                albedo_sfc:_FillValue = -9999.f ;
                albedo_sfc:navigation = "nav" ;

        //      Latent heat net flux

        float   lat_ht_sfc(record, lat, lon ) ;
                lat_ht_sfc:long_name = "Latent heat net flux" ;
                lat_ht_sfc:units = "W / m2" ;
                lat_ht_sfc:_FillValue = -9999.f ;
                lat_ht_sfc:navigation = "nav" ;

        float   LI4_sfc(record, lat, lon ) ;
                LI4_sfc:long_name = "Best 4 layer lift index" ;
                LI4_sfc:units = "K" ;
                LI4_sfc:_FillValue = -9999.f ;
                LI4_sfc:navigation = "nav" ;

        float   cape_sfc(record, lat, lon ) ;
                cape_sfc:long_name = "surface convective available potential 
energy" ;
                cape_sfc:units = "J/kg" ;
                cape_sfc:_FillValue = -9999.f ;
                cape_sfc:navigation = "nav" ;

        float   u_flx_sfc(record, lat, lon ) ;
                u_flx_sfc:long_name = "Momentum flux, u componet" ;
                u_flx_sfc:units = "N/m2" ;
                u_flx_sfc:_FillValue = -9999.f ;
                u_flx_sfc:navigation = "nav" ;

        float   v_flx_sfc(record, lat, lon ) ;
                v_flx_sfc:long_name = "Momentum flux, v componet" ;
                v_flx_sfc:units = "N/m2" ;
                v_flx_sfc:_FillValue = -9999.f ;
                v_flx_sfc:navigation = "nav" ;

        //      Planetary boundary layer height

        float   hpbl_sfc(record) ;
                hpbl_sfc:long_name = "Planetary boundary layer height" ;
                hpbl_sfc:units = "m" ;
                hpbl_sfc:_FillValue = -9999.f ;
                hpbl_sfc:navigation = "nav" ;

        float   RH_atm(record, lat, lon) ;
                RH_atm:long_name = "relative humidity entire atmosphere" ;
                RH_atm:units = "percent" ;
                RH_atm:_FillValue = -9999.f ;
                RH_atm:navigation = "nav" ;

        float   totoz_atm(record, lat, lon) ;
                totoz_atm:long_name = "Total ozone entire atmosphere" ;
                //totoz_atm:units = "Dobson" ;
                totoz_atm:_FillValue = -9999.f ;
                totoz_atm:navigation = "nav" ;

        float   RH_frzlvl(record, lat, lon ) ;
                RH_frzlvl:long_name = "relative humidity at 0 degree isotherm" ;
                RH_frzlvl:units = "percent" ;
                RH_frzlvl:_FillValue = -9999.f ;
                RH_frzlvl:navigation = "nav" ;

        float   Z_frzlvl(record, lat, lon ) ;
                Z_frzlvl:long_name = "geopotential height at 0 isotherm" ;
                Z_frzlvl:units = "gp m" ;
                Z_frzlvl:_FillValue = -9999.f ;
                Z_frzlvl:navigation = "nav" ;

        float   T_hctl(record, lat, lon) ;
                T_hctl:long_name = "temperature at high cloud top level" ;
                T_hctl:units = "degK" ;
                T_hctl:_FillValue = -9999.f ;
                T_hctl:navigation = "nav" ;

        float   P_hctl(record, lat, lon) ;
                P_hctl:long_name = "Pressure at high cloud top level" ;
                P_hctl:units = "Pa" ;
                P_hctl:_FillValue = -9999.f ;
                P_hctl:navigation = "nav" ;

        float   T_mctl(record, lat, lon) ;
                T_mctl:long_name = "temperature at middle cloud top level" ;
                T_mctl:units = "degK" ;
                T_mctl:_FillValue = -9999.f ;
                T_mctl:navigation = "nav" ;

        float   P_mctl(record, lat, lon) ;
                P_mctl:long_name = "Pressure at middle cloud top level" ;
                P_mctl:units = "Pa" ;
                P_mctl:_FillValue = -9999.f ;
                P_mctl:navigation = "nav" ;

        float   T_lctl(record, lat, lon) ;
                T_lctl:long_name = "temperature at low cloud top level" ;
                T_lctl:units = "degK" ;
                T_lctl:_FillValue = -9999.f ;
                T_lctl:navigation = "nav" ;

        float   P_lctl(record, lat, lon) ;
                P_lctl:long_name = "Pressure at low cloud top level" ;
                P_lctl:units = "Pa" ;
                P_lctl:_FillValue = -9999.f ;
                P_lctl:navigation = "nav" ;

        float   P_mcbl(record, lat, lon) ;
                P_mcbl:long_name = "Pressure at middle cloud bottom level" ;
                P_mcbl:units = "Pa" ;
                P_mcbl:_FillValue = -9999.f ;
                P_mcbl:navigation = "nav" ;

        float   P_cctl(record, lat, lon) ;
                P_cctl:long_name = "Pressure at convective cloud top layer" ;
                P_cctl:units = "Pa" ;
                P_cctl:_FillValue = -9999.f ;
                P_cctl:navigation = "nav" ;

        float   P_ccbl(record, lat, lon) ;
                P_ccbl:long_name = "Pressure at convective cloud bottom layer" ;
                P_ccbl:units = "Pa" ;
                P_ccbl:_FillValue = -9999.f ;
                P_ccbl:navigation = "nav" ;

        float   uswrf_topa(record, lat, lon) ;
                uswrf_topa:long_name = "Upward short wave rad.flux" ;
                uswrf_topa:units = "W / m2" ;
                uswrf_topa:_FillValue = -9999.f ;
                uswrf_topa:navigation = "nav" ;

        float   ulwrf_topa(record, lat, lon) ;
                ulwrf_topa:long_name = "Upward long wave rad.flux" ;
                ulwrf_topa:units = "W / m2" ;
                ulwrf_topa:_FillValue = -9999.f ;
                ulwrf_topa:navigation = "nav" ;

        float   N(record, lat, lon) ;
                N:long_name = "Total cloud cover" ;
                N:units = "percent" ;
                N:_FillValue = -9999.f ;
                N:navigation = "nav" ;

        float   N_hcy(record, lat, lon) ;
                N_hcy:long_name = "Total cloud cover, high cloud layer" ;
                N_hcy:_FillValue = -9999.f ;
                N_hcy:navigation = "nav" ;

        float   N_mcy(record, lat, lon) ;
                N_mcy:long_name = "Total cloud cover, middle cloud layer" ;
                N_mcy:_FillValue = -9999.f ;
                N_mcy:navigation = "nav" ;

        float   N_lcy(record, lat, lon) ;
                N_lcy:long_name = "Total cloud cover, low cloud layer" ;
                N_lcy:_FillValue = -9999.f ;
                N_lcy:navigation = "nav" ;

        float   N_bcy(record, lat, lon) ;
                N_bcy:long_name = "Total cloud cover, boundary layer cloud 
layer" ;
                N_bcy:_FillValue = -9999.f ;
                N_bcy:navigation = "nav" ;

        float   N_ccy(record, lat, lon) ;
                N_ccy:long_name = "Total cloud cover, covective cloud layer" ;
                N_ccy:_FillValue = -9999.f ;
                N_ccy:navigation = "nav" ;

        float   N_atm(record, lat, lon) ;
                N_atm:long_name = "Total cloud cover entire atmosphere" ;
                N_atm:_FillValue = -9999.f ;
                N_atm:navigation = "nav" ;

        float   P_hcbl(record, lat, lon) ;
                P_hcbl:long_name = "Pressure, high cloud bottom level" ;
                P_hcbl:units = "Pa" ;
                P_hcbl:_FillValue = -9999.f ;
                P_hcbl:navigation = "nav" ;

        float   P_lcbl(record, lat, lon) ;
                P_lcbl:long_name = "Pressure, low cloud bottom level" ;
                P_lcbl:units = "Pa" ;
                P_lcbl:_FillValue = -9999.f ;
                P_lcbl:navigation = "nav" ;

        float   omega(record, level, lat, lon) ;
                omega:long_name = "pressure vertical velocity" ;
                omega:units = "Pa/s" ;
                omega:_FillValue = -9999.f ;
                omega:navigation = "nav" ;      // georeference info

        float   absvor(record, level, lat, lon) ;
                absvor:long_name = "absolute vorticity" ;
                absvor:units = "1/s" ;
                absvor:_FillValue = -9999.f ;
                absvor:navigation = "nav" ;

        //      Cloud water 

        float   clwmr(record, level, lat, lon ) ;
                clwmr:long_name = "Cloud water" ;
                clwmr:units = "kg / kg" ;
                clwmr:_FillValue = -9999.f ;
                clwmr:navigation = "nav" ;

        float   cloud_wat_atm(record, lat, lon ) ;
                cloud_wat_atm:long_name = "Cloud water" ;
                cloud_wat_atm:units = "kg / m2" ;
                cloud_wat_atm:_FillValue = -9999.f ;
                cloud_wat_atm:navigation = "nav" ;

        float   snow_wat(record, lat, lon ) ;
                snow_wat:long_name = "Water equiv. of accumulated snow depth" ;
                snow_wat:units = "kg / m2" ;
                snow_wat:_FillValue = -9999.f ;
                snow_wat:navigation = "nav" ;

        float   cicepl(record, lat, lon ) ;
                cicepl:long_name = "Categorical ice pellets" ;
                cicepl:_FillValue = -9999.f ;
                cicepl:navigation = "nav" ;

        //      Ozone mixing ratio 

        float   o3mr(record, level, lat, lon ) ;
                o3mr:long_name = "Ozone mixing ratio" ;
                o3mr:units = "kg / kg" ;
                o3mr:_FillValue = -9999.f ;
                o3mr:navigation = "nav" ;

        float   Zdev(record, level, lat, lon ) ;
                Zdev:long_name = "Geopotential height anomaly" ;
                Zdev:units = "gp m" ;
                Zdev:_FillValue = -9999.f ;
                Zdev:navigation = "nav" ;

        float   gpt_hgt5(record, level, lat, lon ) ;
                gpt_hgt5:long_name = "5-wave Geopotential height" ;
                gpt_hgt5:units = "gp m" ;
                gpt_hgt5:_FillValue = -9999.f ;
                gpt_hgt5:navigation = "nav" ;

        float   gflux(record, lat, lon ) ;
                gflux:long_name = "Ground heat flux" ;
                gflux:units = "W / m2" ;
                gflux:_FillValue = -9999.f ;
                gflux:navigation = "nav" ;

        float   vert_sshr_trop(record, lat, lon ) ;
                vert_sshr_trop:long_name = "vertical speed shear" ;
                vert_sshr_trop:units = "1/s" ;
                vert_sshr_trop:_FillValue = -9999.f ;
                vert_sshr_trop:navigation = "nav" ;

        float   T_lbls(record, soil_lpdg, lat, lon ) ;
                T_lbls:long_name = "Temperature layer between 2 depth below 
surface" ;
                T_lbls:units = "K" ;
                T_lbls:_FillValue = -9999.f ;
                T_lbls:navigation = "nav" ;

        float   Z_htfl(record, lat, lon ) ;
                Z_htfl:long_name = "geopotential height" ;
                Z_htfl:units = "gp m" ;
                Z_htfl:_FillValue = -9999.f ;
                Z_htfl:navigation = "nav" ;

        float   RH_htfl(record, lat, lon ) ;
                RH_htfl:long_name = "relative humidity" ;
                RH_htfl:units = "percent" ;
                RH_htfl:_FillValue = -9999.f ;
                RH_htfl:navigation = "nav" ;

        float   reserved(record, level, lat, lon ) ;
                reserved:long_name = "" ;
                reserved:_FillValue = -9999.f ;
                reserved:navigation = "nav" ;

        float   reserved_lbls(record, soil_lpdg, lat, lon ) ;
                reserved_lbls:long_name = "Volumetric soil moisture content" ;
                reserved_lbls:_FillValue = -9999.f ;
                reserved_lbls:navigation = "nav" ;

        float   reserved_atm(record, lat, lon ) ;
                reserved_atm:long_name = "Cloud workfunction" ;
                reserved_atm:_FillValue = -9999.f ;
                reserved_atm:navigation = "nav" ;

        float   reserved_sfc(record, lat, lon ) ;
                reserved_sfc:long_name = "Meridional flux of gravity wave 
stress" ;
                reserved_sfc:_FillValue = -9999.f ;
                reserved_sfc:navigation = "nav" ;

// global attributes:
                :history = "created by gribtonc from HRS broadcast" ;
                :title = "NMC Global Product Set" ;
                :Conventions = "NUWG" ;
                :version = 0.0 ;                // still just a draft

data:

 level = 1000, 975, 950, 925, 900, 850, 800, 750, 700, 650, 600, 550, 500, 450,
         400, 350, 300, 250, 200, 150, 100, 70, 50, 30, 20, 10 ;
 soil_lpdg_top = 0, 0, 5, 10, 60, 150 ;
 soil_lpdg_bot = 5, 10, 30, 200, 90, 180 ;
 lpdg_bot =  0, 0 ;
 lpdg_top = 30, 180 ;
 fhg = 2, 10 ;
 fh = 1829, 2743, 3658 ;
 ls_all_top = 0.0, 0.33, 0.44, 0.44, 0.47, 0.84, 0.72 ;
 ls_all_bot = 1.0, 1.0, 1.0, 0.72, 1.0, 0.98, 0.94 ;
 sigma = 0.9950 ;
 model_id = 77, 81, 96;

 // Navigation
 nav_model = "GRIB1" ;
 grid_type_code = 0 ;
 grid_type  = "Latitude/Longitude" ;
 grid_name = "Global 1.0 x 1.0 degree grid" ;
 grid_center = 7 ;   // NMC
 grid_number = 3 ;   
 i_dim = "lon" ;
 j_dim = "lat" ;
 Ni = 360 ;
 Nj = 181 ;
 La1 = 90.0 ;
 Lo1 = 0.0 ;
 La2 = -90.0 ;
 Lo2 = 359.0 ;
 Di = 1.0 ;
 Dj = 1.0 ;
 ResCompFlag = 0x80 ;

 lon =   0,    1,    2,    3,    4,    5,    6,    7,    8,    9,
        10,   11,   12,   13,   14,   15,   16,   17,   18,   19,
        20,   21,   22,   23,   24,   25,   26,   27,   28,   29,
        30,   31,   32,   33,   34,   35,   36,   37,   38,   39,
        40,   41,   42,   43,   44,   45,   46,   47,   48,   49,
        50,   51,   52,   53,   54,   55,   56,   57,   58,   59,
        60,   61,   62,   63,   64,   65,   66,   67,   68,   69,
        70,   71,   72,   73,   74,   75,   76,   77,   78,   79,
        80,   81,   82,   83,   84,   85,   86,   87,   88,   89,
        90,   91,   92,   93,   94,   95,   96,   97,   98,   99,
       100,  101,  102,  103,  104,  105,  106,  107,  108,  109,
       110,  111,  112,  113,  114,  115,  116,  117,  118,  119,
       120,  121,  122,  123,  124,  125,  126,  127,  128,  129,
       130,  131,  132,  133,  134,  135,  136,  137,  138,  139,
       140,  141,  142,  143,  144,  145,  146,  147,  148,  149,
       150,  151,  152,  153,  154,  155,  156,  157,  158,  159,
       160,  161,  162,  163,  164,  165,  166,  167,  168,  169,
       170,  171,  172,  173,  174,  175,  176,  177,  178,  179,
       180,  181,  182,  183,  184,  185,  186,  187,  188,  189,
       190,  191,  192,  193,  194,  195,  196,  197,  198,  199,
       200,  201,  202,  203,  204,  205,  206,  207,  208,  209,
       210,  211,  212,  213,  214,  215,  216,  217,  218,  219,
       220,  221,  222,  223,  224,  225,  226,  227,  228,  229,
       230,  231,  232,  233,  234,  235,  236,  237,  238,  239,
       240,  241,  242,  243,  244,  245,  246,  247,  248,  249,
       250,  251,  252,  253,  254,  255,  256,  257,  258,  259,
       260,  261,  262,  263,  264,  265,  266,  267,  268,  269,
       270,  271,  272,  273,  274,  275,  276,  277,  278,  279,
       280,  281,  282,  283,  284,  285,  286,  287,  288,  289,
       290,  291,  292,  293,  294,  295,  296,  297,  298,  299,
       300,  301,  302,  303,  304,  305,  306,  307,  308,  309,
       310,  311,  312,  313,  314,  315,  316,  317,  318,  319,
       320,  321,  322,  323,  324,  325,  326,  327,  328,  329,
       330,  331,  332,  333,  334,  335,  336,  337,  338,  339,
       340,  341,  342,  343,  344,  345,  346,  347,  348,  349,
       350,  351,  352,  353,  354,  355,  356,  357,  358,  359 ;

 lat =  90,   89,   88,   87,   86,   85,   84,   83,   82,   81,
        80,   79,   78,   77,   76,   75,   74,   73,   72,   71,
        70,   69,   68,   67,   66,   65,   64,   63,   62,   61,
        60,   59,   58,   57,   56,   55,   54,   53,   52,   51,
        50,   49,   48,   47,   46,   45,   44,   43,   42,   41,
        40,   39,   38,   37,   36,   35,   34,   33,   32,   31,
        30,   29,   28,   27,   26,   25,   24,   23,   22,   21,
        20,   19,   18,   17,   16,   15,   14,   13,   12,   11,
        10,    9,    8,    7,    6,    5,    4,    3,    2,    1,
         0,   -1,   -2,   -3,   -4,   -5,   -6,   -7,   -8,   -9,
       -10,  -11,  -12,  -13,  -14,  -15,  -16,  -17,  -18,  -19,
       -20,  -21,  -22,  -23,  -24,  -25,  -26,  -27,  -28,  -29,
       -30,  -31,  -32,  -33,  -34,  -35,  -36,  -37,  -38,  -39,
       -40,  -41,  -42,  -43,  -44,  -45,  -46,  -47,  -48,  -49,
       -50,  -51,  -52,  -53,  -54,  -55,  -56,  -57,  -58,  -59,
       -60,  -61,  -62,  -63,  -64,  -65,  -66,  -67,  -68,  -69,
       -70,  -71,  -72,  -73,  -74,  -75,  -76,  -77,  -78,  -79,
       -80,  -81,  -82,  -83,  -84,  -85,  -86,  -87,  -88,  -89,
       -90 ;

}
/*
 *   Copyright 1995, University Corporation for Atmospheric Research.
 *   See ../COPYRIGHT file for copying and redistribution conditions.
 */
/* $Id: levels.h,v 1.12 2000/02/15 18:25:39 rkambic Exp $ */

/* GRIB levels */

#ifndef LEVELS_H_
#define LEVELS_H_

#define LEVEL_SURFACE   1       /* surface of the Earth */
#define LEVEL_CLOUD_BASE        2 /* cloud base level */
#define LEVEL_CLOUD_TOP 3       /* cloud top level */
#define LEVEL_ISOTHERM  4       /* 0 degree isotherm level */
#define LEVEL_ADIABAT   5       /* adiabatic condensation level */
#define LEVEL_MAX_WIND  6       /* maximium wind speed level */
#define LEVEL_TROP      7       /* at the tropopause */
#define LEVEL_TOP       8       /* nominal top of atmosphere */
#define LEVEL_SEABOT    9       /* sea bottom */
#define LEVEL_TMPL      20      /* temperature in 1/100 K (2 octets) */
#define LEVEL_ISOBARIC  100     /* isobaric level */
#define LEVEL_LISO      101     /* layer between two isobaric levels */
#define LEVEL_MEAN_SEA  102     /* mean sea level */
#define LEVEL_FH        103     /* fixed height level */
#define LEVEL_LFHM      104     /* layer between 2 height levels above MSL */
#define LEVEL_FHG       105     /* fixed height above ground */
#define LEVEL_LFHG      106     /* layer between 2 height levels above ground */
#define LEVEL_SIGMA     107     /* sigma level */
#define LEVEL_LS        108     /* layer between 2 sigma levels */
#define LEVEL_HY        109     /* Hybrid level */
#define LEVEL_LHY       110     /* Layer between 2 hybrid levels */
#define LEVEL_Bls       111     /* Depth below land surface */
#define LEVEL_LBls      112     /* Layer between 2 depths below land surface */
#define LEVEL_ISEN      113     /* Isentropic (theta) level */
#define LEVEL_LISEN     114     /* Layer between 2 isentropic (theta) levels */
#define LEVEL_PDG       115     /* level at specified pressure difference from 
ground */
#define LEVEL_LPDG      116     /* layer between levels at specif. pressure 
diffs from ground */
#define LEVEL_PV        117     /* potential vorticity */
#define LEVEL_ETAL      119     /* ETA value in 1/10000 */
#define LEVEL_LETA      120     /* layer between two ETA levels */
#define LEVEL_LISH      121     /* layer between 2 isobaric surfaces (high 
precision) */
#define LEVEL_FHGH      125     /* height level above ground (high precision) */
#define LEVEL_LSH       128     /* layer between 2 sigma levels (high 
precision) */
#define LEVEL_LISM      141     /* layer between 2 isobaric surfaces (mixed 
precision) */
#define LEVEL_DBS       160     /* depth below sea level */
#define LEVEL_ATM       200     /* entire atmosphere considered as a single 
layer */
#define LEVEL_OCEAN     201     /* entire ocean considered as a single layer */

#define LEVEL_HTFL      204     /* Highest tropospheric freezing level */
#define LEVEL_BCBL      209     /* Boundary layer cloud bottom level */
#define LEVEL_BCTL      210     /* Boundary layer cloud top level */
#define LEVEL_BCY       211     /* Boundary layer cloud layer */
#define LEVEL_LCBL      212     /* Low cloud bottom level */
#define LEVEL_LCTL      213     /* Low cloud top level */
#define LEVEL_LCY       214     /* Low cloud layer */
#define LEVEL_MCBL      222     /* Middle cloud bottom level */
#define LEVEL_MCTL      223     /* Middle cloud top level */
#define LEVEL_MCY       224     /* Middle cloud layer */
#define LEVEL_HCBL      232     /* High cloud bottom level */
#define LEVEL_HCTL      233     /* High cloud top level */
#define LEVEL_HCY       234     /* Highcloud layer */
#define LEVEL_CCBL      242     /* convective cloud bottom level */
#define LEVEL_CCTL      243     /* convective cloud top level */
#define LEVEL_CCY       244     /* convective cloud layer */
#define LEVEL_MTHE      246     /* maximum equivalent potential temperature 
level */
#define LEVEL_EHLT      247     /* equilibrium level */
#define LEVEL_FL        9999    /* FAA flight level */


#ifdef __cplusplus
extern "C" double mblev(int* levels);
extern "C" char* levelname(int level);
extern "C" char* levelsuffix(int level);
extern "C" char* levelunits(int level);
extern "C" long level_index(double level, float* levels, long nlevels);
extern "C" long layer_index(double top, double bot, float* tops,
                            float* bots, long nlayers);
extern "C" int level1(int flag, int* levels);
extern "C" int level2(int flag, int* levels);
#elif defined(__STDC__)
extern double mblev(int* levels);
extern char* levelname(int level);
extern char* levelsuffix(int level);
extern char* levelunits(int level);
extern long level_index(double level, float* levels, long nlevels);
extern long layer_index(double top, double bot, float* tops,
                            float* bots, long nlayers);
extern int level1(int flag, int* levels);
extern int level2(int flag, int* levels);
#else
extern double mblev ( /* int* levels */ );
#define const
extern char* levelname ( /* int level */ );
extern char* levelsuffix ( /* int level */ );
extern char* levelunits ( /* int level */ );
extern long level_index ( /* double level, float* levels, long nlevels */ );
extern long layer_index( /* double top, double bot, float* tops,
                            float* bots, long nlayers */ );
extern int level1( /* int flag, int* levels */ );
extern int level2( /* int flag, int* levels */ );
#endif

#endif /* LEVELS_H_ */
/*
 *   Copyright 1996 University Corporation for Atmospheric Research
 *   See ../COPYRIGHT file for copying and redistribution conditions.
 */
/* $Id: levels.c,v 1.16 2000/02/15 18:25:17 rkambic Exp $ */

#include "ulog.h"
#include "levels.h"

#include <math.h>
#define float_near(x,y) ((float)((y) + 0.1*fabs((x)-(y))) == (float)(y)) /* 
true if x is "close to" y */

/*
 * Atmospheric level in mb from two 8-bit integers in GRIB product
 */
double
mblev(levels)
     int* levels;
{
    return 256.*levels[0] + levels[1];
}

/*
 * Search in table for specified level.  Returns index
 * of value in table, or -1 if the value was not found.
 */
long
level_index(level, levels, nlevels)
     double level;      /* level sought */
     float* levels;     /* table of levels */
     long nlevels;      /* number of levels in table */
{
    int nn = 0;

    while (nlevels--) {
        float ll;

        ll = *levels++;
        if (float_near(level, ll)) /* true if level "is close to" ll */
            break;
        nn++;
    }
    return nlevels==-1 ? -1 : nn;
}


/*
 * Search in table for specified layer.  Returns index
 * of value in table, or -1 if the value was not found.
 */
long
layer_index(top, bot, tops, bots, nlayers)
     double top;                /* top of layer sought */
     double bot;                /* bottom of layer sought */
     float* tops;               /* table of layer tops */
     float* bots;               /* table of layer bottoms */
     long nlayers;              /* number of layers in table */
{
    int nn = 0;

    while (nlayers--) {
        if (float_near(top, *tops) && float_near(bot, *bots))
            break;
        tops++;
        bots++;
        nn++;
    }
    return nlayers==-1 ? -1 : nn;
}


/*
 * Return name for GRIB level, given GRIB level code.
 */
char *
levelname(ii)
     int ii;
{
    switch(ii){
    case LEVEL_SURFACE: 
        return "Surface";
    case LEVEL_CLOUD_BASE: 
        return "Cloud Base";
    case LEVEL_CLOUD_TOP: 
        return "Cloud Top";
    case LEVEL_ISOTHERM: 
        return "0 Isotherm";
    case LEVEL_ADIABAT: 
        return "Adiabatic Condensation";
    case LEVEL_MAX_WIND: 
        return "Maximum Wind";
    case LEVEL_TROP: 
        return "Tropopause";
    case LEVEL_TOP:
        return "Top of Atmosphere";
    case LEVEL_SEABOT:
        return "Sea Bottom";
    case LEVEL_TMPL:
        return "Temperature in 1/100 K";
    case LEVEL_ISOBARIC: 
        return "Isobaric";
    case LEVEL_LISO: 
        return "Layer Between Two Isobaric";
    case LEVEL_MEAN_SEA: 
        return "Mean Sea";
    case LEVEL_FH: 
        return "Fixed Height";
    case LEVEL_LFHM: 
        return "Layer Between Two Heights Above MSL";
    case LEVEL_FHG: 
        return "Fixed Height Above Ground";
    case LEVEL_LFHG: 
        return "Layer Between Two Fixed Heights Above Ground";
    case LEVEL_SIGMA: 
        return "Sigma";
    case LEVEL_LS: 
        return "Layer Between Two Sigma";
    case LEVEL_HY:
        return "Hybrid level";
    case LEVEL_LHY:
        return "Layer between 2 hybrid levels";
    case LEVEL_Bls: 
        return "Below Land Surface";
    case LEVEL_LBls: 
        return "Layer Between Two Depths Below Land Surface";
    case LEVEL_ISEN:
        return "Isentropic (theta) level";
    case LEVEL_LISEN:
        return "Layer between 2 isentropic (theta) levels";
    case LEVEL_PDG:
        return "level at specified pressure difference from ground to level";
    case LEVEL_LPDG:
        return "layer between 2 levels at specified pressure differences from 
ground to levels";
    case LEVEL_PV:
        return "potential vorticity";
    case LEVEL_ETAL:
        return "ETA level";
    case LEVEL_LETA: 
        return "Layer between two ETA levels";
    case LEVEL_LISH: 
        return "Layer Between Two Isobaric Surfaces, High Precision";
    case LEVEL_FHGH:
        return "Height level above ground (high precision)";
    case LEVEL_LSH: 
        return "Layer Between Two Sigma Levels, High Precision";
    case LEVEL_LISM: 
        return "Layer Between Two Isobaric Surfaces, Mixed Precision";
    case LEVEL_DBS: 
        return "Depth Below Sea";
    case LEVEL_ATM:
        return "Entire atmosphere considered as a single layer";
    case LEVEL_OCEAN:
        return "Entire ocean considered as a single layer";
    case LEVEL_HTFL:
        return "Highest tropospheric freezing level";
    case LEVEL_BCBL:
        return "Boundary layer cloud bottom level";
    case LEVEL_BCTL:
        return "Boundary layer cloud top level";
    case LEVEL_BCY:
        return "Boundary layer cloud layer";
    case LEVEL_LCBL:
        return "Low cloud bottom level";
    case LEVEL_LCTL:
        return "Low cloud top level";
    case LEVEL_LCY:
        return "Low cloud layer";
    case LEVEL_MCBL:
        return "Middle cloud bottom level";
    case LEVEL_MCTL:
        return "Middle cloud top level";
    case LEVEL_MCY:
        return "Middle cloud layer";
    case LEVEL_HCBL:
        return "High cloud bottom level";
    case LEVEL_HCTL:
        return "High cloud top level";
    case LEVEL_HCY:
        return "Highcloud layer";
    case LEVEL_CCBL:
        return "Convective cloud bottom level";
    case LEVEL_CCTL:
        return "Convective cloud top level";
    case LEVEL_CCY:
        return "Convective cloud layer";
    case LEVEL_MTHE:
        return "maximum equivalent potential temperature level";
    case LEVEL_EHLT:
        return "equilibrium level";
    case LEVEL_FL:
        return "flight_level";
    }
    /* default */
    uerror("unknown level: %d", ii);
    return "reserved or unknown";
}


/*
 * Returns level suffix, used in netCDF names for variables on special
 * levels and in "gribdump -b" level abbreviations.
 */
char *
levelsuffix(lev)
    int lev;
{
                                /* Note: If any suffixes are added or
                                   changed, they must be added or changed in
                                   the function grib_pcode() as well. */
    switch(lev) {
    case LEVEL_SURFACE: return "sfc"; /* surface of the Earth */
    case LEVEL_CLOUD_BASE: return "clbs"; /* cloud base level */
    case LEVEL_CLOUD_TOP: return "cltp"; /* cloud top level */
    case LEVEL_ISOTHERM: return "frzlvl"; /* 0 degree isotherm level */
    case LEVEL_ADIABAT: return "adcn"; /* adiabatic condensation level */
    case LEVEL_MAX_WIND: return "maxwind"; /* maximium wind speed level */
    case LEVEL_TROP: return "trop"; /* at the tropopause */
    case LEVEL_TOP: return "topa"; /* nominal top of atmosphere */
    case LEVEL_SEABOT: return "sbot"; /* sea bottom */
    case LEVEL_TMPL: return "tmpl"; /* temperature in 1/100 K */
    case LEVEL_ISOBARIC: return ""; /* isobaric level */
    case LEVEL_LISO: return "liso"; /* layer between two isobaric levels */
    case LEVEL_MEAN_SEA: return "msl"; /* mean sea level */
    case LEVEL_FH: return "fh"; /* fixed height level */
    case LEVEL_LFHM: return "lfhm"; /* layer between 2 height levels above MSL 
*/
    case LEVEL_FHG: return "fhg"; /* fixed height above ground */
    case LEVEL_LFHG: return "lfhg"; /* layer between 2 height levels above 
ground */
    case LEVEL_SIGMA: return "sigma"; /* sigma level */
    case LEVEL_LS: return "ls"; /* layer between 2 sigma levels */
    case LEVEL_HY: return "hybr"; /* Hybrid level */
    case LEVEL_LHY: return "lhyb"; /* Layer between 2 hybrid levels */
    case LEVEL_Bls: return "bls"; /* Depth below land surface */
    case LEVEL_LBls: return "lbls"; /* Layer between 2 depths below land 
surface */
    case LEVEL_ISEN: return "isen"; /* Isentropic (theta) level */
    case LEVEL_LISEN: return "lisn"; /* Layer between 2 isentropic (theta) 
levels */
    case LEVEL_PDG: return "pdg"; /* level at specified pressure difference 
from ground */
    case LEVEL_LPDG: return "lpdg"; /* layer between levels at specif. pressure 
diffs from ground */
    case LEVEL_PV: return "pv"; /* level of specified potential vorticity */
    case LEVEL_ETAL: return "etal"; /* ETA level */
    case LEVEL_LETA: return "leta";     /* layer between 2 ETA levels */
    case LEVEL_LISH: return "lish"; /* layer between 2 isobaric surfaces (high 
precision) */
    case LEVEL_FHGH: return "fhgh"; /* height level above ground (high 
precision) */
    case LEVEL_LSH: return "lsh"; /* layer between 2 sigma levels (high 
precision) */
    case LEVEL_LISM: return "lism"; /* layer between 2 isobaric surfaces (mixed 
precision) */
    case LEVEL_DBS: return "dbs"; /* depth below sea level */
    case LEVEL_ATM: return "atm"; /* entire atmosphere considered as a single 
layer */
    case LEVEL_OCEAN: return "ocn"; /* entire ocean considered as a single 
layer */
    case LEVEL_HTFL: return "htfl"; /* Highest tropospheric freezing level */
    case LEVEL_BCBL: return "bcbl"; /* Boundary layer cloud bottom level */
    case LEVEL_BCTL: return "bctl"; /* Boundary layer cloud top level */
    case LEVEL_BCY: return "bcy"; /* Boundary layer cloud layer */
    case LEVEL_LCBL: return "lcbl"; /* Low cloud bottom level */
    case LEVEL_LCTL: return "lctl"; /* Low cloud top level */
    case LEVEL_LCY: return "lcy"; /* Low cloud layer */
    case LEVEL_MCBL: return "mcbl"; /* Middle cloud bottom level */
    case LEVEL_MCTL: return "mctl"; /* Middle cloud top level */
    case LEVEL_MCY: return "mcy"; /* Middle cloud layer */
    case LEVEL_HCBL: return "hcbl"; /* High cloud bottom level */
    case LEVEL_HCTL: return "hctl"; /* High cloud top level */
    case LEVEL_HCY: return "hcy"; /* Highcloud layer */
    case LEVEL_CCBL: return "ccbl"; /* convective cloud bottom height */
    case LEVEL_CCTL: return "cctl"; /* convective cloud top height */
    case LEVEL_CCY: return "ccy"; /* convective cloud layer */
    case LEVEL_MTHE: return "mthe"; /* maximum equivalent potential temperature 
pressure */
    case LEVEL_EHLT: return "ehlt"; /* equilibrium level height */
    case LEVEL_FL: return "fl"; /* FAA flight level */
    }
                                /* default: */
    uerror("bad level flag: %d", lev);
    return "";
}

/*
 * Returns int for first level (if 2 levels) or level (if only one level)
 */
int
level1(flag, ii)
    int flag;                   /* GRIB level flag */
    int *ii;                    /* GRIB levels */
{
    switch(flag){
    case LEVEL_SURFACE: 
    case LEVEL_CLOUD_BASE: 
    case LEVEL_CLOUD_TOP: 
    case LEVEL_ISOTHERM: 
    case LEVEL_ADIABAT: 
    case LEVEL_MAX_WIND: 
    case LEVEL_TROP: 
    case LEVEL_TOP:
    case LEVEL_SEABOT:
    case LEVEL_MEAN_SEA: 
    case LEVEL_ATM:
    case LEVEL_OCEAN:
    case LEVEL_HTFL:
    case LEVEL_LCBL:
    case LEVEL_LCTL:
    case LEVEL_LCY:
    case LEVEL_MCBL:
    case LEVEL_MCTL:
    case LEVEL_MCY:
    case LEVEL_HCBL:
    case LEVEL_HCTL:
    case LEVEL_HCY:
    case LEVEL_FL:
        return 0;
    case LEVEL_TMPL:
    case LEVEL_ISOBARIC: 
    case LEVEL_FH: 
    case LEVEL_FHG: 
    case LEVEL_SIGMA: 
    case LEVEL_HY:
    case LEVEL_Bls: 
    case LEVEL_ISEN:
    case LEVEL_PDG:
    case LEVEL_PV:
    case LEVEL_ETAL:
    case LEVEL_FHGH:
    case LEVEL_DBS: 
        return 256*ii[0]+ii[1]; /* 2-octet level */
    case LEVEL_LISO: 
    case LEVEL_LFHM: 
    case LEVEL_LFHG: 
    case LEVEL_LS: 
    case LEVEL_LHY:
    case LEVEL_LBls: 
    case LEVEL_LISEN:
    case LEVEL_LPDG:
    case LEVEL_LETA: 
    case LEVEL_LISH: 
    case LEVEL_LSH: 
    case LEVEL_LISM: 
        return ii[0];           /* 1-octet level */
    }
    /* default */
    uerror("unknown level: %d", ii);
    return -1;
}


/*
 * Returns int for second level (if 2 levels) or 0 (if only one level)
 */
int
level2(flag, ii)
    int flag;                   /* GRIB level flag */
    int *ii;                    /* GRIB levels */
{
    switch(flag){
    case LEVEL_SURFACE: 
    case LEVEL_CLOUD_BASE: 
    case LEVEL_CLOUD_TOP: 
    case LEVEL_ISOTHERM: 
    case LEVEL_ADIABAT: 
    case LEVEL_MAX_WIND: 
    case LEVEL_TROP: 
    case LEVEL_TOP:
    case LEVEL_SEABOT:
    case LEVEL_MEAN_SEA: 
    case LEVEL_ATM:
    case LEVEL_OCEAN:
    case LEVEL_HTFL:
    case LEVEL_LCBL:
    case LEVEL_LCTL:
    case LEVEL_LCY:
    case LEVEL_MCBL:
    case LEVEL_MCTL:
    case LEVEL_MCY:
    case LEVEL_HCBL:
    case LEVEL_HCTL:
    case LEVEL_HCY:
    case LEVEL_FL:
    case LEVEL_TMPL: 
    case LEVEL_ISOBARIC: 
    case LEVEL_FH: 
    case LEVEL_FHG: 
    case LEVEL_SIGMA: 
    case LEVEL_HY:
    case LEVEL_Bls: 
    case LEVEL_ISEN:
    case LEVEL_PDG:
    case LEVEL_PV:
    case LEVEL_ETAL:
    case LEVEL_FHGH:
    case LEVEL_DBS: 
        return 0;
    case LEVEL_LISO: 
    case LEVEL_LFHM: 
    case LEVEL_LFHG: 
    case LEVEL_LS: 
    case LEVEL_LHY:
    case LEVEL_LBls: 
    case LEVEL_LISEN:
    case LEVEL_LPDG:
    case LEVEL_LETA: 
    case LEVEL_LISH: 
    case LEVEL_LSH: 
    case LEVEL_LISM: 
        return ii[1];
    }
    /* default */
    uerror("unknown level: %d", ii);
    return -1;
}


/*
 * Return GRIB units (as a string) for various kinds of levels.
 */
char *
levelunits(ii)
{
    switch(ii){
    case LEVEL_SURFACE: 
    case LEVEL_CLOUD_BASE: 
    case LEVEL_CLOUD_TOP: 
    case LEVEL_ISOTHERM: 
    case LEVEL_ADIABAT: 
    case LEVEL_MAX_WIND: 
    case LEVEL_TROP: 
    case LEVEL_TOP:
    case LEVEL_SEABOT:
    case LEVEL_MEAN_SEA: 
    case LEVEL_HY:
    case LEVEL_LHY:
    case LEVEL_ATM:
    case LEVEL_OCEAN:
    case LEVEL_HTFL:
    case LEVEL_LCBL:
    case LEVEL_LCTL:
    case LEVEL_LCY:
    case LEVEL_MCBL:
    case LEVEL_MCTL:
    case LEVEL_MCY:
    case LEVEL_HCBL:
    case LEVEL_HCTL:
    case LEVEL_HCY:
    case LEVEL_FL:
        return "" ;
    case LEVEL_ISOBARIC: 
    case LEVEL_PDG:
    case LEVEL_LPDG:
    case LEVEL_LISH: 
        return "hPa";
    case LEVEL_LISO: 
    case LEVEL_LISM: 
        return "kPa";
    case LEVEL_FH: 
    case LEVEL_FHG: 
    case LEVEL_DBS: 
        return "meters";
    case LEVEL_LFHM: 
    case LEVEL_LFHG: 
        return "hm" ;
    case LEVEL_SIGMA: 
        return ".0001";         /* dimensionless */
    case LEVEL_LS: 
    case LEVEL_LETA: 
        return ".01";           /* dimensionless */
    case LEVEL_Bls: 
    case LEVEL_LBls: 
    case LEVEL_FHGH:
        return "cm";
    case LEVEL_ISEN:
    case LEVEL_LISEN:
        return "degK";
    case LEVEL_TMPL:
        return ".01 degK";
    case LEVEL_LSH: 
        return ".001";
    case LEVEL_PV:
        return ".000001 K m2/kg/sec";
    case LEVEL_ETAL:
        return ".0001";
    }
    /* default */
    return "unknown" ;
}
/*
 *   Copyright 1996 University Corporation for Atmospheric Research
 *   See ../COPYRIGHT file for copying and redistribution conditions.
 */
/* $Id: nc.c,v 1.33 2000/04/11 22:51:34 rkambic Exp $ */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#include <netcdf.h>

#include "ulog.h"
#include "mkdirs_open.h"
#include "nc.h"
#include "nuwg.h"
#include "emalloc.h"
#include "params.h"
#include "units.h"
#include "levels.h"
#include "timeunits.h"
#include "gbds.h"               /* only for FILL_VAL */
#include "gdes.h"
#include "recs.h"

#ifndef FILL_NAME
#define FILL_NAME       "_FillValue"
#endif


typedef struct levels_table {
    int id;                     /* dimension id of netCDF dimension */
    float *vals;                /* levels */
    long num;                   /* number of levels */
    utUnit *bunitp;             /* level units */
} levels_table;


typedef struct layers_table {
    int id;                     /* dimension id of netCDF dimension */
    float *tops;                /* top levels of layers  */
    float *bots;                /* bottom levels of layers  */
    long num;                   /* number of layers */
    utUnit *bunitp;             /* top (and bottom) level units */
} layers_table;


typedef struct tris_table {
    int id;                     /* dimension id of netCDF dimension */
    float *starts;              /* starts of time ranges */
    float *ends;                /* ends of time ranges */
    long num;                   /* number of ranges */
} tris_table;


#ifdef __STDC__
static ncdim* new_dim(int dimid);
static ncvar* new_var(char* ncname, int varid);
static void free_var(ncvar* var);
static void free_dim(ncdim* dim);
static char* parmname(ncfile* nc, int parm, int level);
static int make_ncfile(char* ncname, ncfile* out);
static void free_ncfile(ncfile* np);
static levels_table* getlevtab(ncfile* nc, ncvar* var);
static layers_table* getlaytab(ncfile* nc, ncvar* var);
static long getlev(product_data* pp, ncfile* nc,
                   ncvar* var);
static long gettri(product_data* pp, ncfile* nc,
                   ncvar* var);
static int make_var(char* ncname, int varid, ncvar* out);
static int var_as_int(ncfile* nc, enum ncpart comp, int* val);
static int var_as_float(ncfile* nc, enum ncpart comp, float* val);
static int var_as_lset(ncfile* nc, enum ncpart comp, lset* list);
static void varerr(ncfile* nc, enum ncpart comp);
static int make_navgrid(ncfile* nc, navinfo* nav);
static int make_navinfo(ncfile* nc, navinfo* nav);
static void free_navinfo(navinfo* np);
static navinfo* new_navinfo(ncfile* nc);
static int gd_fne_err(product_data* pp, ncfile* nc, enum ncpart comp, double 
pval, double nval);
static int gd_ine_err(product_data* pp, ncfile* nc, enum ncpart comp, int pval, 
int nval);
static int gd_igt_err(product_data* pp, ncfile* nc, enum ncpart comp, int pval, 
int nval);
static int check_gd(product_data* pp, ncfile* nc);
static int check_pp(product_data* pp, ncfile* nc);
static int subgrid(ncfile* nc, product_data* pp, long* ix0, long* ix1);
#endif

int ncid;                       /* netCDF id of open netCDF output file.
                                 * This is at file scope so routine registered
                                 * with atexit() can get it to close file. */

static ncdim *
new_dim(dimid)
    int dimid;
{
    char dimname[MAX_NC_NAME];
    long size;
    ncdim *out = (ncdim *)emalloc(sizeof(ncdim));
    
    if (ncdiminq(ncid, dimid, dimname, &size) == -1) {
        return 0;
    }
    out->name = estrdup(dimname);
    out->id = dimid;
                                /* We don't cache size because it may change,
                                   e.g. adding records, and we don't want to
                                   have to keep cached value up to date.  To
                                   get size, use ncdiminq(). */

    return out;
}


static int
make_var(ncname, varid, out)
    char *ncname;               /* netCDF pathanme, only used in error msg */
    int varid;                  /* variable ID */
    ncvar *out;                 /* place to put constructed ncvar */
{
    char varname[MAX_NC_NAME];
    nc_type type;
    int ndims;
    int dims[MAX_VAR_DIMS];
    int id;
    
    if(ncvarinq(ncid, varid, varname, &type, &ndims, dims, (int *)0) == -1) {
        return -1;
    }

    out->id = varid;
    out->name = estrdup(varname);
    out->type = type;
    out->ndims = ndims;
    out->dims = (int *)emalloc(ndims * sizeof(int));
    for (id = 0; id < ndims; id++)
        out->dims[id] = dims[id];

                                /* get value of _FillValue attribute, if any,
                                   as a float */
    {
        nc_type atttype;
        int attlen;

        if (ncattinq(ncid, varid, FILL_NAME, &atttype, &attlen) == -1) {
            out->fillval = 0;           /* no fill-value attribute */
        } else {
            union {
                char c;
                unsigned char u;
                short s;
                long l;
                float f;
                double d;
            }fillval;
            
            out->fillval = (float *) emalloc(sizeof(float));

            if(ncattget(ncid, varid, FILL_NAME, (void *)&fillval) == -1) {
                uerror("%s: can't get % attribute for variable %s",
                       ncname, FILL_NAME, varname);
                free(out->dims);
                return -1;
            }
            switch(atttype) {
            case NC_CHAR: 
                *out->fillval = fillval.c;
                break;
            case NC_BYTE:
                *out->fillval = fillval.u;
                break;
            case NC_SHORT:
                *out->fillval = fillval.s;
                break;
            case NC_LONG:
                *out->fillval = fillval.l;
                break;
            case NC_FLOAT:
                *out->fillval = fillval.f;
                break;
            case NC_DOUBLE:
                *out->fillval = fillval.d;
                break;
            }
        }
    }

    if (get_units(ncid, varid, &out->bunitp) == -1) {
        uerror("%s: can't get units attribute for variable %s",
               ncname, varname);
        return -1;
    }

    if (out->bunitp && grib_pcode(varname) != -1) {
        out->uc = uconv(varname, out->bunitp);
    } else {
        out->uc = 0;
    }
    return 0;
}


static void
free_var(var)
    ncvar *var;
{
    if (var) {
        if(var->name)
            free(var->name);
        if(var->dims)
            free(var->dims);
        if(var->bunitp)
            free(var->bunitp);
        if(var->fillval)
            free(var->fillval);
        free(var);
    }
}


/*
 * Creates a new ncvar structure and fills it in with the information from
 * the open netCDF file whose handle is ncid.  A pointer to the structure is
 * returned, or 0 on failure.
 */
static ncvar *
new_var(ncname, varid)
    char *ncname;
{
    ncvar *out;

    if (varid == -1)            /* handle common failure case with message
                                   at higher level */
        return 0;

    out = (ncvar *)emalloc(sizeof(ncvar));
    if (make_var(ncname, varid, out) != 0) {
        free_var(out);
        return 0;
    }

    return out;
}


static void
free_dim(dim)
    ncdim *dim;
{
    if (dim) {
        if(dim->name)
            free(dim->name);
        free(dim);
    }
}


/*
 * Name of environment variable containing directory in which to search for
 * CDL files if not found relative to directory from which this process is
 * invoked.
 */
#define LDM_ETCDIR      "LDM_ETCDIR"

/*
 * Checks to see if netCDF file with specified name exists.  If not,
 * makes netCDF file from CDL template file.
 * Returns netCDF file ID on success, or -1 on error.
 */
int
cdl_netcdf (cdlname, ncname)
     char *cdlname;     /* CDL file specifying netCDF structure */
     char *ncname;      /* filename of netcdf file to be created */
{
    char cmnd[2*_POSIX_PATH_MAX+20];
    char *cdlfile = cdlname;
    static char *cdldir = 0;
    char envcdl[_POSIX_PATH_MAX];

    ncopts = 0;                 /* turn off netCDF error messages from
                                   library, means we will have to check all
                                   netCDF status returns and interpret */

    if (access(ncname, (R_OK|W_OK)) != 0) { /* no writable ncname exists */
        if (cdlfile == 0) {
            uerror("%s doesn't exist, and didn't specify a CDL filename",
                   ncname);
            return -1;
        }

        /* Try to create any directories in output path that don't exist */
        if (diraccess(ncname,  (R_OK | W_OK), !0) == -1) {
            serror("can't access directories leading to %s", ncname);
            return -1;
        }

        /* If CDL file not found, look in environment variable LDM_ETCDIR */
        if (access(cdlname, R_OK) == -1) { /* no CDL file or unreadable */
            if (cdldir == 0) {  /* executed once, when cdldir first needed */
                char *ldm_etcdir = getenv(LDM_ETCDIR);
                int slen;
                
                if (ldm_etcdir == 0) {
                    uerror("CDL file %s not found & LDM_ETCDIR not in 
environment",
                           cdlname);
                    return -1;
                }
                slen = strlen(ldm_etcdir);
                cdldir = (char *)emalloc((slen+2) * sizeof(char));
                strcpy(cdldir, ldm_etcdir);
                if (cdldir[slen-1] != '/') { /* append "/" to dir name */
                    strcat(cdldir, "/");
                }
            }
            strcat(envcdl,cdldir);
            strcat(envcdl,cdlname);
            if (access(envcdl, R_OK) == -1) {
                uerror("can't find CDL file %s, or unreadable", envcdl);
                return -1;
            }
            cdlfile = envcdl;
        }
        
        (void) strcpy(cmnd, "ncgen -o ");
        (void) strcat(cmnd, ncname);
        (void) strcat(cmnd , " ");
        (void) strcat(cmnd, cdlfile);
        
        if (system(cmnd) != 0) {
            uerror("can't run \"%s\"", cmnd);
            return -1;
        }
    }
    return ncopen(ncname, NC_WRITE);
}


void
setncid(id)
    int id;
{
    ncid = id;
}


int
getncid()
{
    return ncid;
}


/* Close open netCDF file, if any */
void
nccleanup()
{
    ncclose(ncid);
}


/*
 * Creates and returns a pointer to a one-dimensional table of levels
 * from the currently-open netCDF file (handle ncid).  Returns 0 on
 * failure.
 */
static levels_table*
getlevtab(nc, var)
    ncfile *nc;                 /* currently open netCDF file */
    ncvar *var;                 /* level variable */
{
    int did;
    levdim *lp = nc->levdims;
    
    /* See if appropriate levels table already exists for this file */
    if (var->ndims < 4) {
        uerror("%s: variable %s has too few dimensions for a level",
               nc->ncname, var->name);
        return 0;
    }
    did = var->dims[1]; /* level dimension */

    while (lp) {
        if(did == lp->levtab->id) {     /* found it */
            return lp->levtab;
        }
        lp = lp->next;
    }
    /* Not, there, so we must create it */
    lp = (levdim *)emalloc(sizeof(levdim));
    lp->levtab = (levels_table *)emalloc(sizeof(levels_table));
    lp->levtab->id = did;
    lp->next = nc->levdims;

    /* Initialize array of levels */
    {
        levels_table *out = lp->levtab;
        char levname[MAX_NC_NAME];
        int levvarid;           /* variable id of level variable */
        ncvar *lev;             /* level variable */
        long start = 0;

        /* get number of levels */
        if (ncdiminq(ncid, out->id, levname, &out->num) == -1) {
            uerror("%s: can't get number of %s levels", nc->ncname, var->name);
            return 0;
        }
        out->vals = (float *) emalloc(out->num * sizeof(float));
        levvarid = ncvarid(ncid, levname);
        if(levvarid == -1) {
            uerror("%s: No %s coordinate variable for %s level",
                   nc->ncname, levname, var->name);
            return 0;
        }
        lev = nc->vars[levvarid];
                                /* Check consistency of lev variable */
        if (strcmp(lev->name, levname) != 0 ||
            lev->type != NC_FLOAT ||
            lev->ndims != 1 ||
            lev->dims[0] != out->id) {
            uerror("%s: variable %s must be float %s(%s)", nc->ncname,
                   levname, lev->name, lev->name);
            return 0;
        }

        if(get_units(ncid, levvarid, &out->bunitp) == -1) {
            uerror("%s: error getting units attribute for %s",
                   nc->ncname, levname);
            return 0;
        }

        if(ncvarget(ncid, levvarid, &start, &out->num,
                    (void *)out->vals) ==-1) {
            uerror("%s: no %s variable for level",
                   nc->ncname, levname);
            return 0;
        }
    }

    nc->levdims = lp;           /* if all goes well */
    return lp->levtab;
}


/*
 * Creates and returns a pointer to a one-dimensional table of layers
 * from the currently-open netCDF file (handle ncid).  Returns 0 on
 * failure.
 */
static layers_table*
getlaytab(nc, var)
    ncfile *nc;                 /* currently open netCDF file */
    ncvar *var;                 /* layer variable */
{
    int did;
    laydim *lp = nc->laydims;
    
    /* See if appropriate layers table already exists for this file */
    if (var->ndims < 4) {
        uerror("%s: variable %s has too few dimensions for a layer",
               nc->ncname, var->name);
        return 0;
    }
    did = var->dims[1]; /* layer dimension */

    while (lp) {
        if(did == lp->laytab->id) {     /* found it */
            return lp->laytab;
        }
        lp = lp->next;
    }
    /* Not, there, so we must create it */
    lp = (laydim *)emalloc(sizeof(laydim));
    lp->laytab = (layers_table *)emalloc(sizeof(layers_table));
    lp->laytab->id = did;
    lp->next = nc->laydims;

    /* Initialize array of layers */
    {
        layers_table *out = lp->laytab;
        char layname[MAX_NC_NAME];
        char topname[MAX_NC_NAME];
        char botname[MAX_NC_NAME];
        int topvarid;           /* variable id of layer top variable */
        int botvarid;           /* variable id of layer bottom variable */
        ncvar *top;             /* layer top variable */
        ncvar *bot;             /* layer bottom variable */
        long start = 0;

        /* get number of layers */
        if (ncdiminq(ncid, out->id, layname, &out->num) == -1) {
            uerror("%s: can't get number of %s layers", nc->ncname, var->name);
            return 0;
        }
        if (strlen(layname)  +  strlen("_top") > (size_t) MAX_NC_NAME) {
            uerror("%s: name of layer dimension too long (%s)",
                   nc->ncname, layname);
            return 0;
        }
        out->tops = (float *) emalloc(out->num * sizeof(float));
        strcpy(topname, layname);
        strcat(topname, "_top");
        topvarid = ncvarid(ncid, topname);
        if(topvarid == -1) {
            uerror("%s: No %s coordinate variable for %s layer top",
                   nc->ncname, layname, var->name);
            return 0;
        }
        top = nc->vars[topvarid];
                                /* Check consistency of top variable */
        if (strcmp(top->name, topname) != 0 ||
            top->type != NC_FLOAT ||
            top->ndims != 1 ||
            top->dims[0] != out->id) {
            uerror("%s: variable %s must be float %s(%s)", nc->ncname,
                   layname, top->name, top->name);
            return 0;
        }

        if(get_units(ncid, topvarid, &out->bunitp) == -1) {
            uerror("%s: error getting units attribute for %s",
                   nc->ncname, topname);
            return 0;
        }

        if(ncvarget(ncid, topvarid, &start, &out->num,
                    (void *)out->tops) ==-1) {
            uerror("%s: no %s variable for top of layer",
                   nc->ncname, topname);
            return 0;
        }
        out->bots = (float *) emalloc(out->num * sizeof(float));
        strcpy(botname, layname);
        strcat(botname, "_bot");
        botvarid = ncvarid(ncid, botname);
        if(botvarid == -1) {
            uerror("%s: No %s coordinate variable for %s layer bot",
                   nc->ncname, layname, var->name);
            return 0;
        }
        bot = nc->vars[botvarid];
                                /* Check consistency of bot variable */
        if (strcmp(bot->name, botname) != 0 ||
            bot->type != NC_FLOAT ||
            bot->ndims != 1 ||
            bot->dims[0] != out->id) {
            uerror("%s: variable %s must be float %s(%s)", nc->ncname,
                   layname, bot->name, bot->name);
            return 0;
        }

        if(ncvarget(ncid, botvarid, &start, &out->num,
                    (void *)out->bots) ==-1) {
            uerror("%s: no %s variable for bottom of layer",
                   nc->ncname, botname);
            return 0;
        }
    }

    nc->laydims = lp;           /* if all goes well */
    return lp->laytab;
}


/*
 * Handle levels
 */
static long
levaux(pp, nc, var)
    product_data *pp;   /* decoded GRIB data to be written */
    ncfile *nc;         /* netCDF file to be written */
    ncvar *var;         /* netCDF variable to be written */
{
    int level_flg = pp->level_flg;
    char *varname = var->name;

    /*
        Assumes level dimension is second dimension of a variable, and that
        there is a coordinate variable associated with a level, values of
        which get stored in the level table.
      */

    int levdim;
    double lev;
    long levix;

    levels_table *levtab = getlevtab(nc, var);

    if (levtab == 0) {  /* initialize table of levels */
        return -1;
    }

    lev = level1(pp->level_flg, pp->level);

    /* Must convert to units of level table */
    {
        utUnit bfunit;
        char *funits = levelunits(level_flg);
        double slope=1.;
        double intercept=0.;
        if(utScan(funits, &bfunit) != 0) { /* "from" unit */
            uerror("Error parsing unit `%s' for level %s", funits, varname);
            return -1;
        }
        if (levtab->bunitp) {
            if(utConvert(&bfunit, levtab->bunitp, &slope, &intercept) ==
               UT_ECONVERT) {
                uerror("units `%s' not conformable with variable %s:units",
                       funits, varname);
                return -1;
            }
        }
        lev = slope * lev + intercept;
    }
    levix = level_index(lev, levtab->vals, levtab->num);
    if (levix == -1) {
        uerror("%s: In %s, no %f level for %s",
               pp->header, nc->ncname, lev, varname);
        return -1;
    }
    return levix;
}


/*
 * Handle layers
 */
static long
layaux(pp, nc, var)
    product_data *pp;   /* decoded GRIB data to be written */
    ncfile *nc;         /* netCDF file to be written */
    ncvar *var;         /* netCDF variable to be written */
{
    int layer_flg = pp->level_flg;
    char *varname = var->name;

    /*
        Assumes layer dimension is second dimension of a variable, and that
        there are _top and _bot levels associated with each layer, values of
        which which get stored in the layer table.
      */

    int laydim;
    double top;
    double bot;
    long layix;

    layers_table *laytab = getlaytab(nc, var);

    if (laytab == 0) {
        return -1;
    }

    top = pp->level[0];
    bot = pp->level[1];
    /* Must convert top,bot to units of layer tables */
    {
        utUnit bfunit;
        char *funits = levelunits(layer_flg);
        double slope=1.;
        double intercept=0.;
        if(utScan(funits, &bfunit) != 0) { /* "from" unit */
            uerror("Error parsing unit `%s' for level %s", funits, varname);
            return -1;
        }
        if(laytab->bunitp) {
            if(utConvert(&bfunit, laytab->bunitp, &slope, &intercept) ==
               UT_ECONVERT) {
                uerror("units `%s' not conformable with variable %s:units",
                       funits, varname);
                return -1;
            }
        }
        top = slope * top + intercept;
        bot = slope * bot + intercept;
    }
    layix = layer_index(top, bot, laytab->tops, laytab->bots, laytab->num);
    if (layix == -1) {
        uerror("%s: In %s, no (%g,%g) level for %s",
               pp->header, nc->ncname, top, bot, varname);
        return -1;
    }
    return layix;
}


/*
 * Return netCDF level dimension index appropriate for decoded GRIB
 * product.  Returns -2 if no level dimension appropriate (e.g. for surface
 * variables) or -1 in case of failure.
 */
static long
getlev(pp, nc, var)
    product_data *pp;   /* decoded GRIB data to be written */
    ncfile *nc;         /* netCDF file to be written */
    ncvar *var;         /* netCDF variable to be written */
{
    switch (pp->level_flg) {
    /* Levels */
    case LEVEL_ISOBARIC:
    case LEVEL_FHG:
    case LEVEL_SIGMA:
    case LEVEL_HY:
    case LEVEL_FH:
    case LEVEL_Bls:
    case LEVEL_ISEN:
    case LEVEL_PDG:
    case LEVEL_FHGH:
    case LEVEL_DBS:
    case LEVEL_FL:
        return levaux(pp, nc, var);

    /* Layers */
    case LEVEL_LBls:
    case LEVEL_LFHG:
    case LEVEL_LFHM:
    case LEVEL_LHY:
    case LEVEL_LISEN:
    case LEVEL_LISH:
    case LEVEL_LISM:
    case LEVEL_LISO:
    case LEVEL_LPDG:
    case LEVEL_LS:
    case LEVEL_LSH:
        return layaux(pp, nc, var);

    /* Special levels, just one so no dimension needed */
    case LEVEL_SURFACE:
    case LEVEL_CLOUD_BASE:
    case LEVEL_CLOUD_TOP:
    case LEVEL_ISOTHERM:
    case LEVEL_ADIABAT:
    case LEVEL_MAX_WIND:
    case LEVEL_TROP:
    case LEVEL_TOP:
    case LEVEL_SEABOT:
    case LEVEL_MEAN_SEA:
    case LEVEL_ATM:
    case LEVEL_OCEAN:
        return -2;
    }
    /* default: */
    return -1;
}


/*
 * Get conventional netcdf variable name, with level indicator appended if
 * appropriate, and see if it exists in the open netCDF file.  If so return
 * name.  If not, return 0.  The name is a pointer to a static string, so
 * should be copied if needed beyond the next call to parmname.
 */
static char*
parmname(nc, parm, level)
    ncfile *nc;                 /* netCDF file */
    int parm;                   /* parameter code from GRIB product */
    int level;                  /* level flag from GRIB product */
{
    char *varname = grib_pname(parm); /* netcdf variable base name */
    int ncopts_save = ncopts;
    char *suffix;
    static char string[MAX_NC_NAME];
    char *name = string;

    if (!varname) {
        uerror("unrecognized GRIB parameter code %d", parm);
        return 0;
    }

    /* Add level modifier to name, if appropriate */
    suffix = "";
    suffix = levelsuffix(level);
    strcpy(name, varname);
    if( suffix == "" )
        uerror("var name : %s", name);

    /* The "_sfc", "_msl", and "_liso" suffixes are redundant for some
       parameters, so we explicitly exclude those here */
    if((level != LEVEL_SURFACE || !sfcparam(parm)) &&
       (level != LEVEL_MEAN_SEA || !mslparam(parm)) &&
       (level != LEVEL_LISO || !lisoparam(parm))) {
        if (suffix[0] != '\0') {
            strcat(name, "_");
            strcat(name, suffix);
        }
    }
    ncopts = 0;
    if (ncvarid(ncid, name) == -1) {
        uerror("%s: no variable %s in netCDF file", nc->ncname, name);
        name = 0;
    }

    ncopts = ncopts_save;
    return name;
}


/*
 * Stores value of a netCDF variable identified by a NUWG conventional id.
 * In case of failure, returns -1.  The value is converted from
 * whatever type is used for the netCDF variable.
 */
static int
var_as_int(nc, comp, val)
    ncfile *nc;
    enum ncpart comp;
    int *val;                   /* where to store the resulting value */

{
    ncvar *var = (ncvar *)emalloc(sizeof(ncvar));
    long start[] = {0};
    long count[] = {1};
    double buf[1];              /* generic data buffer */
    
    if(make_var(nc->ncname, nuwg_getvar(ncid, comp), var) == -1) {
        free_var(var);
        return -1;
    }
    if (ncvarget(ncid, var->id, start, count, (void *)buf) == -1) {
        free_var(var);
        return -1;
    }
    switch (var->type) {        /* return the value as an int, no
                                   matter how it is stored */
      case NC_BYTE:
        *val = *(unsigned char *) buf;
        break;
      case NC_CHAR:
        *val = *(char *) buf;
        break;
      case NC_SHORT:
        *val = *(short *) buf;
        break;
      case NC_LONG:
        *val = *(nclong *) buf;
        break;
      case NC_FLOAT:
        *val = *(float *) buf;
        break;
      case NC_DOUBLE:
        *val = *(double *) buf;
        break;
    }
    free_var(var);
    return 0;
}


/*
 * Stores value of a netCDF variable identified by a NUWG conventional id.
 * In case of failure, returns -1.  The value is converted from
 * whatever type is used for the netCDF variable.
 */
static int
var_as_float(nc, comp, val)
    ncfile *nc;
    enum ncpart comp;
    float *val;
{
    ncvar *var = (ncvar *)emalloc(sizeof(ncvar));
    long start[] = {0};
    long count[] = {1};
    double buf[1];              /* generic data buffer */
    
    if(make_var(nc->ncname, nuwg_getvar(ncid, comp), var) == -1) {
        return -1;
    }
    if (ncvarget(ncid, var->id, start, count, (void *)buf) == -1) {
        free_var(var);
        return -1;
    }
    switch (var->type) {        /* return the value as a float, no
                                   matter how it is stored */
      case NC_BYTE:
        *val = *(unsigned char *) buf;
        break;
      case NC_CHAR:
        *val = *(char *) buf;
        break;
      case NC_SHORT:
        *val = *(short *) buf;
        break;
      case NC_LONG:
        *val = *(nclong *) buf;
        break;
      case NC_FLOAT:
        *val = *(float *) buf;
        break;
      case NC_DOUBLE:
        *val = *(double *) buf;
        break;
    }
    free_var(var);
    return 0;
}


/*
 * Stores values of a netCDF variable (of longs) identified by a NUWG
 * conventional id.  Values are just stored in a list of longs, which can be
 * used as a set in which values are looked up.  In case of failure, returns
 * -1.
 */
static int
var_as_lset(nc, comp, list)
    ncfile *nc;
    enum ncpart comp;
    lset *list;                 /* where to store the resulting list */
{
    ncvar *var = (ncvar *)emalloc(sizeof(ncvar));
    static long start[MAX_VAR_DIMS];
    static long count[MAX_VAR_DIMS];
    long prod;
    int i;
    
    if(make_var(nc->ncname, nuwg_getvar(ncid, comp), var) == -1) {
        return -1;
    }
    if (var->type != NC_LONG) {
        uerror("%s: variable %s must be of type long", nc->ncname,
               nuwg_name(comp));
        free_var(var);
        return -1;
    }
    prod=1;
    for (i=0; i<var->ndims; i++) {
        start[i] = 0;
        if (ncdiminq(ncid, var->dims[i], (char *)0, &count[i]) == -1) {
            uerror("%s: can't get size of dimension for %s", nc->ncname,
                   nuwg_name(comp));
            free_var(var);
            return -1;
        }
        prod *= count[i];
    }
    list->n = prod;
    list->vals = (nclong *)emalloc(sizeof(nclong) * prod);
    if (ncvarget(ncid, var->id, start, count, (void *)list->vals) == -1) {
        uerror("%s: can't get values for %s", nc->ncname, nuwg_name(comp));
        free_var(var);
        free(list->vals);
        return -1;
    }
    free_var(var);
    return 0;
}


static void
varerr(nc,comp)
    ncfile *nc;
    enum ncpart comp;
{
    uerror("%s: no variable for %s", nc->ncname, nuwg_name(comp));
}


/*
 *  Returns 0 on success, -1 on failure.
 */
static int
make_navgrid(nc, nav )
    ncfile *nc;                 /* netCDF file */
    navinfo *nav;               /* where to put the navinfo */
{
    int *ip;
    float *fp;

    switch(nav->grid_type_code) {
    case GRID_LL:
    case GRID_RLL:
    case GRID_SLL:
    case GRID_SRLL:
    {
        gdes_ll *gg = &nav->grid.ll;
        
        if (var_as_int(nc, VAR_NI, &gg->ni) == -1) {
            varerr(nc, VAR_NI);
            return -1;
        }
        
        if (var_as_int(nc, VAR_NJ, &gg->nj) == -1) {
            varerr(nc, VAR_NJ);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LA1, &gg->la1) == -1) {
            varerr(nc, VAR_LA1);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LO1, &gg->lo1) == -1) {
            varerr(nc, VAR_LO1);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LA2, &gg->la2) == -1) {
            varerr(nc, VAR_LA2);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LO2, &gg->lo2) == -1) {
            varerr(nc, VAR_LO2);
            return -1;
        }
        
        if (var_as_float(nc, VAR_DI, &gg->di) == -1) {
            varerr(nc, VAR_DI);
            return -1;
        }
        
        if (var_as_float(nc, VAR_DJ, &gg->dj) == -1) {
            varerr(nc, VAR_DJ);
            return -1;
        }
        
        if (nav->grid_type_code == GRID_RLL || nav->grid_type_code == 
GRID_SRLL) {
            gg->rot = (rotated *)emalloc(sizeof(rotated));
        
            if (var_as_float(nc, VAR_ROTLAT, &gg->rot->lat) == -1) {
                varerr(nc, VAR_ROTLAT);
                return -1;
            }
            if (var_as_float(nc, VAR_ROTLON, &gg->rot->lon) == -1) {
                varerr(nc, VAR_ROTLON);
                return -1;
            }
            if (var_as_float(nc, VAR_ROTANGLE, &gg->rot->angle) == -1) {
                varerr(nc, VAR_ROTANGLE);
                return -1;
            }
        }
        
        if (nav->grid_type_code == GRID_SLL || nav->grid_type_code == 
GRID_SRLL) {
            gg->strch = (stretched *)emalloc(sizeof(stretched));
        
            if (var_as_float(nc, VAR_STRETCHLAT, &gg->strch->lat) == -1) {
                varerr(nc, VAR_STRETCHLAT);
                return -1;
            }
            if (var_as_float(nc, VAR_STRETCHLON, &gg->strch->lon) == -1) {
                varerr(nc, VAR_STRETCHLON);
                return -1;
            }
            if (var_as_float(nc, VAR_STRETCHFACTOR, &gg->strch->factor) == -1) {
                varerr(nc, VAR_STRETCHFACTOR);
                return -1;
            }
        }
    }
    break;
    case GRID_GAU:
    case GRID_RGAU:
    case GRID_SGAU:
    case GRID_SRGAU:
    {
        gdes_gau *gg = &nav->grid.gau;

        if (var_as_int(nc, VAR_NI, &gg->ni) == -1) {
            varerr(nc, VAR_NI);
            return -1;
            }
        
        if (var_as_int(nc, VAR_NJ, &gg->nj) == -1) {
            varerr(nc, VAR_NJ);
            return -1;
            }
        
        if (var_as_float(nc, VAR_LA1, &gg->la1) == -1) {
            varerr(nc, VAR_LA1);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LO1, &gg->lo1) == -1) {
            varerr(nc, VAR_LO1);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LA2, &gg->la2) == -1) {
            varerr(nc, VAR_LA2);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LO2, &gg->lo2) == -1) {
            varerr(nc, VAR_LO2);
            return -1;
        }
        
        if (var_as_float(nc, VAR_DI, &gg->di) == -1) {
            varerr(nc, VAR_DI);
            return -1;
        }
        
        if (var_as_int(nc, VAR_N, &gg->n) == -1) {
            varerr(nc, VAR_N);
            return -1;
        }

        if (nav->grid_type_code == GRID_RLL || nav->grid_type_code == 
GRID_SRLL) {
            gg->rot = (rotated *)emalloc(sizeof(rotated));
        
            if (var_as_float(nc, VAR_ROTLAT, &gg->rot->lat) == -1) {
                varerr(nc, VAR_ROTLAT);
                return -1;
            }
            if (var_as_float(nc, VAR_ROTLON, &gg->rot->lon) == -1) {
                varerr(nc, VAR_ROTLON);
                return -1;
            }
            if (var_as_float(nc, VAR_ROTANGLE, &gg->rot->angle) == -1) {
                varerr(nc, VAR_ROTANGLE);
                return -1;
            }
        }
        
        if (nav->grid_type_code == GRID_SLL || nav->grid_type_code == 
GRID_SRLL) {
            gg->strch = (stretched *)emalloc(sizeof(stretched));
        
            if (var_as_float(nc, VAR_STRETCHLAT, &gg->strch->lat) == -1) {
                varerr(nc, VAR_STRETCHLAT);
                return -1;
            }
            if (var_as_float(nc, VAR_STRETCHLON, &gg->strch->lon) == -1) {
                varerr(nc, VAR_STRETCHLON);
                return -1;
            }
            if (var_as_float(nc, VAR_STRETCHFACTOR, &gg->strch->factor) == -1) {
                varerr(nc, VAR_STRETCHFACTOR);
                return -1;
            }
        }
    }
    break;
    case GRID_SPH:
    case GRID_RSPH:
    case GRID_SSPH:
    case GRID_SRSPH:
    {
        gdes_sph *gg = &nav->grid.sph;

        if (var_as_int(nc, VAR_J, &gg->j) == -1) {
            varerr(nc, VAR_J);
            return -1;
        }
        
        if (var_as_int(nc, VAR_K, &gg->k) == -1) {
            varerr(nc, VAR_K);
            return -1;
        }
        
        if (var_as_int(nc, VAR_M, &gg->m) == -1) {
            varerr(nc, VAR_M);
            return -1;
        }
        
        if (var_as_int(nc, VAR_TYPE, &gg->type) == -1) {
            varerr(nc, VAR_TYPE);
            return -1;
        }
        
        if (var_as_int(nc, VAR_MODE, &gg->mode) == -1) {
            varerr(nc, VAR_MODE);
            return -1;
        }
        if (nav->grid_type_code == GRID_RLL || nav->grid_type_code == 
GRID_SRLL) {
            gg->rot = (rotated *)emalloc(sizeof(rotated));
        
            if (var_as_float(nc, VAR_ROTLAT, &gg->rot->lat) == -1) {
                varerr(nc, VAR_ROTLAT);
                return -1;
            }
            if (var_as_float(nc, VAR_ROTLON, &gg->rot->lon) == -1) {
                varerr(nc, VAR_ROTLON);
                return -1;
            }
            if (var_as_float(nc, VAR_ROTANGLE, &gg->rot->angle) == -1) {
                varerr(nc, VAR_ROTANGLE);
                return -1;
            }
        }
        
        if (nav->grid_type_code == GRID_SLL || nav->grid_type_code == 
GRID_SRLL) {
            gg->strch = (stretched *)emalloc(sizeof(stretched));
        
            if (var_as_float(nc, VAR_STRETCHLAT, &gg->strch->lat) == -1) {
                varerr(nc, VAR_STRETCHLAT);
                return -1;
            }
            if (var_as_float(nc, VAR_STRETCHLON, &gg->strch->lon) == -1) {
                varerr(nc, VAR_STRETCHLON);
                return -1;
            }
            if (var_as_float(nc, VAR_STRETCHFACTOR, &gg->strch->factor) == -1) {
                varerr(nc, VAR_STRETCHFACTOR);
                return -1;
            }
        }
    }
    break;
    case GRID_MERCAT:
    {
        gdes_mercator *gg = &nav->grid.mercator;

        if (var_as_int(nc, VAR_NI, &gg->ni) == -1) {
            varerr(nc, VAR_NI);
            return -1;
        }
        
        if (var_as_int(nc, VAR_NJ, &gg->nj) == -1) {
            varerr(nc, VAR_NJ);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LA1, &gg->la1) == -1) {
            varerr(nc, VAR_LA1);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LO1, &gg->lo1) == -1) {
            varerr(nc, VAR_LO1);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LA2, &gg->la2) == -1) {
            varerr(nc, VAR_LA2);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LO2, &gg->lo2) == -1) {
            varerr(nc, VAR_LO2);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LATIN, &gg->latin) == -1) {
            varerr(nc, VAR_LATIN);
            return -1;
        }
        
        if (var_as_float(nc, VAR_DI, &gg->di) == -1) {
            varerr(nc, VAR_DI);
            return -1;
        }
        
        if (var_as_float(nc, VAR_DJ, &gg->dj) == -1) {
            varerr(nc, VAR_DJ);
            return -1;
        }
        
    }
    break;
    case GRID_GNOMON:           /* fall through */
    case GRID_POLARS:
    {
        gdes_polars *gg = &nav->grid.polars;

        if (var_as_int(nc, VAR_NX, &gg->nx) == -1) {
            varerr(nc, VAR_NX);
            return -1;
        }
        
        if (var_as_int(nc, VAR_NY, &gg->ny) == -1) {
            varerr(nc, VAR_NY);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LA1, &gg->la1) == -1) {
            varerr(nc, VAR_LA1);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LO1, &gg->lo1) == -1) {
            varerr(nc, VAR_LO1);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LOV, &gg->lov) == -1) {
            varerr(nc, VAR_LOV);
            return -1;
        }
        
        if (var_as_float(nc, VAR_DX, &gg->dx) == -1) {
            varerr(nc, VAR_DX);
            return -1;
        }
        
        if (var_as_float(nc, VAR_DY, &gg->dy) == -1) {
            varerr(nc, VAR_DY);
            return -1;
        }
        
        if (var_as_int(nc, VAR_PROJFLAG, &gg->pole) == -1) {
            varerr(nc, VAR_PROJFLAG);
            return -1;
        }
    }
    break;
    case GRID_LAMBERT:
    {
        gdes_lambert *gg = &nav->grid.lambert;

        if (var_as_int(nc, VAR_NX, &gg->nx) == -1) {
            varerr(nc, VAR_NX);
            return -1;
        }
        
        if (var_as_int(nc, VAR_NY, &gg->ny) == -1) {
            varerr(nc, VAR_NY);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LA1, &gg->la1) == -1) {
            varerr(nc, VAR_LA1);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LO1, &gg->lo1) == -1) {
            varerr(nc, VAR_LO1);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LOV, &gg->lov) == -1) {
            varerr(nc, VAR_LOV);
            return -1;
        }
        
        if (var_as_float(nc, VAR_DX, &gg->dx) == -1) {
            varerr(nc, VAR_DX);
            return -1;
        }
        
        if (var_as_float(nc, VAR_DY, &gg->dy) == -1) {
            varerr(nc, VAR_DY);
            return -1;
        }
        
        if (var_as_int(nc, VAR_PROJFLAG, &gg->pole) == -1) {
            varerr(nc, VAR_PROJFLAG);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LATIN1, &gg->latin1) == -1) {
            varerr(nc, VAR_LATIN1);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LATIN2, &gg->latin2) == -1) {
            varerr(nc, VAR_LATIN2);
            return -1;
        }
        
        if (var_as_float(nc, VAR_SPLAT, &gg->splat) == -1) {
            varerr(nc, VAR_SPLAT);
            return -1;
        }
        
        if (var_as_float(nc, VAR_SPLON, &gg->splon) == -1) {
            varerr(nc, VAR_SPLON);
            return -1;
        }
        
    }
    break;
    case GRID_SPACEV:
    {
        gdes_spacev *gg = &nav->grid.spacev;

        if (var_as_int(nc, VAR_NX, &gg->nx) == -1) {
            varerr(nc, VAR_NX);
            return -1;
        }
        
        if (var_as_int(nc, VAR_NY, &gg->ny) == -1) {
            varerr(nc, VAR_NY);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LAP, &gg->lap) == -1) {
            varerr(nc, VAR_LAP);
            return -1;
        }
        
        if (var_as_float(nc, VAR_LOP, &gg->lop) == -1) {
            varerr(nc, VAR_LOP);
            return -1;
        }
        
        if (var_as_float(nc, VAR_DX, &gg->dx) == -1) {
            varerr(nc, VAR_DX);
            return -1;
        }
        
        if (var_as_float(nc, VAR_DY, &gg->dy) == -1) {
            varerr(nc, VAR_DY);
            return -1;
        }
        
        if (var_as_float(nc, VAR_XP, &gg->xp) == -1) {
            varerr(nc, VAR_XP);
            return -1;
        }
        
        if (var_as_float(nc, VAR_YP, &gg->yp) == -1) {
            varerr(nc, VAR_YP);
            return -1;
        }
        
        if (var_as_float(nc, VAR_ORIENTATION, &gg->orient) == -1) {
            varerr(nc, VAR_ORIENTATION);
            return -1;
        }
        
        if (var_as_float(nc, VAR_NR, &gg->nr) == -1) {
            varerr(nc, VAR_NR);
            return -1;
        }
        
        if (var_as_float(nc, VAR_XO, &gg->xo) == -1) {
            varerr(nc, VAR_XO);
            return -1;
        }
        
        if (var_as_float(nc, VAR_YO, &gg->yo) == -1) {
            varerr(nc, VAR_YO);
            return -1;
        }
        
    }
    break;
    case GRID_ALBERS:
    case GRID_OLAMBERT:
    case GRID_UTM:
    case GRID_SIMPOL:
    case GRID_MILLER:
    default:
        uerror("%s: can't handle %s grids", nc->ncname,
               gds_typename(nav->grid_type_code));
        return -1;
    }
    return 0;
}


/*
 * Make an in-memory structure for netCDF navigation information and
 * initialize it from specified file.  We cache this information because
 * some of it must be consulted for every decoded product, and it won't
 * change.  Returns 0 on success, -1 on failure.
 */
static int
make_navinfo(nc, nav)
    ncfile *nc;                 /* netCDF file */
    navinfo *nav;               /* where to put the navinfo */
{
    char *cp;
    int *ip;
    float *fp;
    
    nav->navid = nuwg_getdim(ncid, DIM_NAV);

    if (var_as_int(nc, VAR_GRID_TYPE_CODE, &nav->grid_type_code) == -1) {
        varerr(nc, VAR_GRID_TYPE_CODE);
        return -1;
    }

    if (var_as_lset(nc, VAR_GRID_CENTER, &nav->grid_center) == -1) {
        varerr(nc, VAR_GRID_CENTER);
        return -1;
    }

    /* Multiple grid numbers allowed, they get stitched together */
    if (var_as_lset(nc, VAR_GRID_NUMBER, &nav->grid_numbers) == -1) {
        varerr(nc, VAR_GRID_NUMBER);
        return -1;
    }

    /* GRIB resolution and component flags */
    if (var_as_int(nc, VAR_RESCOMP, &nav->rescomp) == -1) {
        varerr(nc, VAR_RESCOMP);
        return -1;
    }

    if (make_navgrid(nc, nav) == -1) {
        return -1;
    }

    return 0;
}


static void
free_navinfo(np)
    navinfo* np;
{
    if (np) {
        if(np->nav_model)
            free(np->nav_model);
        if(np->grid_numbers.vals)
            free(np->grid_numbers.vals);
        free(np);
    }
}


static navinfo *
new_navinfo(nc)
    ncfile *nc;
{
    navinfo *out = (navinfo *)emalloc(sizeof(navinfo));

    if (make_navinfo(nc, out) != 0) {
        free_navinfo(out);
        return 0;
    }

    return out;
}


/*
 * Make an in-memory structure for netCDF information and initialize it from
 * specified file.  We cache this information because some of it must be
 * consulted for every decoded product, and it won't change.
 */
static int
make_ncfile(ncname, out)
    char *ncname;
    ncfile *out;
{
    int ndims;                  /* number of dimensions */
    int nvars;                  /* number of variables */
    int recid;
    long nrecs;                 /* number of records */
    int dimid;
    int varid;
    int *ip;

    out->ncname = estrdup(ncname);
    out->ncid = ncid;

    if (ncinquire(ncid, &ndims, &nvars, (int *)0, &recid) == -1) {
        uerror("%s: ncinquire() failed", ncname);
        return -1;
    }
    out->ndims = ndims;
    out->nvars = nvars;
    out->dims = (ncdim **)emalloc(ndims * sizeof(ncdim *));
    for (dimid = 0; dimid < ndims; dimid++) {
        out->dims[dimid] = new_dim(dimid);
    }
    out->vars = (ncvar **)emalloc(nvars * sizeof(ncvar *));
    for (varid = 0; varid < nvars; varid++) {
        out->vars[varid] = new_var(ncname, varid);
    }

    if (recid == -1) {
        uerror("%s: no record dimension", ncname);
        return -1;
    }
    out->recid = recid;
    out->reftimeid = nuwg_getvar(ncid, VAR_REFTIME);
    out->valtimeid = nuwg_getvar(ncid, VAR_VALTIME);
    if(out->reftimeid == -1) {
        uerror("%s: no reftime variable", ncname);
        return -1;
    }
    if(out->valtimeid == -1) {
        uerror("%s: no valtime variable", ncname);
        return -1;
    }
    out->datetimeid = nuwg_getvar(ncid, VAR_DATETIME);
    out->valoffsetid = nuwg_getvar(ncid, VAR_VALOFFSET);
    if(out->datetimeid == -1) {
        uerror("%s: no datetimeid variable", ncname);
        return -1;
    }
    if(out->valoffsetid == -1) {
        uerror("%s: no valoffsetid variable", ncname);
        return -1;
    }

    /* initialize reftime,valtime,record table */
    if (new_recs(out) == -1) {
        uerror("%s: can't initialize reftime,valtime table", ncname);
        return -1;
    }

    /* Multiple model numbers allowed, e.g. for initialization */
    if (var_as_lset(out, VAR_MODELID, &out->models) == -1) {
        varerr(out, VAR_MODELID);
        return -1;
    }
    
    out->nav = new_navinfo(out);
    if (!out->nav) {            /* get navigation information */
        uerror("%s: can't get navigation information", out->ncname);
        return -1;
    }
    out->levdims = 0;           /* only add level dimensions as needed */
    out->laydims = 0;           /* only add layer dimensions as needed */
    
    return 0;
}


static void
free_ncfile(np)
    ncfile* np;
{
    if (np) {
        if(np->ncname)
            free(np->ncname);
        if(np->models.vals)
            free(np->models.vals);
        if(np->dims) {
            int dimid;
            for (dimid = 0; dimid < np->ndims; dimid++) {
                free_dim(np->dims[dimid]);
            }
            free(np->dims);
        }
        if(np->vars) {
            int varid;
            for (varid = 0; varid < np->nvars; varid++) {
                free_var(np->vars[varid]);
            }
            free(np->vars);
        }
        if(np->rt)
            free_recs(np->rt);
        free(np);
        if(np->nav)
            free_navinfo(np->nav);
        if(np->levdims)
            free(np->levdims);
        if(np->laydims)
            free(np->laydims);
    }
}


/*
 * Creates a new ncfile structure and fills it in with the information from
 * the open netCDF file whose handle is ncid.  A pointer to the structure is
 * returned, or 0 on failure.
 */
ncfile *
new_ncfile(ncname)
    char *ncname;
{
    ncfile *out = (ncfile *)emalloc(sizeof(ncfile));

    if (make_ncfile(ncname, out) != 0) {
        uerror("make_ncfile failed");
        free_ncfile(out);
        return 0;
    }

    return out;
}


/*
 * Print an error message if two specified floating-point values are not
 * sufficiently close.  Returns 1 if not close, zero otherwise.
 */
static int
gd_fne_err(pp, nc, comp, pval, nval)
    product_data *pp;           /* decoded GRIB product */
    ncfile *nc;                 /* netCDF file */
    enum ncpart comp;           /* name-independent id of component */
    float pval;                 /* first value, from GRIB product */
    float nval;                 /* second value, from netCDF file */
{
#define float_near(x,y) ((float)((y) + 0.1*fabs((x)-(y))) == (float)(y)) /* 
true if x is "close to" y */
    if (! float_near(pval, nval)) {
        uerror("%s, %s nav. mismatch %s: %g != %g",
               pp->header, nc->ncname, nuwg_name(comp), pval, nval);
        return 1;
    }
    return 0;    
}

/*
 * Print an error message if two specified floating-point values are not
 * integer close.  Returns 1 if not close, zero otherwise.
 */
static int
gd_fnei_err(pp, nc, comp, pval, nval)
    product_data *pp;           /* decoded GRIB product */
    ncfile *nc;                 /* netCDF file */
    enum ncpart comp;           /* name-independent id of component */
    float pval;                 /* first value, from GRIB product */
    float nval;                 /* second value, from netCDF file */
{
    if ((int) (pval+0.5) != (int) (nval+0.5)) {
        uerror("%s, %s nav. mismatch %s: %d != %d",
               pp->header, nc->ncname, nuwg_name(comp), (int) (pval+0.5), 
               (int) (nval+0.5));
        return 1;
    }
    return 0;    
}


/*
 * Print an error message if two specified int values are not
 * equal.  Return 1 if not equal, zero otherwise.
 */
static int
gd_ine_err(pp, nc, comp, pval, nval)
    product_data *pp;           /* decoded GRIB product */
    ncfile *nc;                 /* netCDF file */
    enum ncpart comp;           /* name-independent id of component */
    int pval;                   /* first value, from GRIB product */
    int nval;                   /* second value, from netCDF file */
{
    if (pval != nval) {
        uerror("%s, %s nav. mismatch %s: %d != %d",
               pp->header, nc->ncname, nuwg_name(comp), pval, nval);
        return 1;
    }
    return 0;    
}


/*
 * Print an error message if first int value is greater than second.
 * Return 1 if not equal, zero otherwise.
 */
static int
gd_igt_err(pp, nc, comp, pval, nval)
    product_data *pp;           /* decoded GRIB product */
    ncfile *nc;                 /* netCDF file */
    enum ncpart comp;           /* name-independent id of component */
    int pval;                   /* first value, from GRIB product */
    int nval;                   /* second value, from netCDF file */
{
    if (pval > nval) {
        uerror("%s, %s nav. mismatch %s: %d > %d",
               pp->header, nc->ncname, nuwg_name(comp), pval, nval);
        return 1;
    }
    return 0;    
}


/*
 * Check consistency of decoded GRIB product grid description section
 * information with the navigation information in the netCDF file.  Return
 * -1 if inconsistency found, 0 otherwise.
 */
static int
check_gd(pp, nc)
    product_data *pp;           /* decoded GRIB product */
    ncfile *nc;                 /* netCDF file */
{
    gengrid *gp = &pp->gd->grid;        /* decoded Grid Description Section */
    navinfo *nav = nc->nav;
    gengrid *gn;                /* Grid Description Section info in netCDF */
    int errs = 0;

    if (gp == 0) {
        uerror("%s: no grid description in product?", pp->header);
        return -1;
    }
    if (nav == 0) {
        uerror("%s: no navigation info in netCDF file?", nc->ncname);
        return -1;
    }
    gn = &nc->nav->grid;        /* GDS info in netCDF file */

    switch(nav->grid_type_code) {
    case GRID_LL:
    case GRID_RLL:
    case GRID_SLL:
    case GRID_SRLL:
    {
        gdes_ll *gng = &gn->ll;
        gdes_ll *gpg = &gp->ll;

        errs += gd_igt_err(pp, nc, VAR_NI, gpg->ni, gng->ni);
        errs += gd_igt_err(pp, nc, VAR_NJ, gpg->nj, gng->nj);
        errs += gd_fne_err(pp, nc, VAR_DI, gpg->di, gng->di);
        errs += gd_fne_err(pp, nc, VAR_DJ, gpg->dj, gng->dj);

        if (nav->grid_type_code == GRID_RLL || nav->grid_type_code == 
GRID_SRLL) {
            if (gpg->rot == 0) {
                uerror("%s: rotated grid, but no rotation info?",
                       pp->header);
                return -1;
            }
            if (gng->rot == 0) {
                uerror("%s: no rotation info for rotated grid in netCDF",
                       nc->ncname);
                return -1;
            }
            errs += gd_fne_err(pp, nc, VAR_ROTLAT, gpg->rot->lat, 
gng->rot->lat);
            errs += gd_fne_err(pp, nc, VAR_ROTLON, gpg->rot->lon, 
gng->rot->lon);
            errs += gd_fne_err(pp, nc, VAR_ROTANGLE, gpg->rot->angle, 
gng->rot->angle);
        }
        
        if (nav->grid_type_code == GRID_SLL || nav->grid_type_code == 
GRID_SRLL) {
            if (gpg->strch == 0) {
                uerror("%s: stretched grid, but no stretch info?",
                       pp->header);
                return -1;
            }
            if (gng->strch == 0) {
                uerror("%s: no stretch info for stretched grid in netCDF",
                       nc->ncname);
                return -1;
            }
            errs += gd_fne_err(pp, nc, VAR_STRETCHLAT, gpg->strch->lat, 
gng->strch->lat);
            errs += gd_fne_err(pp, nc, VAR_STRETCHLON, gpg->strch->lon, 
gng->strch->lon);
            errs += gd_fne_err(pp, nc, VAR_STRETCHFACTOR, gpg->strch->factor, 
gng->strch->factor);
        }
    }
    break;
    case GRID_GAU:
    case GRID_RGAU:
    case GRID_SGAU:
    case GRID_SRGAU:
    {
        gdes_gau *gng = &gn->gau;
        gdes_gau *gpg = &gp->gau;

        errs += gd_ine_err(pp, nc, VAR_NI, gpg->ni, gng->ni);
        errs += gd_ine_err(pp, nc, VAR_NJ, gpg->nj, gng->nj);
        errs += gd_ine_err(pp, nc, VAR_N, gpg->n, gng->n);
        errs += gd_fne_err(pp, nc, VAR_LA1, gpg->la1, gng->la1);
        errs += gd_fne_err(pp, nc, VAR_LO1, gpg->lo1, gng->lo1);
        errs += gd_fne_err(pp, nc, VAR_LA2, gpg->la2, gng->la2);
        errs += gd_fne_err(pp, nc, VAR_LO2, gpg->lo2, gng->lo2);
        errs += gd_fne_err(pp, nc, VAR_DI, gpg->di, gng->di);

        if (nav->grid_type_code == GRID_RLL || nav->grid_type_code == 
GRID_SRLL) {
            if (gpg->rot == 0) {
                uerror("%s: rotated grid, but no rotation info?",
                       pp->header);
                return -1;
            }
            if (gng->rot == 0) {
                uerror("%s: no rotation info for rotated grid in netCDF",
                       nc->ncname);
                return -1;
            }
            errs += gd_fne_err(pp, nc, VAR_ROTLAT, gpg->rot->lat, 
gng->rot->lat);
            errs += gd_fne_err(pp, nc, VAR_ROTLON, gpg->rot->lon, 
gng->rot->lon);
            errs += gd_fne_err(pp, nc, VAR_ROTANGLE, gpg->rot->angle, 
gng->rot->angle);
        }
        
        if (nav->grid_type_code == GRID_SLL || nav->grid_type_code == 
GRID_SRLL) {
            if (gpg->strch == 0) {
                uerror("%s: stretched grid, but no stretch info?",
                       pp->header);
                return -1;
            }
            if (gng->strch == 0) {
                uerror("%s: no stretch info for stretched grid in netCDF",
                       nc->ncname);
                return -1;
            }
            errs += gd_fne_err(pp, nc, VAR_STRETCHLAT, gpg->strch->lat, 
gng->strch->lat);
            errs += gd_fne_err(pp, nc, VAR_STRETCHLON, gpg->strch->lon, 
gng->strch->lon);
            errs += gd_fne_err(pp, nc, VAR_STRETCHFACTOR, gpg->strch->factor, 
gng->strch->factor);
        }
    }
    break;
    case GRID_SPH:
    case GRID_RSPH:
    case GRID_SSPH:
    case GRID_SRSPH:
    {
        gdes_sph *gng = &gn->sph;
        gdes_sph *gpg = &gp->sph;

        errs += gd_ine_err(pp, nc, VAR_J, gpg->j, gng->j);
        errs += gd_ine_err(pp, nc, VAR_K, gpg->k, gng->k);
        errs += gd_ine_err(pp, nc, VAR_M, gpg->m, gng->m);
        errs += gd_ine_err(pp, nc, VAR_TYPE, gpg->type, gng->type);
        errs += gd_ine_err(pp, nc, VAR_MODE, gpg->mode, gng->mode);

        if (nav->grid_type_code == GRID_RLL || nav->grid_type_code == 
GRID_SRLL) {
            if (gpg->rot == 0) {
                uerror("%s: rotated grid, but no rotation info?",
                       pp->header);
                return -1;
            }
            if (gng->rot == 0) {
                uerror("%s: no rotation info for rotated grid in netCDF",
                       nc->ncname);
                return -1;
            }
            errs += gd_fne_err(pp, nc, VAR_ROTLAT, gpg->rot->lat, 
gng->rot->lat);
            errs += gd_fne_err(pp, nc, VAR_ROTLON, gpg->rot->lon, 
gng->rot->lon);
            errs += gd_fne_err(pp, nc, VAR_ROTANGLE, gpg->rot->angle, 
gng->rot->angle);
        }
        
        if (nav->grid_type_code == GRID_SLL || nav->grid_type_code == 
GRID_SRLL) {
            if (gpg->strch == 0) {
                uerror("%s: stretched grid, but no stretch info?",
                       pp->header);
                return -1;
            }
            if (gng->strch == 0) {
                uerror("%s: no stretch info for stretched grid in netCDF",
                       nc->ncname);
                return -1;
            }
            errs += gd_fne_err(pp, nc, VAR_STRETCHLAT, gpg->strch->lat, 
gng->strch->lat);
            errs += gd_fne_err(pp, nc, VAR_STRETCHLON, gpg->strch->lon, 
gng->strch->lon);
            errs += gd_fne_err(pp, nc, VAR_STRETCHFACTOR, gpg->strch->factor, 
gng->strch->factor);
        }
    }
    break;
    case GRID_MERCAT:
    {
        gdes_mercator *gng = &gn->mercator;
        gdes_mercator *gpg = &gp->mercator;

        errs += gd_ine_err(pp, nc, VAR_NI, gpg->ni, gng->ni);
        errs += gd_ine_err(pp, nc, VAR_NJ, gpg->nj, gng->nj);
        errs += gd_fne_err(pp, nc, VAR_LA1, gpg->la1, gng->la1);
        errs += gd_fne_err(pp, nc, VAR_LO1, gpg->lo1, gng->lo1);
        errs += gd_fne_err(pp, nc, VAR_LA2, gpg->la2, gng->la2);
        errs += gd_fne_err(pp, nc, VAR_LO2, gpg->lo2, gng->lo2);
        errs += gd_fne_err(pp, nc, VAR_LATIN, gpg->latin, gng->latin);
        errs += gd_fne_err(pp, nc, VAR_DI, gpg->di, gng->di);
        errs += gd_fne_err(pp, nc, VAR_DJ, gpg->dj, gng->dj);
    }
    break;
    case GRID_GNOMON:           /* fall through */
    case GRID_POLARS:
    {
        gdes_polars *gng = &gn->polars;
        gdes_polars *gpg = &gp->polars;

        errs += gd_ine_err(pp, nc, VAR_NX, gpg->nx, gng->nx);
        errs += gd_ine_err(pp, nc, VAR_NY, gpg->ny, gng->ny);
        errs += gd_fne_err(pp, nc, VAR_LA1, gpg->la1, gng->la1);
        errs += gd_fne_err(pp, nc, VAR_LO1, gpg->lo1, gng->lo1);
        errs += gd_fne_err(pp, nc, VAR_LOV, gpg->lov, gng->lov);
        errs += gd_fnei_err(pp, nc, VAR_DX, gpg->dx, gng->dx);
        errs += gd_fnei_err(pp, nc, VAR_DY, gpg->dy, gng->dy);
        errs += gd_ine_err(pp, nc, VAR_PROJFLAG, gpg->pole, gng->pole);
    }
    break;
    case GRID_LAMBERT:
    {
        gdes_lambert *gng = &gn->lambert;
        gdes_lambert *gpg = &gp->lambert;

        errs += gd_ine_err(pp, nc, VAR_NX, gpg->nx, gng->nx);
        errs += gd_ine_err(pp, nc, VAR_NY, gpg->ny, gng->ny);
        errs += gd_fne_err(pp, nc, VAR_LA1, gpg->la1, gng->la1);
        errs += gd_fne_err(pp, nc, VAR_LO1, gpg->lo1, gng->lo1);
        errs += gd_fne_err(pp, nc, VAR_LOV, gpg->lov, gng->lov);
        errs += gd_fnei_err(pp, nc, VAR_DX, gpg->dx, gng->dx);
        errs += gd_fnei_err(pp, nc, VAR_DY, gpg->dy, gng->dy);
        errs += gd_ine_err(pp, nc, VAR_PROJFLAG, gpg->pole, gng->pole);
        errs += gd_fne_err(pp, nc, VAR_LATIN1, gpg->latin1, gng->latin1);
        errs += gd_fne_err(pp, nc, VAR_LATIN2, gpg->latin2, gng->latin2);
        errs += gd_fne_err(pp, nc, VAR_SPLAT, gpg->splat, gng->splat);
        errs += gd_fne_err(pp, nc, VAR_SPLON, gpg->splon, gng->splon);
    }
    break;
    case GRID_SPACEV:
    {
        gdes_spacev *gng = &gn->spacev;
        gdes_spacev *gpg = &gp->spacev;

        errs += gd_ine_err(pp, nc, VAR_NX, gpg->nx, gng->nx);
        errs += gd_ine_err(pp, nc, VAR_NY, gpg->ny, gng->ny);
        errs += gd_fne_err(pp, nc, VAR_LAP, gpg->lap, gng->lap);
        errs += gd_fne_err(pp, nc, VAR_LOP, gpg->lop, gng->lop);
        errs += gd_fnei_err(pp, nc, VAR_DX, gpg->dx, gng->dx);
        errs += gd_fnei_err(pp, nc, VAR_DY, gpg->dy, gng->dy);
        errs += gd_fne_err(pp, nc, VAR_XP, gpg->xp, gng->xp);
        errs += gd_fne_err(pp, nc, VAR_YP, gpg->yp, gng->yp);
        errs += gd_fne_err(pp, nc, VAR_ORIENTATION, gpg->orient, gng->orient);
        errs += gd_ine_err(pp, nc, VAR_NR, gpg->nr, gng->nr);
        errs += gd_fne_err(pp, nc, VAR_XO, gpg->xo, gng->xo);
        errs += gd_fne_err(pp, nc, VAR_YO, gpg->yo, gng->yo);
    }
    break;
    case GRID_ALBERS:
    case GRID_OLAMBERT:
    case GRID_UTM:
    case GRID_SIMPOL:
    case GRID_MILLER:
    default:
        uerror("%s: can't handle %s grids", nc->ncname,
               gds_typename(nav->grid_type_code));
        return -1;
    }
    if (errs > 0)
        return -1;
    
    return 0;
}


/*
 * Consistency check between decoded product data and netCDF file to which
 * it will be written.  Returns -1 if a serious inconsistency was found.
 * 
 * If a value is missing in the netCDF file, it should get filled in here.
 * ( *** not implemented yet *** ).
 *
 * The netCDF information is read from cached info rather than from the file
 * for speed, since this check takes place on every product.
 */
static int
check_pp(pp, nc)
    product_data *pp;   /* decoded GRIB product */
    ncfile *nc;         /* netCDF file */
{
    navinfo* nav = nc->nav;
    int found;
    int i;

    found = 0;
    for (i = 0; i < nc->models.n; i++) {
        if (pp->model == nc->models.vals[i]) {
            found = 1;
            break;
        }
    }
    if (!found) {
        uerror("%s model %d not in modelid list in %s", pp->header,
               pp->model, nc->ncname);
        return -1;
    }

    found = 0;
    for (i = 0; i < nav->grid_center.n; i++) {
        if (pp->center == nav->grid_center.vals[i]) {
            found = 1;
            break;
        }
    }
    if (!found) {
        uerror("%s center %d not in grid_center list in %s", pp->header,
               pp->center, nc->ncname);
        return -1;
    }
    
    found = 0;
    for (i = 0; i < nav->grid_numbers.n; i++) {
        if (pp->grid == nav->grid_numbers.vals[i]) {
            found = 1;
            break;
        }
    }
    if (!found) {
        uerror("%s grid %d not in grid_number list in %s", pp->header,
               pp->grid, nc->ncname);
        return -1;
    }
    
    /* gd->res_flags should match what's in nc, but if not, rewrite it. */
    if (pp->gd->res_flags != nav->rescomp) {
        udebug("%s: rescompflag in %s doesn't match, %d, %d",
               pp->header, nc->ncname, (int) pp->gd->res_flags, nav->rescomp);
    }
    
    if(check_gd(pp, nc) == -1) {
        return -1;
    }
    return 0;
}


/*
 * Figure out subgrid location from netCDF navigation information and
 * product Grid Description Section information.  Returns 0 on success, -1
 * on failure.
 */
static int
subgrid(nc, pp, ix0, ix1)
    ncfile *nc;
    product_data *pp;
    long *ix0;
    long *ix1;
{
    navinfo* nav = nc->nav;
    gdes_ll *gll;
    float plon;

    *ix0 = 0;
    *ix1 = 0;

    switch (pp->gd->type) {
    case GRID_LL:
    case GRID_RLL:
    case GRID_SLL:
    case GRID_SRLL:

        if(pp->gd->scan_mode & 0x20 == 1 || /*adjacent points in j direction */
           pp->gd->scan_mode & 0x80 == 1 ) { /* points scan in -i direction */
            uerror("%s: can't handle scan mode of %x ",
                   pp->header,pp->gd->scan_mode);
            return -1;
        }

        /* If scanning mode flag indicates North to South scan, we
           reverse the rows so we can always assume South to North scan */
        if( (pp->gd->scan_mode != 0) &&
                (pp->gd->scan_mode & 0x40) == 0) { /* north to south */
            int row;
            int nrows = pp->gd->nrows;
            int ncols = pp->gd->ncols;
            float tmp;
            
            for (row = 0; row < nrows/2; row++) {
                int col;
                float *upper = pp->data + row * ncols;
                float *lower = pp->data + (nrows - 1 - row) * ncols;
                /* swap row (upper) and nrows-1-row (lower) */
                for (col = 0; col < ncols; col++) {
                    tmp = *upper;
                    *upper = *lower;
                    *lower = tmp;
                    upper++;
                    lower++;
                }
            }
            gll = &pp->gd->grid.ll;
/*
        uerror( "gll->la1=%f, gll->la2=%f,gll->lo1=%f, gll->lo2=%f",
                gll->la1, gll->la2, gll->lo1, gll->lo2 );
*/
            tmp = gll->la1;
            gll->la1 = gll->la2;
            gll->la2 = tmp;
            pp->gd->scan_mode |= 0x40;
        }       

        /* Compare pp->gdes->grid.ll->la1 to value of La1, La2 netCDF
           variables and similarly for lo1, lo2. */
        gll = &pp->gd->grid.ll;
/*
        uerror( "gll->la1=%f, gll->la2=%f,gll->lo1=%f, gll->lo2=%f",
                gll->la1, gll->la2, gll->lo1, gll->lo2 );
*/
        plon = gll->lo1;
        while (plon < nc->nav->grid.ll.lo1)
            plon += 360.0 ;
        /* handle case where right edge is same as left edge */
        if (plon == nc->nav->grid.ll.lo2 &&
            nc->nav->grid.ll.lo1 + 360 ==  nc->nav->grid.ll.lo2) {
            plon = nc->nav->grid.ll.lo1;
        }
/*
        uerror("gll->la1=%f, nc->nav->grid.ll.la1=%f, nc->nav->grid.ll.dj=%f",
               gll->la1, nc->nav->grid.ll.la1, nc->nav->grid.ll.dj);
*/
        *ix0 = (gll->la1 - nc->nav->grid.ll.la1) /nc->nav->grid.ll.dj + 0.5;
        *ix1 = (plon - nc->nav->grid.ll.lo1) /nc->nav->grid.ll.di + 0.5;
/*
        uerror("*ix0=%f, *ix1=%f", *ix0, *ix1);
*/
    }
    /* default: */
    return 0;
}


/*
 * Handle writing extra time-range indicator information, if any, in
 * auxilliary variables.  For example, accumulation interval for
 * precipitation variables would be handled here.

 * For now, we do this in an ad hoc way, until we have a mechanism for
 * writing general auxilliary GRIB info associated with a variable.
 */
static int
triaux(pp, nc, var, start)
    product_data *pp;   /* decoded GRIB data to be written */
    ncfile *nc;         /* netCDF file to be written */
    ncvar *var;         /* netCDF variable to be written */
    long *start;        /* index where variable to be written */
{
    char tri_name[MAX_NC_NAME];
    char *suf;
    ncvar *trivar;
    int trivarid;
    long ix[2];
    long count[2];
    float trivals[2];

    if(pp->tr_flg == TRI_P1 || pp->tr_flg == TRI_LP1)
        return 0;      /* usually time range info is in valid_time variable */

    /* check if auxilliary time-range variable exists */
    suf = trisuffix(pp->tr_flg);
    if (strlen(var->name) + 1 + strlen(suf) > (size_t) MAX_NC_NAME) {
        uerror("%s: name of %s TRI variable too long (%s)",
               nc->ncname, suf, var->name);
        return -1;
    }

    strcpy(tri_name, var->name);
    strcat(tri_name, "_");
    strcat(tri_name, suf);
    trivarid = ncvarid(ncid, tri_name);
    if(trivarid == -1) {
        return 0;               /* not an error, since optional whether file
                                 * has auxilliary time-range variable */
    }

    /* *** should check units of _accum_len variable and convert to
       those, use float_nc() *** */
    trivar = nc->vars[trivarid];

    switch (trinum(pp->tr_flg)) {
    case 2:
        ix[0]=start[0];         /* record number */
        ix[1]=0;
        count[0] = 1;
        count[1] = 2;           /* two values for time range */
        trivals[0] = pp->tr[0];
        trivals[1] = pp->tr[1];
        if (ncvarput(ncid, trivarid, ix, count, &trivals) == -1) {
            uerror("%s: can't write accum_len variable for (%s)",
                   nc->ncname, var->name);
            return -1;
        }
        break;
    case 1:
        ix[0]=start[0];
        trivals[0] = frcst_time(pp);
        if (ncvarput1(ncid, trivarid, ix, &trivals) == -1) {
            uerror("%s: can't write accum_len variable for (%s)",
                   nc->ncname, var->name);
            return -1;
        }
        break;
    default:
            uerror("%s: can't handle time flag %d for variable (%s)",
                   nc->ncname, pp->tr_flg, var->name);
            return -1;
    }
    return 0;
}

/*
 * Writes decoded GRIB product to netCDF file open for writing.
 * Returns 0 on success, -1 on failure.
 */
int
nc_write(pp, nc)
    product_data *pp;   /* decoded GRIB product to be written */
    ncfile *nc;         /* netCDF file to write */
{
    double reftime, valtime;
    long rec;
    char *varname;
    ncvar *var;
    int varid;
    long lev;                   /* level to write, if level dimension */
    char *cp = parmname(nc, pp->param, pp->level_flg); /* var to write */
    int dim=0;                  /* which dimension we are dealing with */
#define MAX_PARM_DIMS   4       /* Maximum dimensions for a parameter.
                                   X(rec,lev,lat,lon) This would be 4 for a
                                   variable with a level dimension, 3 if no
                                   level dimension. */
    long start[MAX_PARM_DIMS];
    long count[MAX_PARM_DIMS];

                                /* Check consistency of product with
                                 * netCDF file, e.g. model_id, gdes vs.
                                 * netCDF navigation */
    if(check_pp(pp, nc) == -1) {
        /* any messages about inconsistency should have already been logged */
        return -1;
    }

    if (!cp) {
        uerror("%s: unrecognized (param,level_flg) combination (%d,%d)",
               pp->header, pp->param, pp->level_flg);
        return -1;
    }
    varname = estrdup(cp);
                                /* locate variable in output netCDF file */
    varid = ncvarid(ncid,varname);
    if (varid == -1) {
        uerror("%s: no variable %s in %s", pp->header, varname,
               nc->ncname);
        return -1;
    }
    var = nc->vars[varid];

    if (var->dims[0] != nc->recid) { /* no record dimension */
        dim--;
    } else {                    /* handle record dimension */
        humtime ht;
        
        if (rvhours(pp, nc, &reftime, &valtime, &ht) != 0) {
            uerror("%s: can't get reftime,valtime", pp->header);
            return -1;
        }

        rec = getrec(nc, reftime, valtime, &ht); /* which record to write */
        if (rec == -1) {
            uerror("%s: can't get record number for variable %s in %s",
                   pp->header, varname, nc->ncname);
            return -1;
        }
        start[dim] = rec;
        count[dim] = 1;
    }

    /* Handle auxilliary time-range indicator information, if any */
    if (triaux(pp, nc, var, start) == -1)
        return -1;

    /* handle level dimension, if any */
    lev = getlev(pp, nc, var);    
    if (lev == -1)
        return -1;
    if(lev >= 0) {
        dim++;
        start[dim] = lev;
        count[dim] = 1;
    }

    /* Write varname(rec,lev) or varname(rec) hyperslab, but start in the
       right place if this is only a subgrid of the netCDF file domain */
    {
        double slope, intercept;
        long ix0;               /* if subgridded, lat index, otherwise 0 */
        long ix1;               /* if subgridded, lon index, otherwise 0 */
        
        if (subgrid(nc, pp, &ix0, &ix1) == -1) {
            uerror("%s: can't fit %s variable subgrid into %s",
                       pp->header, varname, nc->ncname);
            return -1;
        }
        dim++;
        start[dim] = ix0;
        count[dim] = pp->gd->nrows;

        dim++;
        start[dim] = ix1;
        count[dim] = pp->gd->ncols;
        
        if (var->uc) {          /* units conversion */
            slope = var->uc->slope;
            intercept = var->uc->intercept;
        } else {
            slope = 1.0;
            intercept = 0.0;
        }

        if (float_nc(ncid, varid, start, count,
                      pp->data, slope, intercept, FILL_VAL) == -1) {
            uerror("%s: error writing %s variable in %s",
                       pp->header, varname, nc->ncname);
            return -1;
        }
    }

    if (ncsync(ncid) == -1) {
        uerror("%s: ncsync failed after writing %s variable in %s",
                       pp->header, varname, nc->ncname);
        return -1;
    }
    if (var->dims[0] != nc->recid) { /* no record dimension */
        if (lev < 0) {
            uinfo("%s: wrote %s(*,*) to %s", pp->header, varname,
                  nc->ncname);
        } else if (lev >= 0) {
            uinfo("%s: wrote %s(%ld,*,*) to %s", pp->header, varname,
                  lev, nc->ncname);
        }
    } else {
        if (lev < 0) {
            uinfo("%s: wrote %s(%ld,*,*) to %s", pp->header, varname,
                  rec, nc->ncname);
        } else if (lev >= 0) {
            uinfo("%s: wrote %s(%ld,%ld,*,*) to %s", pp->header, varname,
                  rec, lev, nc->ncname);
        }
    }

    free(varname);
    return 0;
}