# Metview Macro

# **************************** LICENSE START ***********************************
#
# Copyright 2012 ECMWF. This software is distributed under the terms
# of the Apache License version 2.0. In applying this license, ECMWF does not
# waive the privileges and immunities granted to it by virtue of its status as
# an Intergovernmental Organization or submit itself to any jurisdiction.
#
# ***************************** LICENSE END ************************************


# **************************************************************************
# Function      : mvl_ml2hPa
#
# Syntax        : fieldset mvl_ml2hPa (lnsp:fieldset,
#                                      mfld:fieldset,
#                                      plist:list )
#
# Author (date) : Hans Hersbach, ECMWF (28/02/2011)
#
# Category      : INTERPOLATION
#
# OneLineDesc   : Interpolates a fieldset on model levels to pressure levels (in hPa)
#
# Description   : Interpolates a fieldset on model levels to pressure levels (in hPa).
#                 Locations where interpolation is not possible are returned as missing.
#                 Neither mfld nor plist need to be sorted.
#
# Parameters    : lnsp  - logarithm of surface pressure
#                 mfld  - list of fieldsets on model levels
#                 plist - list of pressure levels in hPa
#                 
#
# Return Value  : list of fieldsets interpolated onto pressure levels
#
# Dependencies  : none
#
# Example Usage : 
#                 plevels = [1000, 900, 850, 500, 300, 100, 10, 1, 0.1]
#                 tpres = mvl_ml2hPa (lnsp, tmod, plevels)
#
# **************************************************************************

function mvl_ml2hPa(lnsp:fieldset, mfld:fieldset, plist:list)

# Returns interpolated fieldset from model to pressure levels (in hPa)
# Locations where interpolation is not possible are returned as missing
# Neither mfld nor plist needs to be sorted. All units in hPa

#  lnsp  : logarithm of surface pressure                       ( input)
#  mfld  : list of fieldsets on model levels                   ( input)
#  plist : list of pressure levels in hPa                      ( input)
#  ml2hPa: list of fieldsets interpolated onto pressure levels (output)

# Hans Hersbach, 26 February 2011, ECMWF

#-Find and sort model levels
  mlist=grib_get_double(mfld,"level")
  mlevs=sort        (mlist)
  index=sort_indices(mlist)

#-Get a's and b's on full model levels
  abh=grib_get_double_array(mfld[1],"pv") 
  nlevh=count(abh)/2
  a   =nil
  b   =nil
  plev=nil
  for i=1 to count(mlevs) do  # only pick the ones you'll need
      ilev=mlevs[i]
      jlev=mlevs[i] + nlevh
      a   = a   & [ 0.01*(abh[ilev]+abh[ilev+1])/2 ]  # in hPa
      b   = b   & [      (abh[jlev]+abh[jlev+1])/2 ]
      plev=plev & [0]     
      if ( b[i] = 0 ) then
         a[i]=a[i]*(1-2.0E-5) # to avoid some strange rounding issues
      end if
  end for

#-Find required range of model levels for the interpolation
  ps = 0.01*exp(lnsp)
  psmin=minvalue(ps)
  psmax=maxvalue(ps)
  imin=nil
  imax=nil
  for ipress = 1 to count(plist) do
    press=plist[ipress]
    i1=1
    i2=1
    for i=2 to count(mlevs)-1 do
      if (a[i]+b[i]*psmax < press) then
         i1 = i
      end if
      if (a[i]+b[i]*psmin < press) then
         i2 = i
      end if
    end for
    imin = imin & [i1]
    imax = imax & [i2]

#  -Pre-calculate the pressure fields you'll need
    for i=i1 to i2+1 do
      if (type(plev[i]) = type(0)) then
         plev[i] = a[i] + b[i]*ps
      end if
    end for
  end for

#-Start interpolation; loop over pressure list
  pfld=nil
  for ipress = 1 to count(plist) do
    press=plist[ipress]

#  -Find bracketing levels p1 and p2, with field values f1 and f2
    p1=0.*plev[imin[ipress]]
    p2=0.*plev[imin[ipress]]
    f1=0.*mfld[1]
    f2=0.*mfld[1]
    for i = imin[ipress]  to imax[ipress] do
        ip = (plev[i]<=press) and (press<plev[i+1])
        p1 = p1 + ip*plev[      i   ]
        p2 = p2 + ip*plev[      i+1 ]
        f1 = f1 + ip*mfld[index[i  ]]
        f2 = f2 + ip*mfld[index[i+1]]
    end for
#  -Where interpolation not possible (e.g. below surface), set to missing
    p1=bitmap(p1,0.)
    p2=bitmap(p2,0.)

#  -Interpolate, transform to pressure field, and add to fieldset list
    fint= ( f1*(press-p2)+ f2*(p1-press) )/(p1-p2)

#   This code is GRIB edition dependent because it seems that gribAPI
#   has a problem to encode the key typeOfLevel = isobaricInhPa if data
#   is edition 2. GribAPI team is investigating this issue.
#   Also, it is not possible to use the key typeOfLevel = isobaricInPa
#   for all cases because GRIB edition 1 can not encode values greater
#   than 65535 (16 bits).
#   Update this code when gribAPI team finds a solution.
    edition = grib_get_long(fint,"editionNumber")
    if edition = 1 then
      if (press >= 1) then
         fpr = grib_set(fint, ["typeOfLevel", "isobaricInhPa", "level", press])
      else
         fpr = grib_set(fint, ["typeOfLevel", "isobaricInPa", "level", press*100])
      end if
    else
      fpr = grib_set(fint, ["typeOfLevel", "isobaricInPa", "level", press*100])
    end if

    pfld= pfld & fpr
  end for

#-Done!
  return pfld

end ml2hPa
