#Metview Macro

#  **************************** LICENSE START ***********************************
# 
#  Copyright 2019 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      : thermo_parcel_path
#
# Syntax        : 
#                                          
# Category      : THERMODYNAMICS
#
# OneLineDesc   : computes the path of an ascending thermodynamic parcel
#
# Description   : Computes the path of an ascending thermodynamic parcel
#
# Parameters    : 
#           
# Return Value  : 
#
# Dependencies  : none
#
#==============================================================================

function thermo_parcel_path

    fn_name = "thermo_parcel_path"
    args=arguments()
    
    if count(args) = 0 then
        _invalid_arguments(args)
    end if
       
    options = nil 
    keys =  ["mode", "start_t", "start_t", "start_p", "start_td", "top_p", "bottom_p", "layer_depth",
             "stop_at_el", "comp_top", "debug", "virtual", "ept_method"]
   
    if type(args[1]) = "netcdf" then
        nc = args[1]
        if count(args) = 1 then 
            options = (mode: "mucape", layer_depth: 300)
        else if count(args) = 2 then 
            options = args[2]
        else              
            options = __get_kwargs(fn_name, args, 2, keys)
        end if
        if options = nil then
            _invalid_arguments(args)
        end if
        return _thermo_parcel_path_nc(nc, options)
        
    else if type(args[1]) = "vector" then 
        if count(args) < 3 then
            _invalid_arguments(args)
        else 
            t = args[1]
            td = args[2]
            p = args[3]
            if count(args) = 3 then 
                options = ("mode": "mucape", "layer_depth": 300)
            else if count(args) = 4 then 
                options = args[4]
            else              
                options = __get_kwargs(fn_name, args, 4, keys)
            end if
            if options = nil then
                _invalid_arguments(args)
            end if
            return __compute_parcel_path_core(t, td, p, options)
        end if
    end if
    
    _invalid_arguments(args)
          
end thermo_parcel_path

function _invalid_arguments(args)
    
    s = "thermo_parcel_path: cannot call with arguments=("
    for i=1 to count(args) do
        s = s & type(args[i])
        if i <> count(args) then
            s = s & ","
        end if
    end for
    s = s & ")"  
    fail(s)

end _invalid_arguments

function _thermo_parcel_path_nc(nc: netcdf, options: definition)
	
	min_level_num = 3
	
	first_dim_val = 1
	if base_language = 'python' then 
	   first_dim_val = 0
	end if
	   
    # get temperature in C
    setcurrent(nc, "t")
    t = values(nc,[first_dim_val,'all'])
    
    # get dewpoint in C
    setcurrent(nc, "td")
    td = values(nc,[first_dim_val,'all'])
     
    # get pressure in hPa
    setcurrent(nc, "pres")
    p = values(nc,[first_dim_val,'all'])
    
    missing_val = 1E+30
    attr=global_attributes(nc)
    if attr._FILL_VALUE <> "" then
        missing_val = attr._FILL_VALUE
    end if    
    
    # remove missing values and levels above the min pressure (50 hPa)
    if count(p) > 0 then
    
    	num = count(p)
    	eps = 1E-3
    	# check for missing values
    	cnt = 1
    	for i=1 to num do   	
    	   if p[i] >= 50 and abs(t[i]-missing_val) > eps and abs(td[i]-missing_val) > eps then
    	       t[cnt] = t[i]
    	       td[cnt] = td[i]
    	       p[cnt] = p[i]
    	       cnt = cnt +1
    	   end if    
    	end for
    	
    	if cnt - 1 < min_level_num then
    	   print("Number of levels must be >= ",min_level_num,"! Only ",cnt - 1," found")
    	   return nil
    	end if   
    	
    	if cnt <> num + 1 then
    	   t = t[1,cnt-1]
    	   td = td[1,cnt-1]
    	   p = p[1,cnt-1]
    	   num = cnt - 1
    	end if
    	
    	# we need to be sure the profile goes upwards
    	if p[1] < p[num] then
    	
    		p_r = p
    		t_r = t
    		td_r = td
    		
    		nn=num
    		
    		p=vector(nn)
    		t=vector(nn)
    		td=vector(nn)
    		
    		for i=1 to nn do 
    			p[i] = p_r[num-i+1]
    			t[i] = t_r[num-i+1]
    			td[i] = td_r[num-i+1]
    		end for	
    	end if
  
        #print(t)
        #print(td)
        #print(p)
    
    	return __compute_parcel_path_core(t, td, p, options)
   	end if
   	
   	return nil 

end thermo_parcel_path
     
#=================================================================     
#     
# PRIVATE FUNCTIONS - SHOULD NOT BE CALLED BY USERS
#
#=================================================================  

function __adjust_parcel_mode(mode)

    if mode = "most_unstable" then
        mode = "mucape"
    else if mode = "mean_layer" then
        mode = "ml"
    end if
    return mode
end __adjust_parcel_mode

function __check_parcel_mode(options)
	__THERMO_PARCEL_MODES = ["surface","custom","mucape","ml","ml50","ml100"]
	if options.mode not in __THERMO_PARCEL_MODES then
	    print("Invalid thermo parcel mode=",options.mode,". Allowed modes=",__THERMO_PARCEL_MODES)
	    return -1
	end if
	return 1
end __check_parcel_mode		
  
function __check_ept_method(options)
	__EPT_METHODS = ["old", "ifs", "bolton39"]
	if options.ept_method not in __EPT_METHODS then
	    print("Invalid ept_method=",options.ept_methos,". Allowed methods=",__EPT_METHODS)
	    return -1
	end if
	return 1
end __check_ept_method
  
function __init_bool_option(opt, default_val)
	if opt <> nil then
	   if type(opt) = "string" then
	       if default_val = 1 and opt = "off" then
	           opt = 0
	       else if default_val <> 1 and opt = "on" then
	           opt = 1
	       end if
	   else if type(opt) = "number" then
	       if default_val = 1 and opt <> 1  then
	           opt = 0
	       else if default_val <> 1 and opt <> 0  then
	           opt = 1   
	       end if
	   end if
	else
	   opt = default_val  
	end if
	
	return opt
end __init_bool_option
   
function __compute_parcel_path_core(t: vector, td: vector, p: vector, options: definition)
	
	# checks the start mode
	if options.mode = nil then
	   options.mode = "mucape"
	end if
	
	options.mode = __adjust_parcel_mode(options.mode)
	if not __check_parcel_mode(options) then
    	return nil
    end if
	
	# init options
	options.virtual = __init_bool_option(options.virtual, 1)
	options.stop_at_el = __init_bool_option(options.stop_at_el, 0)
	#options.el_area = __init_bool_option(options.el_area, 0)
	options.comp_top = __init_bool_option(options.comp_top, 0)
	options.mean_start_from_theta = __init_bool_option(options.mean_start_from_theta, 1)
	options.debug = __init_bool_option(options.debug, 0)
	
	if options.ept_method = nil then
	   options.ept_method = "bolton39"
	end if
	__check_ept_method(options)
	
	if options.stop_at_el = 1 then
	   #options.el_area = 0
	   options.comp_top = 0
    end if
    # if options.comp_top = 1 then
    #    options.el_area = 1
    # end if
    
    if options.debug = 1 then	
        print("options=", options)	
    end if
    
	# check number of values
	if count(p) = 0 then 
		print("p array is empty")
		return nil
	end if
	
	if count(t) = 0 then 
		print("t array is empty")
		return nil
	end if			
	
	if count(td) = 0 then 
		print("td array is empty")
		return nil
	end if
	
	num = count(p)
	
	if count(t) <> num then
		print("Different number of values in t (",count(t),") and p (",count(p),")")
		return nil	
	end if
	
	if count(td) <> num then
		print("Different number of values in td (",count(td),") and p (",count(p),")")
		return nil	
	end if	
	
	# profile: check input units, must be Celsius and hPa
	
	if t[1] > 60 then
        print("Invalid profile temperature t[1]=",t[1]," C")
        return nil
	end if
	
	if td[1] > 60 then
		print("Invalid profile dewpoint td[1]=",td[1]," C")
        return nil 
    end if	
	
	if p[1] > 1200 then
		print("Invalid profile pressure p[1]=",p[1], " hPa")
        return nil 
	end if			
	
	# convert to SI
    for i=1 to num do   	    	  
        t[i] = t[i] + 273.16
    	td[i] = td[i] + 273.16
    	p[i] = p[i] * 100
    end for
	
	# define the start conditions
	start = ("t": 0, "td": 0, "p": 0)
	
	# default is mucape
	if options.mode = "surface" then
		start.p=p[1]
		start.t=t[1]
		start.td=td[1]
	
	else if options.mode = "custom" then
		start.p=options.start_p * 100
		if options.start_t = nil or options.start_td = nil then
		    start.t = __compute_profile_at_level(start.p, t, p)["t"]
		    start.td = __compute_profile_at_level(start.p, td, p)["t"]
		else 
            start.t=options.start_t + 273.16
		    start.td=options.start_td + 273.16
		end if
    else if options.mode = "ml" or options.mode = "ml50" or options.mode = "ml100" then		
								
        p_bottom = p[1]
        
        if options.mode = "ml" then
    		if options.bottom_p <> nil then		
    		  p_bottom=options.bottom_p * 100
    		end if
    				
    		if options.top_p <> nil then		
    			p_top=options.top_p * 100 
    		end if	
    	
            if options.layer_depth <> nil then
            	if options.top_p <> nil then
            	   fail("mode=ml: top_p cannot be specified when layer_depth is set!")	
    			end if
    			p_layer = options.layer_depth * 100
    			p_top = p_bottom - p_layer
    		else if options.top_p <> nil then
    		    p_top = options.top_p * 100 
    		else
    		    p_top = p_bottom - 100*100
    		end if
    		
		else if options.mode = "ml50" then
		      p_top = p_bottom - 50*100
		else if options.mode = "ml100" then
		      p_top = p_bottom - 100*100
		else 
		      fail("Internal error. Cannot handle mode=", options.mode)
		end if
		
		if options.mean_start_from_theta = 1 then
            th_mean=0	
            td_mean=0
            cnt_mean=0
            for i=1 to num do
                if p[i] >= p_top and p[i] <= p_bottom then
                    th_mean = th_mean + potential_temperature(t[i], p[i])
                    td_mean = td_mean + td[i]
                    cnt_mean = cnt_mean + 1
                end if	
            end for
		
            if cnt_mean = 0 then
                print("mode=ml: no profile points found in specified layer (p_bottom = ",p_bottom," hPa, p_top = ",p_top," hPa)")	
                return nil
            end if
            
            start.p=p[1]
            start.t=temperature_from_potential_temperature(th_mean/cnt_mean, p[1])
            start.td=td_mean/cnt_mean
		else 
            t_mean=0	
            td_mean=0
            p_mean=0
            cnt_mean=0
            for i=1 to num do
                if p[i] >= p_top and p[i] <= p_bottom then
				    t_mean = t_mean + t[i]
                    td_mean = td_mean + td[i]
                    p_mean = p_mean + p[i]
                    cnt_mean = cnt_mean + 1
			     end if	
            end for
		
            if cnt_mean = 0 then
                print("mode=ml: no profile points found in specified layer (p_bottom = ",p_bottom," hPa, p_top = ",p_top," hPa)")	
                return nil
            end if
			
            start.p=p_mean/cnt_mean
            start.t=t_mean/cnt_mean
            start.td=td_mean/cnt_mean
        end if	
        
	else if options.mode = "mucape" then
		p_bottom = p[1]
		if options.bottom_p <> nil then		
			p_bottom = options.bottom_p * 100
		end if
		
		if options.layer_depth <> nil then
        	if options.top_p <> nil then
        	   fail("mode=mucape: top_p cannot be specified when layer_depth is set!")	
			end if
			p_layer = options.layer_depth * 100
			p_top = p_bottom - p_layer
		else if options.top_p <> nil then
		    p_top = options.top_p * 100 
		else 
		    p_top= p_bottom - 300*100 
		end if
		
		cape = __compute_cape_max(p_bottom, p_top, t, td, p, options)
		
		if cape.cape <> 0  then			
		  start.p = cape.p
		  start.t = cape.t
		  start.td = cape.td
		else 
		  start.p=p[1]
		  start.t=t[1]
		  start.td=td[1]
		end if
		
	end if		
		
	# start conditions: check units 
	
	if start.t < 60 then
		print("invalid start temperature = ",start.t," K")
		return nil
	end if
	
	if start.td < 60 then
		print("invalid start dewpoint = ",start.td," K")
		return nil
	end if	
	
	if start.p < 1200 then
		print("invalid start pressure = ", start.p, " Pa")
		return nil
	end if			
	
	if options.debug = 1 then
	   print("start=",start)
	end if
	
	return __compute_parcel_path_detailed(start, t, td, p, 
	           0, options)

end __compute_parcel_path_core			   
                
function __compute_parcel_path_detailed(start_parcel: definition, t_prof: vector, td_prof: vector, p: vector,
               cape_only: number,  options: definition)
 
    __DO_DEBUG_PARCEL = 0
	
	if __DO_DEBUG_PARCEL then
	   print("start_parcel=",start_parcel)
	end if   
	
	# at this point all the checks on the input data must have been done!!!
	num = count(p)
	
	# compute the LCL
	lcl_parcel = lifted_condensation_level(start_parcel.t, start_parcel.td, start_parcel.p)

	if __DO_DEBUG_PARCEL then
		print("lcl=",lcl_parcel)
	end if
	
	# the LCL must exist to continue
	if lcl_parcel = nil then
		print("LCL does not exist")
		return nil
	end if
			
	#t_lcl_parcel = lcl_parcel.t
	#p_lcl = lcl_parcel.p

	# the LCL must be above (in terms of height) the start pressure
	if lcl_parcel.p > start_parcel.p then
	    lcl_parcel = start_parcel
	    if options.debug = 1 then
		  print("LCL (=",lcl_parcel.p,"Pa) is below start level (=",start_parcel.p,"Pa)")
		end if
		#res_obj = __build_path_object_nopath(start_parcel, lcl_parcel, options)
        #return res_obj
	end if
	
	# compute the profile temperature at the LCL
	lcl_prof = __compute_profile_at_level(lcl_parcel.p, t_prof, p)
	if lcl_prof = nil then
	    if options.debug = 1 then
		   print("Could not interpolate profile temperature to LCL=", lcl_prof)
		end if
		res_obj = __build_path_object_nopath(start_parcel, lcl_parcel, options)
		return nil
	end if	
	
	#t_lcl_prof = lcl_prof.t
	idx_lcl = lcl_prof.index

	if __DO_DEBUG_PARCEL then
		print("lcl_prof=",lcl_prof)
		print("lcl_parcel=",lcl_parcel)		
	end if

	#cape = nil
	#cin = -1
	#el = nil
	#top = nil
		
	# compute the mixing ratio of the parcel
	mr_lcl_parcel = saturation_mixing_ratio(start_parcel.td, start_parcel.p)
		
	# compute the equivalent potential temperature at the LCL. This is an 
	# invariant along the moist adiabat.
	eqpt_parcel = __equivalent_potential_temperature_lcl(lcl_parcel.t,mr_lcl_parcel,
	                       lcl_parcel.p, options.ept_method)	
	                       
	#e2 = __equivalent_potential_temperature_lcl(lcl_parcel.t,mr_lcl_parcel,
	#                       lcl_parcel.p, "old")	 
	#precision(9) 
	#print("ept: ",eqpt_parcel, " ", e2)   
	                                            
	if __DO_DEBUG_PARCEL then
		print("eqpt_parcel=",eqpt_parcel,"K")		
	end if
		
	# compute the potential temperature on the dry adiabat
    pt_parcel=potential_temperature(start_parcel.t,start_parcel.p)
	if __DO_DEBUG_PARCEL then
		print("pt_parcel=",pt_parcel, "K")		
	end if
    
    rv = nil		
		
	# Perform virtual temperature based computations
	if options.virtual then
	   t_parcel = t_prof
	   tv_parcel = t_prof
	   tv_prof = t_prof
	   q_lcl_parcel = specific_humidity_from_mixing_ratio(mr_lcl_parcel)
	   #print("q_lcl_parcel=", q_lcl_parcel)
	   # compute parcel profile
	   for i = 1 to count(p) do		
	      qs = saturation_specific_humidity(td_prof[i], p[i])
	      #print("qs=", qs)
		  tv_prof[i] = virtual_temperature(t_prof[i], qs)
			
		  # dry adiabat - below the LCL
		  if p[i] > lcl_parcel.p then				
			t_parcel[i] = temperature_from_potential_temperature(pt_parcel,p[i])
			tv_parcel[i] = virtual_temperature(t_parcel[i], q_lcl_parcel)
		  # moist adiabat - above the LCL
		  else	
			t_parcel[i] = __temperature_from_equivalent_potential_temperature(eqpt_parcel,p[i],options.ept_method)	
		    qs = saturation_specific_humidity(t_parcel[i], p[i])	    
		    tv_parcel[i] = virtual_temperature(t_parcel[i], qs)	
		  end if
	   end for	
	   
	   #precision(8)
	   #for i=1 to count(t_parcel) do
	   #    print(i, " ", p[i]/100, " ", tv_parcel[i])
	   #end for
	      
	   start_parcel_virt = start_parcel
	   start_parcel_virt.t = virtual_temperature(start_parcel.t,q_lcl_parcel)
	   lcl_parcel_virt = lcl_parcel
	   lcl_parcel_virt.t = virtual_temperature(lcl_parcel.t, q_lcl_parcel)
	 
	   # determine areas
	   area_lst =__compute_parcel_areas(start_parcel_virt, tv_parcel, tv_prof, p)
	   
	   # compute cape, cin etc
	   rv = __compute_buoyancy(area_lst, lcl_parcel.p, tv_parcel, tv_prof, p, eqpt_parcel, 
	                           cape_only, options, __DO_DEBUG_PARCEL)
	                                
	# perform temperature based computations
	else
	   t_parcel = t_prof
	   # compute parcel profile
	   for i = 1 to count(p) do		
		  # dry adiabat - below the LCL
		  if p[i] > lcl_parcel.p then				
			t_parcel[i] = temperature_from_potential_temperature(pt_parcel,p[i])
		  # moist adiabat - above the LCL
		  else	
			t_parcel[i] = __temperature_from_equivalent_potential_temperature(eqpt_parcel,p[i],options.ept_method)	
		  end if
		  #print(i,") ", t_parcel[i], " ", t_prof[i], " ", t_parcel[i] > t_prof[i])
	   end for	
	   
	   # determine areas
	   area_lst =__compute_parcel_areas(start_parcel, t_parcel, t_prof, p)
	     
	   # compute cape, cin etc  
       rv = __compute_buoyancy(area_lst, lcl_parcel.p, t_parcel, t_prof, p, eqpt_parcel, 
                               cape_only, options, __DO_DEBUG_PARCEL)    
	end if
	
	cape = rv[1]
	
	if cape_only = 1 then
        return rv
	else	
	    cape = rv[1]
        cin = rv[2]
        el = rv[3]
        top = rv[4] 
        area_lst = rv[5]  
	end if
	
	if __DO_DEBUG_PARCEL then
		__print_parcel_area(area_lst)
	end if

	# ------------------------------------
	#  the parcel path curve/obj
	# ------------------------------------
	
	if options.virtual then
	    li = __compute_li(lcl_parcel.p, pt_parcel, eqpt_parcel, tv_prof, p, options.ept_method)	
        res_obj = __build_path_object(area_lst, cape, cin, el, top, li, start_parcel_virt,
                    lcl_parcel_virt, lcl_prof, tv_parcel, tv_prof, p, options)
    else
        li = __compute_li(lcl_parcel.p, pt_parcel, eqpt_parcel, t_prof, p, options.ept_method)
        res_obj = __build_path_object(area_lst, cape, cin, el, top, li, start_parcel, 
                    lcl_parcel, lcl_prof, t_parcel, t_prof, p, options)
    end if
     
    if __DO_DEBUG_PARCEL then
		__print_parcel_result(res_obj)
	end if
	
	return res_obj

end __compute_parcel_path_detailed

function __compute_li(lcl_p, pt_parcel, eqpt_parcel, t_prof, p, ept_method)

    pval = 50000
    li_prof = __compute_profile_at_level(pval, t_prof, p)
    
    if lcl_p > pval then
        li_parcel = __temperature_from_equivalent_potential_temperature(eqpt_parcel,pval, ept_method)
    else	
        li_parcel = temperature_from_potential_temperature(pt_parcel, pval)
    end if
    
    return li_prof.t - li_parcel
	
end __compute_li
  
#=====================================================================================
# Compute all intersections and define areas between the profile and the parcel path. 
#
# Input: 
#		start: the start point of the parcel
#		t_parcel: the temperature along the parcel path on the profile's levels (K)
#       t_prof: the temperature profile (K)
#		p: the pressure profile (Pa)
# Return:
#       -list of definitions of the following format:
#          (positive: 1 if it as a positive area, otherwise it is a negative area
#		    bottom: the bottom point (t,p) of the area on the parcel side 
#           bottom_prof: the bottom point (t,p) of the area on the profile side. If the area is closed
#                        at the bottom it is nil. 
#           top: the top point (t,p) of the area on the parcel side
#           top_prof: the top point (t,p) of the area on the profile side. If the area is closed
#                        at the top it is nil.
#           index_start: the index of the first level in the given area
#           index_end: the index of the last level in the given area           
#		-on error nil is returned
#==============================================================================

function __compute_parcel_areas(start, t_parcel: vector, t_prof: vector, p: vector)
		
	num = count(p)
	if num <= 2 then
		#fail("num <= 2")
		return nil
	end if		
	
	# if all levels are above the start level there are no areas
	if start.p > p[1] + 1E-5 then
		return nil
	end if	
		
    SAME_PRES_EPS = 1E-3
    SAME_TEMP_EPS = 1E-5		
		
	# find first index above (or on) the start level
	i = 1
	while i <= num and p[i] - start.p > SAME_PRES_EPS do
		i = i + 1
	end while	
	
    # if all the levels are below the start level, there are no areas
	if i > num then
		return nil
	end if	
	
	# this is the first index above (or on) the start level
	idx_start = i
	
	# detect if start is on a profile level
	start_is_level = 0	
	if abs(start.p - p[idx_start]) < SAME_PRES_EPS then
		start_is_level =  1
	end if	
	
	# sanity check
	if start_is_level = 0 and idx_start = 1 then
		return nil
	end if	
	
	
	# the start is always on the parcel but might not be a profile poin. We need to 
	# check for it
	start_prof = start
	start_prof.t = t_prof[idx_start]

	# if the start is not on a profile level we need to 
	# interpolate the profile onto it	
	if start_is_level = 0 then
	
		# compute the profile temperature on the start level
		val = __compute_profile_at_level(start.p, t_prof, p)
		if val = nil then
			return nil
		end if	
		start_prof.t = val.t
	end if
		
	# create object for the first area - its type may not be known at this time
	# if the parcel starts from the profile				
	area = __make_parcel_area()
	area.bottom = start
	area.index_start = idx_start
	area.positive = -1 #unknown
		
	# parcel is warmer than the profile  - positive area 
	if start.t - start_prof.t > SAME_TEMP_EPS then 
	
		area.positive = 1
		area.bottom_prof = start_prof
	
	# parcel is colder than the profile - negative area
	else if start_prof.t - start.t > SAME_TEMP_EPS then 
	
		area.positive = 0
		area.bottom_prof = start_prof
	
	end if
	
	# if the start is on a profile level we start the computations form the level above
	if start_is_level then
		idx_start = idx_start + 1
	end if	
	
	# sanity check
	if idx_start <= 1 then
		return nil
	end if	
	
	#print("start:", start)
	#print("start_prof:", start_prof)
	#print("idx_start:", idx_start)
    	
	area_lst = nil
	
	for i=idx_start to num do					
			
		if area.positive = -1 then
		  area.positive = t_parcel[i] > t_prof[i]
		
		
	    else if (t_parcel[i] > t_prof[i] and area.positive = 0) or
	       (t_parcel[i] <= t_prof[i] and area.positive = 1) then
	       
	        # print("isec:", t_parcel[i], " ",  t_prof[i], " ", area.positive)
            # find intersection point of parcel path and profile
            sec = __find_intersection(t_prof[i-1],t_prof[i], t_parcel[i-1], t_parcel[i], p[i-1], p[i])
			
			# add top to the previous area and add it to the area list 	
            area.top = ("t": sec[1], "p": sec[2])
            # precision(8)
            # if area.positive <> 100 then
            # print("top:", area.top.t, " " , area.top.p, " prof:", t_prof[i-1]," " , t_prof[i], " parcel:", t_parcel[i-1], 
            #                 " ", t_parcel[i], " p:" , p[i-1], " " , p[i])
            # end if
                
            area.index_end = i-1
            area_lst = area_lst & [area] 	
				 	
            # create a new area 			
            area = __make_parcel_area()
            area.bottom = ("t": sec[1], "p": sec[2])
            area.index_start = i
            area.positive = t_parcel[i] > t_prof[i]
			
	    # we not yet determined the area type			       
        #else if area.positive = -1 then
		#  area.positive = t_parcel[i] > t_prof[i]
		end if 
	end for
						
	# add last area
	top_index = num
	if area.index_start <= top_index and 
	   abs(p[top_index]-area.bottom.p) > SAME_PRES_EPS then
		area.top = (t:t_parcel[top_index], p: p[top_index])
		area.top_prof = (t: t_prof[top_index],p: p[top_index])
		area.index_end = top_index
		area_lst = area_lst & [area]
	end if	
	
	return area_lst
	
end __compute_parcel_areas


#=====================================================================================
# Compute CAPE, CIN, Level of Free Convection (LFC) and Equilibrium Level (EL).
#
# The bottom area can be open at the bottom and the top area 
# can be open at the top!
#
# Input: 
#       area_lst: the list of the areas
#       lcl_p: the pressure of the LCL (Pa)
#		t_parcel: the temperature along the parcel path on the profile's levels (K)
#       t_prof: the temperature profile (K)
#		p: the pressure profile (Pa)
#==============================================================================

function __compute_buoyancy(area_lst, lcl_p, t_parcel, t_prof, p, eqpt_parcel, cape_only, options, __DO_DEBUG_PARCEL)
    
    cape = nil
    el = nil
    top = nil
    cin = 0
    #print("areas", area_lst)
    #print(lcl_p)
    if area_lst <> nil then
	
		# compute CAPE
		cape = __compute_cape(area_lst, lcl_p, t_parcel, t_prof, p)
		if __DO_DEBUG_PARCEL then
			print("cape=",cape)
		end if
		
		if cape_only = 1 then
		  if cape <> nil then
		      return cape.cape
		  else 
		      return nil
		  end if
		end if
		
        if cape_only <> 1 and cape <> nil then			
		    el = area_lst[cape.end_index].top
		    # compute TOP
			if options.stop_at_el = 0 and options.comp_top = 1 then
			    if cape.cape > 0 and cape.exit_status = 1 and cape.end_index < count(area_lst) then
				    top = __compute_top(area_lst[cape.end_index+1], cape.cape, eqpt_parcel, t_parcel, t_prof, p, options)
				    if __DO_DEBUG_PARCEL then
					   print("top=",top)
				    end if
			     end if
			end if
			
			# compute CIN, we use the top LFC!
			if cape.cape > 0 and cape.start_index > 1 then
				cin = __compute_cin(area_lst, cape.end_index, t_parcel, t_prof, p)				
				if __DO_DEBUG_PARCEL then
					print("cin=",cin)	
				end if				
			end if	
			
		end if
	end if	
	
	return [cape, cin, el, top, area_lst]	

end __compute_buoyancy


#=====================================================================================
# Compute the Convective Available Potential Energy (CAPE) using the parcel method.
# CAPE is the integral of buoyancy through the positive areas between the LCL and EL 
# bounded by the parcel path and the profile.
#
# Input: 
#		areas: the list of the areas
#       p_lcl: the pressure at the LCL (Pa)
#		t_parcel: the temperature along the parcel path on the profile's levels (K)
#       t_prof: the temperature profile (K)
#		p: the pressure profile (Pa)
# Return:
#       -a definition with the following members:
#          cape: the value of CAPE (J/kg)
#          start_index: the index of the (positive) area where the CAPE computation started
#          end_index: the index of the (positive) area where the CAPE computation finished
#          exit_status: 1 if the computation ended at the top of an area, otherwise 
#                      the computation stopped before reaching the top of the area       
#		-on error 0 is returned
#==============================================================================

function __compute_cape(areas: list, p_lcl: number, t_parcel: vector,t_prof: vector, p: vector)
					
	# gas constant
	Rd = 287.0597 # J/kg/K
	
	cape = 0.0
	area_start_index = 0
	area_end_index = 0
	exit_status = 0
	
	# we need at least one area
	if count(areas) = 0 then
		return nil
	end if
	
	for i=1 to count(areas) do
		
		#print(i, " ", areas[i])
		#print(" ", p_lcl)
		# we only use positive areas when they end above the LCL and
		# start below the 100 hPa level
		if areas[i].positive = 1 and 
			areas[i].top.p <= p_lcl and areas[i].bottom.p > 10000 then
				
			if area_start_index = 0 then	
				area_start_index = i
			end if
			
			area_end_index = i
			exit_status = 1
			
			t_prof_bottom=areas[i].bottom.t
			# the area can be open at the bottom
			if areas[i].bottom_prof <> nil then
				t_prof_bottom=areas[i].bottom_prof.t
			end if	

			t_prof_top=areas[i].top.t
			# the area can be open at the top
			if areas[i].top_prof <> nil then
				t_prof_top=areas[i].top_prof.t
			end if	
			
			idx_start = areas[i].index_start
			idx_end = areas[i].index_end
		
			# we start at the bottom	
			p1 = areas[i].bottom.p
			t_parcel_1 = areas[i].bottom.t
			t_prof_1 = t_prof_bottom
			
			for j=idx_start to idx_end+1 do
	
				# we only do the CAPE computations up to 50 hPa (~ 21 km)
				if p1 >= 5000 then
	
					# we are below the EL
					if j <> idx_end+1 then
						p2 = p[j]
						t_prof_2 = t_prof[j]
						t_parcel_2 = t_parcel[j]					
					
					# the last point
					else 
						p2 = areas[i].top.p
						t_prof_2 = t_prof_top 
						t_parcel_2 = areas[i].top.t
					end if
	
					dlnp = log(p2) - log(p1)
					dt1 =  t_parcel_1 - t_prof_1
					dt2 =  t_parcel_2 - t_prof_2
					cape = cape - Rd * dlnp * (dt2 + dt1) / 2.0
		
					p1 = p2
					t_prof_1 = t_prof_2
					t_parcel_1 = t_parcel_2						    
		   		else
		   			exit_status = 0
		   		end if
		   		
			end for								
		end if
	end for
	
	if area_start_index = 0 then
		return nil
	end if	
	
	return (cape: cape, start_index: area_start_index, end_index: area_end_index, exit_status: exit_status)
	
end __compute_cape

#===============================================================
# Compute the maximum cape in a given layer
#===============================================================
function __compute_cape_max(p_bottom, p_top, t_prof, td_prof, p, options)

	num = count(p)

	#print(p_bottom," ",p_top," ",p[1])

	cape_max = 0 
	t_cape= 0
	td_cape = 0
	p_cape = 0
			
	eps = 1E-4		
		
    cnt = 0		
  
	for i=1 to num do
		 
		if p_bottom + eps >= p[i] and p_top - eps <= p[i] then
			
			start_parcel  = ("t": t_prof[i], "td": td_prof[i], "p": p[i])
			
			#print(start_parcel)
			
			cape = __compute_parcel_path_detailed(start_parcel, t_prof, td_prof, p, 1, options)
	        
			#print("CAPE=",cape," ",start_parcel)
			if cape <> nil then     
			    if cape > cape_max then
				    cape_max = cape
				    t_cape= start_parcel.t
				    td_cape = start_parcel.td
				    p_cape = start_parcel.p
			    end if	
		    end if	
		    
			cnt = cnt + 1
		end if	
	end for
	
	#print("cape cnt=", cnt)
	return (cape: cape_max, t: t_cape, td: td_cape, p: p_cape)
		
end __compute_cape_max

#=====================================================================================
# Compute the the uppermost level (TOP) an ascending parcel can reach based on the parcel method.
# This is the level in the last (and open) "negative buoyancy area" where the negative buoyancy 
# energy equals the CAPE. 
#
# Input: 
#		areas: the neagtive are where the TOP computation has to be done
#       cape: the CAPE value (J/kg)
#       eqpt_parcel: the equivalent potential temperature of the parcel along the 
#                    moist adiabat (K)
#		t_parcel: the temperature along the parcel path on the profile's levels (K)
#       t: the temperature profile (K)
#		p: the pressure profile (Pa)
# Return:
#       -a definition with the following members:
#        	p: the pressure of TOP (Pa)
#           t_prof: the temperature of the profile at TOP
#           t_parcel: the temperature of the parcel at TOP
#           index_top: the index of the profile level just above the TOP         
#		-on error or if the TOP does not exist nil is returned
#==============================================================================	

function __compute_top(area, cape: number, eqpt_parcel: number, t_parcel: vector,t: vector, p: vector, options)
					
	# gas constant
	Rd = 287.05 # J/kg/K
	
	# we need a valid CAPE
	if cape <= 0 then
		return nil
	end if
	
	p_top = -1
	t_top_prof = -1
	t_top_parcel = -1
	idx_top = 0	
	energy = 0.0
	
	# the start point is the EL
    el = area.bottom
	t_el = el.t
	p_el = el.p
	
	if area.bottom_prof <> nil then
		return nil
	end if	
		
	idx_start = area.index_start
	idx_end = area.index_end
	
	if idx_end <= 0 then
		return nil
	end if	
			
	# we start at the EL	
	p1 = p_el
	p2 = p1	
	t1 = t_el
	t2 = t1		
	t_parcel_1 = t1
	t_parcel_2 = t_parcel_1
	
	for i=idx_start to idx_end do
		
		if idx_top = 0 then
		
			p2 = p[i]
			t2 = t[i]
			#the parcel is on a moist adiabat - its temperature is already computed
			t_parcel_2 = t_parcel[i]					
	
			# compute buoyancy (energy)
			dlnp = log(p2) - log(p1) # < 0
			dt1 =  t_parcel_1 - t1 # <=0
			dt2 =  t_parcel_2 - t2 # <=0
			de = Rd * dlnp * (dt2 + dt1) / 2.0
			energy = energy + de
		
			#print("energy ",p2," ",energy)
		
			p1 = p2
			t1 = t2
			t_parcel_1 = t_parcel_2						    
		   
		    # when the accumulated energy is larger than the CAPE we are just above the TOP
			if energy >= cape then
				idx_top = i
				de_target = cape - (energy - de) 				
			end if	    
		end if									
	end for
	
	# there is a top - we only knows that it is between two pressure levels.
	# Now we need to determine its exact pressure!
	if idx_top <> 0 then
	
		# we find the top pressure by iteration
		iter_num=10
    	eps=0.25
		
		# start with a bottom half of the given layer
		dp = (p[idx_top-1]-p[idx_top])/2
	
		p1 = p[idx_top-1]
		p2 = p1-dp
		t1 = t[idx_top-1]
		t2 = __logp_interpolation(t1, t[idx_top], p1, p[idx_top], p2)		
		t_parcel_1 = t1
		t_parcel_2 = __temperature_from_equivalent_potential_temperature(eqpt_parcel,p2, options.ept_method)
		top_found = 0
		
		for i=1 to iter_num do 
		 
		 	if top_found = 0 then
		 
		 		# compute the buoyancy in the layer
				dlnp = log(p2) - log(p1) # <0
				dt1 =  t_parcel_1 - t1 # <= 0
				dt2 =  t_parcel_2 - t2 # <=0
				de = Rd * dlnp * (dt2 + dt1) / 2.0
			
				# check convergence
				if abs(de-de_target) < eps or i=iter_num then
					p_top = p2
					t_top_prof = t2
					t_top_parcel = t_parcel_2
					top_found = 1
				
				# adjust the top pressure of the layer
				else					
			 		# decrease pressure delta as we converge, dp is always positive
        	 		dp = dp / 2.0
						
					if de < de_target then
						p2 = p2 - dp # moving up
					else
						p2 = p2 + dp #moving down	
					end if
					
					t2 = __logp_interpolation(t1, t[idx_top], p1, p[idx_top], p2)	
        	 		t_parcel_2 = temperature_from_potential_temperature(eqpt_parcel,p2)
		
				 end if
			end if		
		end for
		
		return (p:p_top, t_prof: t_top_prof, t_parcel: t_top_parcel, index_top: idx_top)
		
	end if		
	
	return nil
	
end __compute_top		
	
#===============================================================================================
# Compute the Convective Inhibition (CIN). This is the negative buoyancy energy the ascending
# parcel has to overcome to reach the LFC. There can be several positive areas above the LCL (i.e.
# several LFCs). Here we use the one with the lowest pressure (passed as an input arg).
#
# Input: 
#		areas: the buoyancy areas
#       lfc_area_index: the index of the positive area belonging to the LFC
#		t_parcel: the temperature along the parcel path on the profile's levels (K)
#       t_prof: the temperature profile (K)
#		p: the pressure profile (Pa)
# Return:
#       the value of CIN (J/kg) (>=0)
#=============================================================================================		

function __compute_cin(areas: list, lfc_area_index: number, t_parcel: vector, t_prof: vector, p: vector)
					
	# gas constant
	Rd = 287.05 # J/kg/K
	
	cin = 0
	
	# we need at least one area
	if count(areas) = 0 then
		return cin
	end if
	
    # print("lfc_area_index=",lfc_area_index) 
	for i=1 to lfc_area_index-1 do
		
		# we only use negative areas
		if areas[i].positive = 0 then
			
			t_prof_bottom = areas[i].bottom.t
			# the area can be open at the bottom
			if areas[i].bottom_prof <> nil then
				t_prof_bottom=areas[i].bottom_prof.t
			end if	
			
			t_prof_top = areas[i].top.t
			# the area can be open at the top
			if areas[i].top_prof <> nil then
				t_prof_top=areas[i].top_prof.t
			end if	
		
			# we start at the bottom
			p1 = areas[i].bottom.p
			t_parcel_1 = areas[i].bottom.t
			t_prof_1 = t_prof_bottom
			
			idx_start = areas[i].index_start
			idx_end = areas[i].index_end
			
			# print("t_parcel_1=", t_parcel_1)
			# print("t_prof_1=", t_prof_1)
			
			for j=idx_start to idx_end+1 do

                if j <> idx_end+1 then
				    p2 = p[j]
				    t_prof_2 = t_prof[j]
                    t_parcel_2 = t_parcel[j]					
				# the last point
				else 
				    p2 = areas[i].top.p
				    t_prof_2 = t_prof_top
				    t_parcel_2 = areas[i].top.t
				end if
	
				dlnp = log(p2) - log(p1)
				dt1 =  t_parcel_1 - t_prof_1
				dt2 =  t_parcel_2 - t_prof_2
				#print("dlnp=", dlnp)
				#print("dt1=", dt1, " dt2=", dt2)
				cin = cin + Rd * dlnp * (dt2 + dt1) / 2.0
		
				p1 = p2
				t_prof_1 = t_prof_2
				t_parcel_1 = t_parcel_2						    
			end for								
		end if
	end for
		
	return cin
	
end __compute_cin


function __make_parcel_area()

	return (bottom: nil, bottom_prof: nil, top: nil, top_prof: nil, index_start: 0, index_end: 0, positive: -1)	

end __make_parcel_area

function __build_path_object_nopath(start_parcel, lcl_parcel, options)
                
   res=(t: [], 
	   p: [],
	   area: [], 
	   lcl: __tp_point_to_c_hpa(lcl_parcel),
	   cape: 0,
	   cin: 0,
	   li: nil,
	   lfc: nil,
	   el: nil,
	   top: nil,
	   start: (t: start_parcel.t - 273.16,
	           td: start_parcel.td - 273.16,
	           p: start_parcel.p / 100,
	           mode: options.mode)
	)
	
	return res          
                
end __build_path_object_nopath

# ------------------------------------
# Build the parcel path curve. The parcel path is already
# defined on the same levels as the profile, but we still need 
# to insert the start, LCL, LFC, EL, TOP and all the
# other intersections
# ------------------------------------
function __build_path_object(area_lst, cape, cin, el, top, li, 
                start_parcel, lcl_parcel, lcl_prof, t_parcel, t_prof, p, options)
	
	#__print_parcel_area(area_lst)
	
	p_top = 0
	if top <> nil then
		p_top = top.p
	else if options.stop_at_el = 1 and cape <> nil then
	    p_top = area_lst[cape.end_index].top.p
	end if
	
	num = 0
	for i=1 to count(area_lst) do
	   if p_top <= area_lst[i].bottom.p then
	      if i=1 then
	        num = num + 1 
	      end if
	      if lcl_prof.index >= area_lst[i].index_start and
	         lcl_prof.index <= area_lst[i].index_end then
	         num = num + 1
	      end if
	      num = num + area_lst[i].index_end - area_lst[i].index_start + 1
	      if p_top <= area_lst[i].top.p then
	           num = num + 1
	      end if
	   end if
	end for
	
	#print("Num=", num)
	t_parcel_lst = vector(num)
	p_parcel_lst = vector(num)
	
	# the start level
	pos = 1
	for i=1 to count(area_lst) do
	
		if p_top <= area_lst[i].bottom.p then	
		
			if i=1 then
				t_parcel_lst[pos] = area_lst[i].bottom.t
				p_parcel_lst[pos] = area_lst[i].bottom.p
				pos = pos + 1
			end if
		
			for j = area_lst[i].index_start to area_lst[i].index_end do			
			
				if j = lcl_prof.index then
					t_parcel_lst[pos] = lcl_parcel.t
					p_parcel_lst[pos] = lcl_parcel.p
					pos = pos + 1
				end if	
				
				if p_top <= p[j] then
					t_parcel_lst[pos] = t_parcel[j]
					p_parcel_lst[pos] = p[j]
					pos = pos + 1
				end if	
			end for	
			
			if p_top <= area_lst[i].top.p then
				t_parcel_lst[pos] = area_lst[i].top.t
				p_parcel_lst[pos] = area_lst[i].top.p
				pos = pos + 1
			end if	
		
		end if
		
	end for
	#print("pos=", pos)	
    # convert result to C and hPa
    t_parcel_lst = t_parcel_lst - 273.16
    p_parcel_lst = p_parcel_lst / 100	
    
    #print("t_parcel_lst=",t_parcel_lst)
    #print("p_parcel_lst=",p_parcel_lst)
    
    pos = pos -1
    if pos < count(t_parcel_lst) then
	   t_parcel_lst = t_parcel_lst[1,pos]
	   p_parcel_lst = p_parcel_lst[1,pos]
	end if
	        
	        
	#for i=1 to count(t_parcel_lst) do
	#   t_parcel_lst[i] = t_parcel_lst[i] - 273.16
	#   p_parcel_lst[i] = p_parcel_lst[i] / 100
	#end for   
	   
	# build the polygons making up the areas		
	
	poly_lst = nil	
	
	area_num = count(area_lst)
	if cape <> nil and options.comp_top = 0 then
	   area_num = cape.end_index
	end if
	
	for i=1 to area_num do

		if p_top <= area_lst[i].bottom.p then	
				
		    # estimate count
		    num = 1		
			if lcl_prof.index >= area_lst[i].index_start and lcl_prof.index <=	area_lst[i].index_end then
			     num = num + 1
			end if
		    num = num + 2 * (area_lst[i].index_end - area_lst[i].index_start + 1)		
			
			if p_top <= area_lst[i].top.p then
				num = num + 1				
				if area_lst[i].top_prof <> nil then
					num = num + 1
				end if
		    end if
		    if area_lst[i].bottom_prof <> nil then
		      	num = num + 1
			end if
								
		    t_poly = vector(num)
		    p_poly = vector(num)
		    pos = 1	
			
			# start from bottom on the parcel side
			t_poly[pos] = area_lst[i].bottom.t
			p_poly[pos] = area_lst[i].bottom.p
			pos = pos + 1
		
			# goes up along parcel path
			for j = area_lst[i].index_start to area_lst[i].index_end do			
			
				if j = lcl_prof.index then
					t_poly[pos] = lcl_parcel.t
					p_poly[pos] = lcl_parcel.p
					pos = pos + 1
				end if	
				
				if p_top <= p[j] then
					t_poly[pos] = t_parcel[j]
					p_poly[pos] = p[j]
					pos = pos + 1
				end if
					
			end for	
		
			# add top (parcel side)
			if p_top <= area_lst[i].top.p then
				t_poly[pos] = area_lst[i].top.t
				p_poly[pos] = area_lst[i].top.p
				pos = pos + 1
			
				# add top on profile side if area is open on the top
				if area_lst[i].top_prof <> nil then
					t_poly[pos] = area_lst[i].top_prof.t
					p_poly[pos] = area_lst[i].top_prof.p
					pos = pos + 1
				end if
		    end if
		    
		    # goes down along profile
			for j = area_lst[i].index_end to area_lst[i].index_start by -1 do			
				if p_top <= p[j] then
					t_poly[pos] = t_prof[j]
					p_poly[pos] = p[j]
					pos = pos + 1
				end if				
			end for	
			
			# add bottom on profile side if area is open at the bottom
			if area_lst[i].bottom_prof <> nil then
				t_poly[pos] = area_lst[i].bottom_prof.t
				p_poly[pos] = area_lst[i].bottom_prof.p
				pos = pos + 1
			end if
	       
	        # convert result to C and hPa	
	        #for j=1 to count(t_poly) do
	        #   t_poly[j] = t_poly[j] - 273.16
	        #   p_poly[j] = p_poly[j] / 100
	        #end for     
		
		    t_poly = t_poly - 273.16
	        p_poly = p_poly / 100
	        
	        pos = pos -1
	        if pos < num then
	           t_poly = t_poly[1,pos]
	           p_poly = p_poly[1,pos]
	        end if
	        
	        #print("pos=",area_lst[i].positive)
	        #print("  t_poly=", t_poly)
	        #print("  p_poly=", p_poly)
	        
			poly_lst = poly_lst & [ [(t: t_poly, p: p_poly, positive: area_lst[i].positive)]]
		end if
		
	end for	
	
	# create the resulting definition
	
	res=(t: t_parcel_lst, 
	   p: p_parcel_lst,
	   area: poly_lst, 
	   lcl: __tp_point_to_c_hpa(lcl_parcel),
	   cape: 0,
	   cin: cin,
	   li: li,
	   lfc: nil,
	   el: nil,
	   top: nil,
	   start: (t: start_parcel.t - 273.16,
	           td: start_parcel.td - 273.16,
	           p: start_parcel.p / 100,
	           mode: options.mode)
	)
	 

	if cape <> nil then
	    cape_val = 0
	    if cape.cape <> nil then
	       cape_val = cape.cape
	    end if
		res.cape = cape_val		
		res.lfc = __tp_point_to_c_hpa(area_lst[cape.end_index].bottom)
		res.el = area_lst[cape.end_index].top
		if res.el <> nil then
		  res.el = __tp_point_to_c_hpa(res.el)		
		end if
	end if	
	
	if top <> nil then
		res.top = __tp_point_to_c_hpa((t: top.t_parcel, p: top.p))
    end if		
	
	return res

end __build_path_object

#=============================================================================
# Compute the intersection of a temperature profile in a given layer and a 
# moist adiabat defined by its equivalent potential temperature.
#
# Input: 
#		t1,t2: the temperatures on the layer's boundaries (K)
#       p1,p2: the pressures on the layers's boundaries (Pa)
#       eqpt: the equivalent potential temperature (K)
#       dp_sign: 
# Return:
#       a definition of (t,p) containing the temperature (K) and the pressure (Pa)
#       of the intersection             
#==============================================================================

function __find_moist_intersection(t1,t2,p1,p2,eqpt,dp_sign, ept_method)
	
	dp = (p1 - p2)/2.0
	p = p2 + dp
	
	eps = 1E-5
	iter_num = 12
	
	for i=0 to iter_num do
		t = __logp_interpolation(t1,t2,p1,p2,p)		
		t_parcel = __temperature_from_equivalent_potential_temperature(eqpt,p, ept_method)
		
		dp = dp / 2.0
		if abs(t_parcel - t) < eps or i=iter_num then
			return (t:t,p:p)
		else if t_parcel > t then
			p = p + dp * dp_sign
		else 
			p = p - dp * dp_sign
		end if		
	end for
	
	return (t:t,p:p)		

end __find_moist_intersection

#=============================================================================
# Compute the intersection of a temperature profile in a given layer and a 
# dry adiabat defined by its potential temperature.
#
# Input: 
#		t1,t2: the temperatures on the layer's boundaries (K)
#       p1,p2: the pressures on the layers's boundaries (Pa)
#       pt: the potential temperature (K)
#       dp_sign: 
# Return:
#       a definition of (t,p) containing the temperature (K) and the pressure (Pa)
#       of the intersection             
#==============================================================================

function __find_dry_intersection(t1,t2,p1,p2,pt,dp_sign)
	
	dp = (p1 - p2)/2.0
	p = p2 + dp
	
	eps = 1E-5
	iter_num = 12
	
	for i=0 to iter_num do
		t = __logp_interpolation(t1,t2,p1,p2,p)		
		t_parcel = temperature_from_potential_temperature(pt,p)
		
		dp = dp / 2.0
		if abs(t_parcel - t) < eps or i = iter_num then
			return (t:t,p:p)
		else if t_parcel > t then
			p = p + dp * dp_sign
		else 
			p = p - dp * dp_sign
		end if		
	end for
	
	return (t:t,p:p)		

end __find_dry_intersection
	
function __find_intersection(t1a,t2a,t1b,t2b,p1,p2)

    eps = 1E-8
    if abs(t1b - t1a) < eps or abs(p2 - p1) < eps then
        return [t1a, p1]
    end if   
    dp = (p2 - p1)
    p = p1 + dp * (t1a - t1b) / (t2b - t1b - t2a + t1a)  
    t = t1a + (p -p1)* (t2a - t1a)/dp
	return [t, p]		

end __find_intersection	
	
	
#=============================================================================
# Compute the temperature on a given pressure level in a profile using 
# logartihmic pressure interpolation.
#
# Input: 
#		p_level: the target pressure level (Pa)
#       t: the temperature profile (K)
#		p: the pressure profile (Pa)
# Return:
#       -a definition of (t,index) containing the temperature (K) on the target level and 
#        the index of the profile just above the target level           
#		-on error nil is returned
#==============================================================================

function __compute_profile_at_level(p_level: number, t: vector, p: vector)
		
	num = count(p)
	
	if num <= 2 then
		return nil
	end if		
			
	# the target level cannot be below the first profile level 
	if p_level > p[1] then
		return nil
	end if
	
	if p_level = p[1] then
	   return (t: t[1], index: 1)
	end if
	
	# find the first level above the target level
	idx = -1
	for i=1 to num do
	   if idx = -1 and p_level >= p[i] then
	      idx = i
	   end if
	end for
	
	# if all levels are below the target level 
	if idx = -1 then		
		return nil
	end if	
	
	# interpolate profile to target level
	t_prof=__logp_interpolation(t[idx], t[idx-1], p[idx], p[idx-1], p_level)	
	
	return (t:t_prof, index: idx)

end __compute_prof_at_level	

#=============================================================================
# Compute the value of a given parameter on a given pressure level in a
# given layer using logartihmic pressure interpolation.
#
# Input: 
#		x1,x2: the parameter's values at the layer's boundaries
#       p1,p2: the pressure at the layer's boundaries (Pa)
#		p: the target pressure (Pa)
# Return:
#       the parameter's value at the given pressure level 
#==============================================================================

function __logp_interpolation(x1, x2, p1, p2, p)

	if  x1 = x2 or p1 = p2 then
		return x1
	end if

	return x1 + (x2-x1) * log(p/p1)/log(p2/p1)

end __logp_interpolation

#=================================================================================
# Compute the equivalent potential temperature from the parcel's state at the LCL.
#
# Input: 
#       t_lcl: the parcel's temperature at the LCL (K)
#		mr_lcl: the parce's saturation mixing ration at the LCL (kg/kg)
#       p_lcl: the pressure at the LCL (Pa)
# Return:
#       the equivalent potential temperature (K)           
#==============================================================================

# function __equivalent_potential_temperature_lcl(t_lcl: number, mr_lcl: number,p_lcl: number)
# 
#     b=2.674456
#     
#     t_lcl_kelvin = t_lcl
#     
#     return potential_temperature(t_lcl,p_lcl)*exp(b*1000*mr_lcl/t_lcl_kelvin)
# 	
# end __equivalent_potential_temperature_lcl	


function __equivalent_potential_temperature_lcl(t: number, mr: number,p: number, method: string)

    if method = "old" then
         b=2674.456
         return potential_temperature(t,p)*exp(b*mr/t)        
    else if method = "ifs" then
        K = 2500800.0/1004.79 #Lv/cp
        q = mr/(1+mr)
        return potential_temperature(t,p)*exp(K*q/t)
    else if method = "bolton39" then
         KAPPA=0.285691 #Rd/cp
         es = saturation_vapour_pressure(t)
         return t * (1E5/(p - es))^KAPPA * exp((3036/t - 1.78)*mr*(1+ 0.448*mr))
    end if
    return nil
	
end __equivalent_potential_temperature_lcl

function __tp_point_to_c_hpa(point: definition)

    return (t: point.t - 273.16,p: point.p / 100)

end  __tp_point_to_c_hpa

function __print_parcel_area(v)
		
	for i = 1 to count(v) do
		print("AREA: ",i)
		print(" positive=",v[i].positive)
		print(" bottom=",v[i].bottom)
		print(" bottom_prof=",v[i].bottom_prof)
		print(" top=",v[i].top)
		print(" top_prof=",v[i].top_prof)
		print(" index_start=",v[i].index_start)
		print(" index_end=",v[i].index_end)		
	end for
	
end __print_parcel_area		

function __print_parcel_result(r)
		
    print("RESULT:")
    aStr=""
	if r.area <> nil then 
		loop a in r.area
			aStr = aStr & a.positive
		end loop
	end if
			
	print(" areas=(",count(r.area),") ",aStr)
	print(" cape=",r.cape)
	print(" cin=",r.cin)
	print(" lcl=",r.lcl)
	print(" lfc=",r.lfc)
	print(" el=",r.el) 
	print(" top=",r.top)
	
end __print_parcel_result	

function __print_parcel_result_for_test(r)
		
    if r = nil then
        return
    end if	
    
	print(tab, tab, "cape: ",r.cape,",")
	print(tab, tab, "cin: ",r.cin,",")
	print(tab, tab, "li: ",r.li,",")
	print(tab, tab, "lcl: ",r.lcl,",")
	print(tab, tab, "lfc: ",r.lfc,",")
	print(tab, tab, "el: ",r.el,",") 
	print(tab, tab, "top: ",r.top,",")
	
	print(tab, tab, "area_count: ",count(r.area),",")
	aStr="a"
	if r.area <> nil then 
		loop a in r.area
			aStr = aStr & a.positive
		end loop
	end if
	print(tab, tab, "area_type: \"",aStr,"\",")
	
	print(tab, tab, "t_count: ",count(r.t),",")
	print(tab, tab, "p_count: ",count(r.p),",")

	num = count(r.t)
	if num > 0 then
	   print(tab, tab, "t_first: ",r.t[1],",")
	   print(tab, tab, "t_last: ",r.t[num],",")
	   print(tab, tab, "t_mean: ",mean(r.t),",")
	end if
	   
	num = count(r.p) 
	if num > 0 then
	   print(tab, tab, "p_first: ",r.p[1],",")
	   print(tab, tab, "p_last: ",r.p[num],",")
	   print(tab, tab, "p_mean: ",mean(r.p))
	end if
	
end __print_parcel_result_for_test