function objective_min_cost_TNEP(pm::_PM.AbstractPowerModel)
    if pm.setting["FSprotection"] == true || pm.setting["NSprotection"] == true
        objective_min_cost_TNEP_FSNS(pm)
    elseif  pm.setting["Permanentloss"] == true
        objective_min_cost_TNEP_PL(pm)
    end
end

function objective_min_cost_TNEP_nocl(pm::_PM.AbstractPowerModel)
    if pm.setting["FSprotection"] == true || pm.setting["NSprotection"] == true
        objective_min_cost_TNEP_FSNS_nocl(pm)
    elseif  pm.setting["Permanentloss"] == true
        objective_min_cost_TNEP_PL_nocl(pm)
    end
end

function objective_min_cost_TNEP_FSNS_nocl(pm::_PM.AbstractPowerModel)
        base_nws = pm.setting["base_list"]
        cont_nws = pm.setting["Cont_list"]
        Total_sample = pm.setting["Total_sample"]
        curt_gen = pm.setting["curtailed_gen"]
        max_curt = pm.setting["max_curt"]
        year_base = pm.setting["year_base"]
        total_year = pm.setting["total_yr"]

        gen_cost = Dict()
        FFR_cost = Dict()
        FCR_cost = Dict()
        fail_prob = Dict()
        Inv_cost = _PM.var(pm)[:Inv_cost] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        Gen_cost = _PM.var(pm)[:Gen_cost] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        FFRReserves = _PM.var(pm)[:FFR_Reserves] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        FCRReserves = _PM.var(pm)[:FCR_Reserves] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        Cont = _PM.var(pm)[:Cont]     = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        Curt = _PM.var(pm)[:curt] = JuMP.@variable(pm.model, [i in 1:total_year], start = 0, lower_bound = 0)

        sol_component_value_mod_wonw(pm,   :Inv_cost, Inv_cost)
        sol_component_value_mod_wonw(pm,   :Gen_cost, Gen_cost)
        sol_component_value_mod_wonw(pm,   :Curt, Curt)
        sol_component_value_mod_wonw(pm,   :FFR_Reserves, FFRReserves)
        sol_component_value_mod_wonw(pm,   :FCR_Reserves, FCRReserves)
        sol_component_value_mod_wonw(pm,   :Cont, Cont)

        for (n, nw_ref) in _PM.nws(pm)
            for (r, reserves) in nw_ref[:reserves]
                FFR_cost[(n,r)] = reserves["Cf"]
                FCR_cost[(n,r)] = reserves["Cg"]
            end
            for (i,gen) in nw_ref[:gen]
                pg = _PM.var(pm, n, :pg, i)
                if length(gen["cost"]) == 1
                    gen_cost[(n,i)] = gen["cost"][1]
                elseif length(gen["cost"]) == 2
                    gen_cost[(n,i)] = gen["cost"][1]*pg + gen["cost"][2]
                elseif length(gen["cost"]) == 3
                    gen_cost[(n,i)] = gen["cost"][2]*pg + gen["cost"][3]
                else
                    gen_cost[(n,i)] = 0.0
                end
            end
        end

        FFR_list =  Dict()
        FCR_list = Dict()
        for (b,c,br) in cont_nws
            FFR_list[(b,c)] = FFR_cost[(c,2)]*100/10^6*_PM.var(pm, c, :Pff, 2)+
                              FCR_cost[(c,2)]*100/10^6*_PM.var(pm, c, :Pgg, 2)
            # a_ub, a_lb = _IM.variable_domain(_PM.var(pm, c, :Pff, 2))
            # display("a_ub:$a_ub a_lb:$a_lb")
        end

        Zff = _PM.var(pm)[:Zff] = Dict((b, c) => JuMP.@variable(pm.model, base_name = "zff[$(string(b)),$(string(c))]", binary = true, start = 0) for (b, c, br) in cont_nws)
        sol_component_value_mod_wonw(pm, :Zff, Zff)

        #maximum out of onshore converters
        FFRReserves_max = _PM.var(pm)[:FFRReserves_max] = JuMP.@variable(pm.model, [b in base_nws], base_name ="FFRReserves_max", start = 0)
        FCRReserves_max = _PM.var(pm)[:FCRReserves_max] = JuMP.@variable(pm.model, [b in base_nws], base_name ="FCRReserves_max", start = 0)
        for b in base_nws
            sol_component_value_mod(pm, b, :FFRReserves_max, FFRReserves_max[b])
            sol_component_value_mod(pm, b, :FCRReserves_max, FCRReserves_max[b])
        end

        base_cont= Dict()

        for b in base_nws
            # [item for item in a if item[2] == 4]
            base_cont = [tt for tt in cont_nws if tt[1] == b]

            ####_PM.var(pm)[:Zff] = JuMP.@variable(pm.model, [(z,w) in base_cont], base_name="zff", binary = true, start = 0 ) #just one variable, how to create multiple with an index
            for (z,w) in base_cont
            display(JuMP.@constraint(pm.model,  FFRReserves_max[b]  >= FFR_list[(z,w)] ))
            display(JuMP.@constraint(pm.model,  FFRReserves_max[b] <= FFR_list[(z,w)] + 100*(1-Zff[(z,w)])))
            end
            display(JuMP.@constraint(pm.model,  sum(Zff[(z,w)] for (z, w) in base_cont) == 1 ) )
        end

        Scale = 8760*5/Total_sample # 5 for no. of gap years between two time steps
        # multiperiod

		JuMP.@constraint(pm.model, Inv_cost == sum(sum(conv["cost"]*_PM.var(pm, 1, :conv_ne, i) for (i,conv) in _PM.nws(pm)[1][:convdc_ne]) for (n, nw_ref) in _PM.nws(pm)) +
		           sum(sum(branch["cost"]*_PM.var(pm, 1, :branchdc_ne, i) for (i,branch) in _PM.nws(pm)[1][:branchdc_ne]) for (n, nw_ref) in _PM.nws(pm)) )
		JuMP.@constraint(pm.model, Gen_cost == sum(sum(Scale/10^6*gen_cost[(b,i)] for (i,gen) in _PM.nws(pm)[b][:gen]) for b in base_nws) )
		JuMP.@constraint(pm.model, FFRReserves == Scale*sum(FFRReserves_max[b] for b in base_nws) )
		JuMP.@constraint(pm.model, FCRReserves == Scale*sum(FCRReserves_max[b] for b in base_nws) )

		# display(JuMP.@constraint(pm.model, FCRReserves ==  sum(FCR_cost[(c,2)]*100*Scale/10^6*_PM.var(pm, c, :Pgg, 2)  for (b,c,br) in cont_nws) ) )
		display(JuMP.@constraint(pm.model, Cont == sum(_PM.ref(pm, b, :branchdc_ne, br)["fail_prob"]*_PM.var(pm, b, :branchdc_ne, br)*
            	(Scale/10^6*gen_cost[(c,3)] - Scale/10^6*gen_cost[(b,3)]) for (b,c,br) in cont_nws) ) )

      curtailment = Dict()
      capacity = Dict()

      for b in base_nws
          for c in curt_gen
            curtailment[(b,c)] = JuMP.upper_bound(_PM.var(pm, b, :pg, c)) - _PM.var(pm, b, :pg, c)
            capacity[(b,c)] = JuMP.upper_bound(_PM.var(pm, b, :pg, c))
         end
     end
     for y = 1:total_year
            JuMP.@constraint(pm.model, Curt[y] == sum(sum(curtailment[(b,c)] for c in curt_gen) for b in year_base[y]) / sum(sum(capacity[(b,c)] for c in curt_gen) for b in year_base[y])  )
            JuMP.@constraint(pm.model, Curt[y] <= max_curt)
      end
        return  JuMP.@objective(pm.model, Min, Inv_cost + Gen_cost + FFRReserves + FCRReserves + Cont - Gen_cost)
end

function objective_min_cost_TNEP_FSNS(pm::_PM.AbstractPowerModel)
	display("objective_min_cost_TNEP_FSNS")
	base_nws = pm.setting["base_list"]
	cont_nws = pm.setting["Cont_list"]
	Total_sample = pm.setting["Total_sample"]
	curt_gen = pm.setting["curtailed_gen"]
	max_curt = pm.setting["max_curt"]
	year_base = pm.setting["year_base"]
	total_year = pm.setting["total_yr"]

	gen_cost = Dict()
	FFR_cost = Dict()
	FCR_cost = Dict()
	fail_prob = Dict()
        Inv_cost = _PM.var(pm)[:Inv_cost] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        Gen_cost = _PM.var(pm)[:Gen_cost] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        FFRReserves = _PM.var(pm)[:FFR_Reserves] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        FCRReserves = _PM.var(pm)[:FCR_Reserves] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        Curt = _PM.var(pm)[:curt] = JuMP.@variable(pm.model, start = 0)
        Cont = _PM.var(pm)[:Cont]     = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        weights = pm.setting["weights"]

        sol_component_value_mod_wonw(pm,   :Inv_cost, Inv_cost)
        sol_component_value_mod_wonw(pm,   :Gen_cost, Gen_cost)
		sol_component_value_mod_wonw(pm,   :FFR_Reserves, FFRReserves)
        sol_component_value_mod_wonw(pm,   :FCR_Reserves, FCRReserves)
        sol_component_value_mod_wonw(pm,   :Cont, Cont)
		sol_component_value_mod_wonw(pm,   :Curt, Curt)

        for (n, nw_ref) in _PM.nws(pm)
            for (r, reserves) in nw_ref[:reserves]
                FFR_cost[(n,r)] = reserves["Cf"]
                FCR_cost[(n,r)] = reserves["Cg"]
            end

             for (i,gen) in nw_ref[:gen]
                pg = _PM.var(pm, n, :pg, i)

                if length(gen["cost"]) == 1
                    gen_cost[(n,i)] = gen["cost"][1]
                elseif length(gen["cost"]) == 2
                    gen_cost[(n,i)] = gen["cost"][1]*pg + gen["cost"][2]
                elseif length(gen["cost"]) == 3
                    gen_cost[(n,i)] = gen["cost"][2]*pg + gen["cost"][3]
                else
                    gen_cost[(n,i)] = 0.0
                end
            end
        end

        FFR_list =  Dict()
        FCR_list = Dict()
        for (b,c,br) in cont_nws
            FFR_list[(b,c)] = FFR_cost[(c,2)]*100/10^6*_PM.var(pm, c, :Pff, 2)+
                              FCR_cost[(c,2)]*100/10^6*_PM.var(pm, c, :Pgg, 2)
            # a_ub, a_lb = _IM.variable_domain(_PM.var(pm, c, :Pff, 2))
            # display("a_ub:$a_ub a_lb:$a_lb")
        end
        Zff = _PM.var(pm)[:Zff] = Dict((b, c) => JuMP.@variable(pm.model, base_name = "zff[$(string(b)),$(string(c))]", binary = true, start = 0) for (b, c, br) in cont_nws)
        sol_component_value_mod_wonw(pm, :Zff, Zff)
        # _IM.sol_component_value(pm, nw, :reserves, :Zff, base_nws, Zff)
        FFRReserves_max = _PM.var(pm)[:FFRReserves_max] = JuMP.@variable(pm.model, [b in base_nws], base_name ="FFRReserves_max", start = 0)
        FCRReserves_max = _PM.var(pm)[:FCRReserves_max] = JuMP.@variable(pm.model, [b in base_nws], base_name ="FCRReserves_max", start = 0)
        for b in base_nws
            sol_component_value_mod(pm, b, :FFRReserves_max, FFRReserves_max[b])
            sol_component_value_mod(pm, b, :FCRReserves_max, FCRReserves_max[b])
        end
        base_cont= Dict()
        for b in base_nws
            # [item for item in a if item[2] == 4]
            base_cont = [tt for tt in cont_nws if tt[1] == b]

            ####_PM.var(pm)[:Zff] = JuMP.@variable(pm.model, [(z,w) in base_cont], base_name="zff", binary = true, start = 0 ) #just one variable, how to create multiple with an index
            for (z,w) in base_cont
            JuMP.@constraint(pm.model,  FFRReserves_max[b] >= FFR_list[(z,w)] )
            JuMP.@constraint(pm.model,  FFRReserves_max[b] <= FFR_list[(z,w)] + 100*(1-Zff[(z,w)]))
            end
            JuMP.@constraint(pm.model,  sum(Zff[(z,w)] for (z, w) in base_cont) == 1 )
        end

        Scale = 8760*10  # 5 for no. of gap years between two time steps

            # for (i,branch) in _PM.nws(pm)[1][:branchdc_ne]
            #     display(branch["cost"])
            #  end
            #
            #  for (i,conv) in _PM.nws(pm)[1][:convdc_ne]
            #      display(conv["cost"])
            #   end
            # display(gen_cost)

            JuMP.@constraint(pm.model, Inv_cost == sum(sum(conv["cost"]*_PM.var(pm, 1, :conv_ne, i) for (i,conv) in _PM.nws(pm)[1][:convdc_ne]) for (n, nw_ref) in _PM.nws(pm)) +
                       sum(sum(branch["cost"]*_PM.var(pm, 1, :branchdc_ne, i) for (i,branch) in _PM.nws(pm)[1][:branchdc_ne]) for (n, nw_ref) in _PM.nws(pm)) )
			JuMP.@constraint(pm.model, Gen_cost == sum(weights[b]*sum(Scale/10^6*gen_cost[(b,i)] for (i,gen) in _PM.nws(pm)[b][:gen]) for b in base_nws) )
			# display(JuMP.@constraint(pm.model, FFRReserves == sum(FFR_cost[(c,2)]*100*Scale/10^6* _PM.var(pm, c, :Pff, 2) for (b,c,br) in cont_nws) ))
            JuMP.@constraint(pm.model, FFRReserves == Scale*sum(weights[b]*FFRReserves_max[b] for b in base_nws) )
            JuMP.@constraint(pm.model, FCRReserves == Scale*sum(weights[b]*FCRReserves_max[b] for b in base_nws) )

			# display(JuMP.@constraint(pm.model, FCRReserves ==  sum(FCR_cost[(c,2)]*100*Scale/10^6*_PM.var(pm, c, :Pgg, 2)  for (b,c,br) in cont_nws) ) )
			# print(JuMP.@constraint(pm.model, Cont == sum( sum( (Scale/10^6) *gen_cost[(c,i)]*_PM.ref(pm, b, :branchdc_ne, br)["fail_prob"]*_PM.var(pm, 1, :branchdc_ne, br)
            #         for i in  curt_gen)
            #         for (b,c,br) in cont_nws) ) )

            # display(JuMP.@constraint(pm.model, Cont == sum(weights[b]*_PM.ref(pm, b, :branchdc_ne, br)["fail_prob"]*_PM.var(pm, b, :branchdc_ne, br)*
            # (Scale/10^6*gen_cost[(c,3)] - Scale/10^6*gen_cost[(b,3)]) for (b,c,br) in cont_nws) ) )
 			curtailment = Dict()
			capacity = Dict()
			#
			#  for b in base_nws
			# 	  for c in curt_gen
			# 		curtailment[(b,c)] = JuMP.upper_bound(_PM.var(pm, b, :pg, c)) - _PM.var(pm, b, :pg, c)
            #         # curtailment[(b,c)] = _PM.var(pm, b, :pg, c)
			# 		capacity[(b,c)] = JuMP.upper_bound(_PM.var(pm, b, :pg, c))
			# 	 end
			#  end
			 curtailment = Dict()
	         capacity = Dict()

	         for b in base_nws
	             for c in curt_gen
	               curtailment[(b,c)] = JuMP.upper_bound(_PM.var(pm, b, :pg, c)) - _PM.var(pm, b, :pg, c)
	               capacity[(b,c)] = JuMP.upper_bound(_PM.var(pm, b, :pg, c))
	            end
	        end
	        for y = 1:total_year
	               JuMP.@constraint(pm.model, Curt[y] == sum(sum(curtailment[(b,c)] for c in curt_gen) for b in year_base[y]) / sum(sum(capacity[(b,c)] for c in curt_gen) for b in year_base[y])  )
	               # JuMP.@constraint(pm.model, Curt[y] <= max_curt)
	        end
            return JuMP.@objective(pm.model, Min, Inv_cost + FFRReserves + FCRReserves + Gen_cost)
end

function objective_min_cost_TNEP_PL_nocl(pm::_PM.AbstractPowerModel)
	base_nws = pm.setting["base_list"]
	cont_nws = pm.setting["Cont_list"]
	Total_sample = pm.setting["Total_sample"]
	curt_gen = pm.setting["curtailed_gen"]
	max_curt = pm.setting["max_curt"]
	year_base = pm.setting["year_base"]
	total_year = pm.setting["total_yr"]
	weights = pm.setting["weights"]

	gen_cost = Dict()
	FFR_cost = Dict()
	FCR_cost = Dict()
	fail_prob = Dict()
	Inv_cost = _PM.var(pm)[:Inv_cost] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
	Gen_cost = _PM.var(pm)[:Gen_cost] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
	FFRReserves = _PM.var(pm)[:FFR_Reserves] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
	FCRReserves = _PM.var(pm)[:FCR_Reserves] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
	Cont = _PM.var(pm)[:Cont]     = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
	Curt = _PM.var(pm)[:curt] = JuMP.@variable(pm.model, [i in 1:total_year], start = 0, lower_bound = 0)

	sol_component_value_mod_wonw(pm,   :Inv_cost, Inv_cost)
	sol_component_value_mod_wonw(pm,   :Gen_cost, Gen_cost)
	sol_component_value_mod_wonw(pm,   :Curt, Curt)
	sol_component_value_mod_wonw(pm,   :FFR_Reserves, FFRReserves)
	sol_component_value_mod_wonw(pm,   :FCR_Reserves, FCRReserves)
	sol_component_value_mod_wonw(pm,   :Cont, Cont)

	for (n, nw_ref) in _PM.nws(pm)
		for (r, reserves) in nw_ref[:reserves]
			FFR_cost[(n,r)] = reserves["Cf"]
			FCR_cost[(n,r)] = reserves["Cg"]
		end

		 for (i,gen) in nw_ref[:gen]
			pg = _PM.var(pm, n, :pg, i)

			if length(gen["cost"]) == 1
				gen_cost[(n,i)] = gen["cost"][1]
			elseif length(gen["cost"]) == 2
				gen_cost[(n,i)] = gen["cost"][1]*pg + gen["cost"][2]
			elseif length(gen["cost"]) == 3
				gen_cost[(n,i)] = gen["cost"][2]*pg + gen["cost"][3]
			else
				gen_cost[(n,i)] = 0.0
			end
		end
	end

	Scale = 8760/Total_sample*5 # 5 for no. of gap years between two time steps

	JuMP.@constraint(pm.model, Inv_cost == sum(sum(conv["cost"]*_PM.var(pm, 1, :conv_ne, i) for (i,conv) in _PM.nws(pm)[1][:convdc_ne]) for (n, nw_ref) in _PM.nws(pm)) +
			   sum(sum(branch["cost"]*_PM.var(pm, 1, :branchdc_ne, i) for (i,branch) in _PM.nws(pm)[1][:branchdc_ne]) for (n, nw_ref) in _PM.nws(pm)) )
	JuMP.@constraint(pm.model, Gen_cost == sum(sum(Scale/10^6*gen_cost[(b,i)] for (i,gen) in _PM.nws(pm)[b][:gen]) for b in base_nws) )
	JuMP.@constraint(pm.model, FFRReserves == sum(FFR_cost[(c,2)]*100*Scale/10^6* _PM.var(pm, c, :Pff, 2) for (b,c,br) in cont_nws) )
	JuMP.@constraint(pm.model, FCRReserves ==  sum(FCR_cost[(c,2)]*100*Scale/10^6*_PM.var(pm, c, :Pgg, 2)  for (b,c,br) in cont_nws) )
	JuMP.@constraint(pm.model, Cont == sum(_PM.ref(pm, b, :branchdc_ne, br)["fail_prob"]*_PM.var(pm, b, :branchdc_ne, br)*
	(Scale/10^6*gen_cost[(c,3)] - Scale/10^6*gen_cost[(b,3)]) for (b,c,br) in cont_nws) )
	curtailment = Dict()
	capacity = Dict()

	  for b in base_nws
	      for c in curt_gen
	        curtailment[(b,c)] = JuMP.upper_bound(_PM.var(pm, b, :pg, c)) - _PM.var(pm, b, :pg, c)
	        capacity[(b,c)] = JuMP.upper_bound(_PM.var(pm, b, :pg, c))
	     end
	 end
	 for y = 1:total_year
	        JuMP.@constraint(pm.model, Curt[y] == sum(sum(curtailment[(b,c)] for c in curt_gen) for b in year_base[y]) / sum(sum(capacity[(b,c)] for c in curt_gen) for b in year_base[y])  )
	        # JuMP.@constraint(pm.model, Curt[y] <= max_curt)
	  end

	return display( JuMP.@objective(pm.model, Min, Inv_cost + Gen_cost + FFRReserves + FCRReserves + Cont  ) )
end

function objective_min_cost_TNEP_PL(pm::_PM.AbstractPowerModel)
	base_nws = pm.setting["base_list"]
	cont_nws = pm.setting["Cont_list"]
	Total_sample = pm.setting["Total_sample"]
	curt_gen = pm.setting["curtailed_gen"]
	max_curt = pm.setting["max_curt"]
	year_base = pm.setting["year_base"]
	total_year = pm.setting["total_yr"]

	gen_cost = Dict()
	FFR_cost = Dict()
	FCR_cost = Dict()
	fail_prob = Dict()
        Inv_cost = _PM.var(pm)[:Inv_cost] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        Gen_cost = _PM.var(pm)[:Gen_cost] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        FFRReserves = _PM.var(pm)[:FFR_Reserves] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        FCRReserves = _PM.var(pm)[:FCR_Reserves] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        Curt = _PM.var(pm)[:curt] = JuMP.@variable(pm.model, start = 0)
        Cont = _PM.var(pm)[:Cont]     = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        weights = pm.setting["weights"]

        sol_component_value_mod_wonw(pm,   :Inv_cost, Inv_cost)
        sol_component_value_mod_wonw(pm,   :Gen_cost, Gen_cost)
		sol_component_value_mod_wonw(pm,   :FFR_Reserves, FFRReserves)
        sol_component_value_mod_wonw(pm,   :FCR_Reserves, FCRReserves)
        sol_component_value_mod_wonw(pm,   :Cont, Cont)
		sol_component_value_mod_wonw(pm,   :Curt, Curt)

        for (n, nw_ref) in _PM.nws(pm)
            for (r, reserves) in nw_ref[:reserves]
                FFR_cost[(n,r)] = reserves["Cf"]
                FCR_cost[(n,r)] = reserves["Cg"]
            end

             for (i,gen) in nw_ref[:gen]
                pg = _PM.var(pm, n, :pg, i)

                if length(gen["cost"]) == 1
                    gen_cost[(n,i)] = gen["cost"][1]
                elseif length(gen["cost"]) == 2
                    gen_cost[(n,i)] = gen["cost"][1]*pg + gen["cost"][2]
                elseif length(gen["cost"]) == 3
                    gen_cost[(n,i)] = gen["cost"][2]*pg + gen["cost"][3]
                else
                    gen_cost[(n,i)] = 0.0
                end
            end
        end

        FFR_list =  Dict()
        FCR_list = Dict()
        for (b,c,br) in cont_nws
            FFR_list[(b,c)] = FFR_cost[(c,2)]*100/10^6*_PM.var(pm, c, :Pff, 2)+
                              FCR_cost[(c,2)]*100/10^6*_PM.var(pm, c, :Pgg, 2)
            # a_ub, a_lb = _IM.variable_domain(_PM.var(pm, c, :Pff, 2))
            # display("a_ub:$a_ub a_lb:$a_lb")
        end
        Zff = _PM.var(pm)[:Zff] = Dict((b, c) => JuMP.@variable(pm.model, base_name = "zff[$(string(b)),$(string(c))]", binary = true, start = 0) for (b, c, br) in cont_nws)
        sol_component_value_mod_wonw(pm, :Zff, Zff)
        # _IM.sol_component_value(pm, nw, :reserves, :Zff, base_nws, Zff)
        FFRReserves_max = _PM.var(pm)[:FFRReserves_max] = JuMP.@variable(pm.model, [b in base_nws], base_name ="FFRReserves_max", start = 0)
        FCRReserves_max = _PM.var(pm)[:FCRReserves_max] = JuMP.@variable(pm.model, [b in base_nws], base_name ="FCRReserves_max", start = 0)
        for b in base_nws
            sol_component_value_mod(pm, b, :FFRReserves_max, FFRReserves_max[b])
            sol_component_value_mod(pm, b, :FCRReserves_max, FCRReserves_max[b])
        end
        base_cont= Dict()
        for b in base_nws
            # [item for item in a if item[2] == 4]
            base_cont = [tt for tt in cont_nws if tt[1] == b]

            ####_PM.var(pm)[:Zff] = JuMP.@variable(pm.model, [(z,w) in base_cont], base_name="zff", binary = true, start = 0 ) #just one variable, how to create multiple with an index
            for (z,w) in base_cont
            JuMP.@constraint(pm.model,  FFRReserves_max[b] >= FFR_list[(z,w)] )
            JuMP.@constraint(pm.model,  FFRReserves_max[b] <= FFR_list[(z,w)] + 100*(1-Zff[(z,w)]))
            end
            JuMP.@constraint(pm.model,  sum(Zff[(z,w)] for (z, w) in base_cont) == 1 )
        end

        Scale = 8760*10  # 5 for no. of gap years between two time steps

            # for (i,branch) in _PM.nws(pm)[1][:branchdc_ne]
            #     display(branch["cost"])
            #  end
            #
            #  for (i,conv) in _PM.nws(pm)[1][:convdc_ne]
            #      display(conv["cost"])
            #   end
            # display(gen_cost)

            JuMP.@constraint(pm.model, Inv_cost == sum(sum(conv["cost"]*_PM.var(pm, 1, :conv_ne, i) for (i,conv) in _PM.nws(pm)[1][:convdc_ne]) for (n, nw_ref) in _PM.nws(pm)) +
                       sum(sum(branch["cost"]*_PM.var(pm, 1, :branchdc_ne, i) for (i,branch) in _PM.nws(pm)[1][:branchdc_ne]) for (n, nw_ref) in _PM.nws(pm)) )
			JuMP.@constraint(pm.model, Gen_cost == sum(weights[b]*sum(Scale/10^6*gen_cost[(b,i)] for (i,gen) in _PM.nws(pm)[b][:gen]) for b in base_nws) )
			# display(JuMP.@constraint(pm.model, FFRReserves == sum(FFR_cost[(c,2)]*100*Scale/10^6* _PM.var(pm, c, :Pff, 2) for (b,c,br) in cont_nws) ))
            JuMP.@constraint(pm.model, FFRReserves == Scale*sum(weights[b]*FFRReserves_max[b] for b in base_nws) )
            JuMP.@constraint(pm.model, FCRReserves == Scale*sum(weights[b]*FCRReserves_max[b] for b in base_nws) )

			# display(JuMP.@constraint(pm.model, FCRReserves ==  sum(FCR_cost[(c,2)]*100*Scale/10^6*_PM.var(pm, c, :Pgg, 2)  for (b,c,br) in cont_nws) ) )
			# print(JuMP.@constraint(pm.model, Cont == sum( sum( (Scale/10^6) *gen_cost[(c,i)]*_PM.ref(pm, b, :branchdc_ne, br)["fail_prob"]*_PM.var(pm, 1, :branchdc_ne, br)
            #         for i in  curt_gen)
            #         for (b,c,br) in cont_nws) ) )

            # display(JuMP.@constraint(pm.model, Cont == sum(weights[b]*_PM.ref(pm, b, :branchdc_ne, br)["fail_prob"]*_PM.var(pm, b, :branchdc_ne, br)*
            # (Scale/10^6*gen_cost[(c,3)] - Scale/10^6*gen_cost[(b,3)]) for (b,c,br) in cont_nws) ) )``
 			curtailment = Dict()
			capacity = Dict()
			#
			#  for b in base_nws
			# 	  for c in curt_gen
			# 		curtailment[(b,c)] = JuMP.upper_bound(_PM.var(pm, b, :pg, c)) - _PM.var(pm, b, :pg, c)
            #         # curtailment[(b,c)] = _PM.var(pm, b, :pg, c)
			# 		capacity[(b,c)] = JuMP.upper_bound(_PM.var(pm, b, :pg, c))
			# 	 end
			#  end
			 curtailment = Dict()
	         capacity = Dict()

	         for b in base_nws
	             for c in curt_gen
	               curtailment[(b,c)] = JuMP.upper_bound(_PM.var(pm, b, :pg, c)) - _PM.var(pm, b, :pg, c)
	               capacity[(b,c)] = JuMP.upper_bound(_PM.var(pm, b, :pg, c))
	            end
	        end
	        for y = 1:total_year
	               JuMP.@constraint(pm.model, Curt[y] == sum(sum(curtailment[(b,c)] for c in curt_gen) for b in year_base[y]) / sum(sum(capacity[(b,c)] for c in curt_gen) for b in year_base[y])  )
	               # JuMP.@constraint(pm.model, Curt[y] <= max_curt)
	        end

	return display( JuMP.@objective(pm.model, Min, Inv_cost + Gen_cost + FFRReserves + FCRReserves) )
end

function objective_min_cost_TNEP_FSNS_rev1(pm::_PM.AbstractPowerModel)
	base_nws = pm.setting["base_list"]
	cont_nws = pm.setting["Cont_list"]
	Total_sample = pm.setting["Total_sample"]
	curt_gen = pm.setting["curtailed_gen"]
	max_curt = pm.setting["max_curt"]
	year_base = pm.setting["year_base"]
	total_year = pm.setting["total_yr"]

	gen_cost = Dict()
	FFR_cost = Dict()
	FCR_cost = Dict()
	fail_prob = Dict()
        Inv_cost = _PM.var(pm)[:Inv_cost] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        Gen_cost = _PM.var(pm)[:Gen_cost] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        FFRReserves = _PM.var(pm)[:FFR_Reserves] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        FCRReserves = _PM.var(pm)[:FCR_Reserves] = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        Curt = _PM.var(pm)[:curt] = JuMP.@variable(pm.model, start = 0)
        Cont = _PM.var(pm)[:Cont]     = JuMP.@variable(pm.model, start = 0, lower_bound = 0)
        weights = pm.setting["weights"]

        sol_component_value_mod_wonw(pm,   :Inv_cost, Inv_cost)
        sol_component_value_mod_wonw(pm,   :Gen_cost, Gen_cost)
		sol_component_value_mod_wonw(pm,   :FFR_Reserves, FFRReserves)
        sol_component_value_mod_wonw(pm,   :FCR_Reserves, FCRReserves)
        sol_component_value_mod_wonw(pm,   :Cont, Cont)

        for (n, nw_ref) in _PM.nws(pm)
            for (r, reserves) in nw_ref[:reserves]
                FFR_cost[(n,r)] = reserves["Cf"]
                FCR_cost[(n,r)] = reserves["Cg"]
            end

             for (i,gen) in nw_ref[:gen]
                pg = _PM.var(pm, n, :pg, i)

                if length(gen["cost"]) == 1
                    gen_cost[(n,i)] = gen["cost"][1]
                elseif length(gen["cost"]) == 2
                    gen_cost[(n,i)] = gen["cost"][1]*pg + gen["cost"][2]
                elseif length(gen["cost"]) == 3
                    gen_cost[(n,i)] = gen["cost"][2]*pg + gen["cost"][3]
                else
                    gen_cost[(n,i)] = 0.0
                end
            end
        end

        FFR_list =  Dict()
        FCR_list = Dict()
        for (b,c,br) in cont_nws
            FFR_list[(b,c)] = FFR_cost[(c,2)]*100/10^6*_PM.var(pm, c, :Pff, 2)+
                              FCR_cost[(c,2)]*100/10^6*_PM.var(pm, c, :Pgg, 2)
            # a_ub, a_lb = _IM.variable_domain(_PM.var(pm, c, :Pff, 2))
            # display("a_ub:$a_ub a_lb:$a_lb")
        end
        Zff = _PM.var(pm)[:Zff] = Dict((b, c) => JuMP.@variable(pm.model, base_name = "zff[$(string(b)),$(string(c))]", binary = true, start = 0) for (b, c, br) in cont_nws)
        sol_component_value_mod_wonw(pm, :Zff, Zff)
        # _IM.sol_component_value(pm, nw, :reserves, :Zff, base_nws, Zff)
        FFRReserves_max = _PM.var(pm)[:FFRReserves_max] = JuMP.@variable(pm.model, [b in base_nws], base_name ="FFRReserves_max", start = 0)
        FCRReserves_max = _PM.var(pm)[:FCRReserves_max] = JuMP.@variable(pm.model, [b in base_nws], base_name ="FCRReserves_max", start = 0)
        for b in base_nws
            sol_component_value_mod(pm, b, :FFRReserves_max, FFRReserves_max[b])
            sol_component_value_mod(pm, b, :FCRReserves_max, FCRReserves_max[b])
        end
        base_cont= Dict()
        for b in base_nws
            # [item for item in a if item[2] == 4]
            base_cont = [tt for tt in cont_nws if tt[1] == b]

            ####_PM.var(pm)[:Zff] = JuMP.@variable(pm.model, [(z,w) in base_cont], base_name="zff", binary = true, start = 0 ) #just one variable, how to create multiple with an index
            for (z,w) in base_cont
            JuMP.@constraint(pm.model,  FFRReserves_max[b]  >= FFR_list[(z,w)] )
            JuMP.@constraint(pm.model,  FFRReserves_max[b] <= FFR_list[(z,w)] + 100*(1-Zff[(z,w)]))
            end
            JuMP.@constraint(pm.model,  sum(Zff[(z,w)] for (z, w) in base_cont) == 1 )
        end

        Scale = 8760*10  # 5 for no. of gap years between two time steps

            JuMP.@constraint(pm.model, Inv_cost == sum(sum(conv["cost"]*_PM.var(pm, 1, :conv_ne, i) for (i,conv) in _PM.nws(pm)[1][:convdc_ne]) for (n, nw_ref) in _PM.nws(pm)) +
                       sum(sum(branch["cost"]*_PM.var(pm, 1, :branchdc_ne, i) for (i,branch) in _PM.nws(pm)[1][:branchdc_ne]) for (n, nw_ref) in _PM.nws(pm)) )
			display(JuMP.@constraint(pm.model, Gen_cost == sum(weights[b]*sum(Scale/10^6*gen_cost[(b,i)] for i in curt_gen) for b in base_nws) ) )
			# display(JuMP.@constraint(pm.model, FFRReserves == sum(FFR_cost[(c,2)]*100*Scale/10^6* _PM.var(pm, c, :Pff, 2) for (b,c,br) in cont_nws) ))
            JuMP.@constraint(pm.model, FFRReserves == Scale*sum(weights[b]*FFRReserves_max[b] for b in base_nws) )
            JuMP.@constraint(pm.model, FCRReserves == Scale*sum(weights[b]*FCRReserves_max[b] for b in base_nws) )

            # JuMP.@constraint(pm.model, Cont == sum(weights[b]*_PM.ref(pm, b, :branchdc_ne, br)["fail_prob"]*_PM.var(pm, b, :branchdc_ne, br)*
            # (Scale/10^6*(gen_cost[(c,1)] + gen_cost[(c,2)]) - Scale/10^6*(gen_cost[(b,1)]+gen_cost[(b,2)]) ]) for (b,c,br) in cont_nws) )

			curtailment = Dict()
			capacity = Dict()

			 # for b in base_nws
				#   for c in curt_gen
				# 	curtailment[(b,c)] = JuMP.upper_bound(_PM.var(pm, b, :pg, c)) - _PM.var(pm, b, :pg, c)
             #        # curtailment[(b,c)] = _PM.var(pm, b, :pg, c)
				# 	capacity[(b,c)] = JuMP.upper_bound(_PM.var(pm, b, :pg, c))
				#  end
			 # end
			 # curtailment = Dict()
	         # capacity = Dict()

	        #  for b in base_nws
	        #      for c in curt_gen
	        #        curtailment[(b,c)] = JuMP.upper_bound(_PM.var(pm, b, :pg, c)) - _PM.var(pm, b, :pg, c)
	        #        capacity[(b,c)] = JuMP.upper_bound(_PM.var(pm, b, :pg, c))
	        #     end
	        # end
	        # for y = 1:total_year
	        #        JuMP.@constraint(pm.model, Curt[y] == sum(sum(curtailment[(b,c)] for c in curt_gen) for b in year_base[y]) / sum(sum(capacity[(b,c)] for c in curt_gen) for b in year_base[y])  )
	        #        JuMP.@constraint(pm.model, Curt[y] <= max_curt)
	        #  end
			 display("objective_min_cost_TNEP_FSNS_rev1")
            return JuMP.@objective(pm.model, Min,  FFRReserves + FCRReserves + Inv_cost  - Gen_cost)
end


using Test
using PyCall

using Distributed

addprocs(2)
@everywhere using PotentialCalculation
@everywhere using PotentialCalculation.psi4

fname = tempname()
rname = tempname()
sname = tempname()
xyzname = tempname()

formic_acid=Cluster(
 [-6.7041359778      1.3501192944      0.0102209137
-5.3688853815      1.2229556023      0.0440598937
-7.2470157373      2.4374213225      0.0651311769
 -5.0398812618      2.1435406993      0.1155201154
 -7.2001330967      0.3718768293     -0.0703451879]',
  AtomOnlySymbol.(["C", "O", "O", "H", "H"]) )

Ar = Cluster(rand(3), AtomOnlySymbol.(["Ar"]))
N2 = Cluster([[0.0 1.0]; [0.0 0.0]; [0.0 0.0]], AtomOnlySymbol.(["N", "N"]))

open(xyzname,"w") do io
    print_xyz(io, formic_acid)
end

pbar=true

testrestarts = false

if Sys.which("orca") != nothing
    @info "Orca binary found. Testing ORCA."
    @testset "Orca" begin
        ca = Calculator("blyp d3bj TIGHTSCF", "def2-svp", Orca())

        input1=create_inputs(xyzname, Ar, ca)
        inputs=create_inputs(xyzname, N2, ca; npoints=5)
        inputss=create_inputs(xyzname, xyzname, ca)

        data1=calculate_potential(inputs, save_file=fname, pbar=pbar)
        data2=calculate_potential(fname,ca,save_file=sname, restart_file=rname, pbar=pbar)
        data3=continue_calculation(rname, Orca(), save_file=sname, restart_file=rname, pbar=pbar)

        @test all(isapprox.(data1["Energy"], data2["Energy"], atol=2E-6))
        @test all(isapprox.(data1["Energy"], data3["Energy"], atol=2E-6))
        @test all(isapprox.(data2["Energy"], data3["Energy"], atol=2E-6))
    end
    testrestarts = true
else
    @warn "Orca binary not found from PATH. Skipping tests. ORCA backend might not work!"
end

testpsi4 = true

try
    pyimport("psi4")
catch
    global testpsi4 = false
    @warn "Psi4 was not detected. Skipping testing. Psi4 backend is not working!"
end
if  testpsi4
    @info "Psi4 found. Testing Psi4."
    @testset "Psi4" begin
        ca = Calculator("blyp-d3bj", "def2-svp",Psi4(memory="1000MiB", nthreads=2))

        input1=create_inputs(xyzname, Ar, ca)
        inputs=create_inputs(xyzname, N2, ca; npoints=5)
        inputss=create_inputs(xyzname, xyzname, ca)

        data1=calculate_potential(inputs, save_file=fname, pbar=pbar)
        data2=calculate_potential(fname,ca,save_file=sname, restart_file=rname, pbar=pbar)
        data3=continue_calculation(rname,ca, save_file=sname, restart_file=rname, pbar=pbar)

        calculate_energy(ca, N2)
        calculate_energy(ca, [N2,N2])
        @test all(isapprox.(data1["Energy"], data2["Energy"], atol=2E-6))
        @test all(isapprox.(data1["Energy"], data3["Energy"], atol=2E-6))
        @test all(isapprox.(data2["Energy"], data3["Energy"], atol=2E-6))
    end
    testrestarts = true
end

if testrestarts
    @testset "restarttools" begin
        savedata = load_data_file(sname)

        ldata=load_jld_data(sname)
        for fn in [fname, sname]
            ldata=load_jld_data(fn)
            @test length(ldata["cluster1"]) + length(ldata["cluster2"]) == length(ldata["Points"][1])
        end
    end
    rm(fname)
    rm(rname)
    rm(sname)
    rm(xyzname)
else
    @warn "Restarting calculations was not tested!"
end


println("Dj) Test of the MultiPhotonDeExcitation module with ASF from an internally generated initial- and final-state multiplet.")
#
setDefaults("print summary: open", "zzz-MultiPhotonDeExcitation.sum")
setDefaults("method: continuum, Galerkin")           ## setDefaults("method: continuum, Galerkin")  "method: continuum, asymptotic Coulomb"
                                                     ## setDefaults("method: normalization, Ong-Russek")
setDefaults("method: normalization, pure sine")      ## setDefaults("method: normalization, pure Coulomb")    setDefaults("method: normalization, pure sine")
setDefaults("unit: rate", "a.u.")

## grid = Radial.Grid(Radial.Grid(false), rnt = 4.0e-6, h = 5.0e-2, hp = 1.0e-2, rbox = 10.0)
grid = Radial.Grid(Radial.Grid(true), rnt = 4.0e-6, h = 5.0e-2, rbox = 10.0)
name = "two-photon absorption or emission of lithium-like xenon"

if  false
    # Green function for two-photon absorption or emission of lithium-like xenon
    refConfigs       = [Configuration("[He] 2p")]
    levelSymmetries  = [LevelSymmetry(1//2, Basics.minus), LevelSymmetry(3//2, Basics.minus),
                        LevelSymmetry(1//2, Basics.plus),  LevelSymmetry(3//2, Basics.plus)]
    greenSettings    = GreenSettings(3, [0, 1], 0.01, true, LevelSelection())

    greenRep         = Representation(name, Nuclear.Model(54.), grid, refConfigs,
                                      GreenExpansion( AtomicState.DampedSpaceCI(), Basics.DeExciteSingleElectron(), levelSymmetries, 3, greenSettings) )
                                   ## GreenExpansion( AtomicState.CoreSpaceCI(), Basics.DeExciteSingleElectron(),
                                   ## GreenExpansion( AtomicState.DampedSpaceCI(), Basics.DeExciteSingleElectron()
    greenOut         = generate(greenRep, output=true)
    mpGreen          = greenOut["Green channels"]

elseif  false
    #
    println("Calculate two-photon absorption cross sections by monochromatic and equally-polarized photons from the same beam")
    mpSettings       = MultiPhotonDeExcitation.Settings(MultiPhotonDeExcitation.TwoPhotonAbsorptionMonochromatic(),
                                                        [E1,M1], [UseCoulomb], mpGreen, 0, true, LineSelection())
    wa = Atomic.Computation(Atomic.Computation(), name=name, grid=grid, nuclearModel=Nuclear.Model(54.),
                            initialConfigs= [Configuration("1s^2 2s")],
                            finalConfigs  = [Configuration("1s^2 3s")],
                            process = MultiPhotonDE(), processSettings= mpSettings  )
    wb = perform(wa)

elseif  true
    #
    println("Calculate two-photon emission cross sections: energy-differential + total cross sections")
    mpSettings       = MultiPhotonDeExcitation.Settings(MultiPhotonDeExcitation.TwoPhotonEmission(),
                                                        [E1,M1], [UseCoulomb], mpGreen, 4, true, LineSelection())

    wc = Atomic.Computation(Atomic.Computation(), name=name, grid=grid, nuclearModel=Nuclear.Model(54.),
                            initialConfigs= [Configuration("1s^2 3s")],
                            finalConfigs  = [Configuration("1s^2 2s")],
                            process = MultiPhotonDE(), processSettings= mpSettings    )
    wd = perform(wc)

end

setDefaults("print summary: close", "")


using VDPTag
using Plots
using POMDPToolbox
using Reel
using ProgressMeter
using PmapProgressMeter
using ParticleFilters
using ContinuousPOMDPTreeSearchExperiments
using Plots

@show N = 2

@everywhere begin
    using POMDPs
    using POMDPToolbox
    using ContinuousPOMDPTreeSearchExperiments
    using ParticleFilters
    using POMCPOW
    using BasicPOMCP
    using VDPTag

    pomdp = VDPTagPOMDP()

    n = 10000
    seed = 42

    planners = Dict{String, Policy}(

        "pomcpow" => begin
            ro = ToNextML(mdp(pomdp))
            solver = POMCPOWSolver(tree_queries=n,
                                   criterion=MaxUCB(40.0),
                                   final_criterion=MaxTries(),
                                   max_depth=20,
                                   k_action=8.0,
                                   alpha_action=1/20,
                                   k_observation=4.0,
                                   alpha_observation=1/20,
                                   estimate_value=FORollout(ro),
                                   check_repeat_act=false,
                                   check_repeat_obs=false,
                                   rng=MersenneTwister(13)
                                  )
            solve(solver, pomdp)
        end,

        # "discrete_pomcpow" => begin
        #     dpomdp = AODiscreteVDPTagPOMDP(n_angles=1000, n_obs_angles=1000)
        #     ro = translate_policy(ToNextML(mdp(pomdp)), mdp(pomdp), dpomdp, dpomdp)
        #     solver = POMCPOWSolver(tree_queries=10_000,
        #                            criterion=MaxUCB(40.0),
        #                            final_criterion=MaxTries(),
        #                            max_depth=20,
        #                            k_action=8.0,
        #                            alpha_action=1/20,
        #                            k_observation=4.0,
        #                            alpha_observation=1/20,
        #                            estimate_value=FORollout(ro),
        #                            check_repeat_act=false,
        #                            check_repeat_obs=false,
        #                            rng=MersenneTwister(13)
        #                           )
        #     translate_policy(solve(solver, dpomdp), dpomdp, pomdp, dpomdp)
        # end,

        "discrete_pomcp" => begin
            dpomdp = AODiscreteVDPTagPOMDP()
            ro = translate_policy(ToNextML(mdp(pomdp)), mdp(pomdp), dpomdp, dpomdp)
            solver = POMCPSolver(tree_queries=n,
                                   c=40.0,
                                   max_depth=20,
                                   estimate_value=FORollout(ro),
                                   rng=MersenneTwister(13)
                                  )
            dpolicy = solve(solver, dpomdp)
            translate_policy(dpolicy, dpomdp, pomdp, dpomdp)
        end,
    )
end

for (k,p) in planners
    prog = Progress(N, desc="Simulating...")
    # rewards = pmap(prog, 1:N) do i
    rewards = map(1:N) do i
        hr = HistoryRecorder(max_steps=100, rng=MersenneTwister(i))
        up_rng = MersenneTwister(i+100_000)
        up = ObsAdaptiveParticleFilter(deepcopy(pomdp), LowVarianceResampler(10_000), 0.05, up_rng)
        hist = simulate(hr, pomdp, p, up)
        discounted_reward(hist)
    end
    @show k
    @show mean(rewards)
end


# These functions are for testing purposes only
# They need to be tested and abstractized

# remember that the psoils are corrected by surface tension, psoil = p_25 * f_st
#=
function test_soil_from_psoil(
            node::SPACMono{FT},
            psoils::Array{FT,1},
            Δt::FT=FT(10)
) where {FT<:AbstractFloat}
    # unpack the values
    @unpack n_canopy = node;
    n_sl = node.canopy_rt.nAzi * node.canopy_rt.nIncl;

    # update the soil water contents and potential in each layer
    for i_root in 1:node.n_root
        node.swc[i_root] = max(node.mswc[i_root], soil_swc(node.plant_hs.roots[i_root].sh, psoils[i_root]));
        node.plant_hs.roots[i_root].p_ups = psoils[i_root];
    end

    e_sum = FT(0);

    # loop through the canopy layers
    for i_can in 1:node.n_canopy
        rt_layer = node.n_canopy + 1 - i_can;
        f_view = (node.can_opt.Ps[rt_layer] + node.can_opt.Ps[rt_layer+1]) / 2;

        e_i_can = FT(0);
        for i_leaf in 1:(n_sl+1)
            e_i_can += node.plant_ps[i_can].g_lw[i_leaf] *
                       (node.plant_ps[i_can].p_sat - node.envirs[i_can].p_H₂O) /
                       node.envirs[i_can].p_atm *
                       node.plant_ps[i_can].LAIx[i_leaf] *
                       node.plant_ps[i_can].LA;
        end

        # calculate the photosynthetic rates
        gas_exchange!(node.photo_set, node.plant_ps[i_can], node.envirs[i_can], GswDrive());
        gsw_control!(node.photo_set, node.plant_ps[i_can], node.envirs[i_can]);

        # use the ball-berry model here for now as the ∂A/∂E and ∂A/∂Θ functions are not yet ready
        gsw_ss = stomatal_conductance(node.stomata_model, node.plant_ps[i_can], node.envirs[i_can], FT(1));

        # assume τ = 10 minutes
        for i_leaf in 1:(n_sl+1)
            node.plant_ps[i_can].g_sw[i_leaf] += (gsw_ss[i_leaf] - node.plant_ps[i_can].g_sw[i_leaf]) / 600 * Δt;
        end

        e_sum += e_i_can;
    end

    # update root flow rates
    roots_flow!(node.plant_hs.roots,
                node.plant_hs.container_k,
                node.plant_hs.container_p,
                node.plant_hs.container_q,
                e_sum);

    return nothing
end

function test_soil_from_swc(
            node::SPACMono{FT},
            swcs::Array{FT,1},
            Δt::FT=FT(10)
) where {FT<:AbstractFloat}
    # unpack the values
    @unpack n_canopy = node;
    n_sl = node.canopy_rt.nAzi * node.canopy_rt.nIncl;

    # update the soil water contents and potential in each layer
    for i_root in 1:node.n_root
        node.swc[i_root] = max(node.mswc[i_root], swcs[i_root]);
        node.p_soil[i_root] = soil_p_25_swc(node.plant_hs.roots[i_root].sh,
                                            node.swc[i_root]) *
                              node.plant_hs.roots[i_root].f_st;
        # pass the pressure to root_hs
        node.plant_hs.roots[i_root].p_ups = node.p_soil[i_root];
    end

    e_sum = FT(0);

    # loop through the canopy layers
    for i_can in 1:node.n_canopy
        rt_layer = node.n_canopy + 1 - i_can;
        f_view = (node.can_opt.Ps[rt_layer] + node.can_opt.Ps[rt_layer+1]) / 2;

        e_i_can = FT(0);
        for i_leaf in 1:(n_sl+1)
            e_i_can += node.plant_ps[i_can].g_lw[i_leaf] *
                       (node.plant_ps[i_can].p_sat - node.envirs[i_can].p_H₂O) /
                       node.envirs[i_can].p_atm *
                       node.plant_ps[i_can].LAIx[i_leaf] *
                       node.plant_ps[i_can].LA;
        end

        # calculate the photosynthetic rates
        gas_exchange!(node.photo_set, node.plant_ps[i_can], node.envirs[i_can], GswDrive());
        gsw_control!(node.photo_set, node.plant_ps[i_can], node.envirs[i_can]);

        # use the ball-berry model here for now as the ∂A/∂E and ∂A/∂Θ functions are not yet ready
        gsw_ss = stomatal_conductance(node.stomata_model, node.plant_ps[i_can], node.envirs[i_can], FT(1));

        # assume τ = 10 minutes
        for i_leaf in 1:(n_sl+1)
            node.plant_ps[i_can].g_sw[i_leaf] += (gsw_ss[i_leaf] - node.plant_ps[i_can].g_sw[i_leaf]) / 600 * Δt;
        end

        e_sum += e_i_can;
    end

    # update root flow rates
    roots_flow!(node.plant_hs.roots,
                node.plant_hs.container_k,
                node.plant_hs.container_p,
                node.plant_hs.container_q,
                e_sum);

    return nothing
end
=#

function test_soil_from_psoil!(
            node::SPACMono{FT},
            psoils::Array{FT,1},
            e_sum::FT
) where {FT<:AbstractFloat}
    # update the soil water contents and potential in each layer
    for i_root in 1:node.n_root
        node.swc[i_root] = max(node.mswc[i_root],
                               soil_swc(node.plant_hs.roots[i_root].sh,
                                        psoils[i_root])
                              );
        node.plant_hs.roots[i_root].p_ups = psoils[i_root];
    end

    # update root flow rates
    roots_flow!(node.plant_hs, e_sum);

    return nothing
end

function test_soil_from_swc!(
            node::SPACMono{FT},
            swcs::Array{FT,1},
            e_sum::FT
) where {FT<:AbstractFloat}
    # update the soil water contents and potential in each layer
    for i_root in 1:node.n_root
        node.swc[i_root] = max(node.mswc[i_root], swcs[i_root]);
        node.p_soil[i_root] = soil_p_25_swc(node.plant_hs.roots[i_root].sh,
                                            node.swc[i_root]) *
                              node.plant_hs.roots[i_root].f_st;
        # pass the pressure to root_hs
        node.plant_hs.roots[i_root].p_ups = node.p_soil[i_root];
    end

    # update root flow rates
    roots_flow!(node.plant_hs, e_sum);

    return nothing
end


# These functions are taken from algorithms.jl in
# https://github.com/JuliaImages/Images.jl/. We don't want to depend on the
# entire Images package just for these functions hence I have just copied and
# pasted them here for now. However, as we are refactoring the Images.jl package
# we might want to move these (and some other functions) into a separate
# package. Once that is completed we can import that package and remove this
# file.

"""
`m = minfinite(A)` calculates the minimum value in `A`, ignoring any values that are not finite (Inf or NaN).
"""
function minfinite(A::AbstractArray{T}) where T
    ret = sentinel_min(T)
    for a in A
        ret = minfinite_scalar(a, ret)
    end
    ret
end
function minfinite(f, A::AbstractArray)
    ret = sentinel_min(typeof(f(first(A))))
    for a in A
        ret = minfinite_scalar(f(a), ret)
    end
    ret
end

"""
`m = maxfinite(A)` calculates the maximum value in `A`, ignoring any values that are not finite (Inf or NaN).
"""
function maxfinite(A::AbstractArray{T}) where T
    ret = sentinel_max(T)
    for a in A
        ret = maxfinite_scalar(a, ret)
    end
    ret
end
function maxfinite(f, A::AbstractArray)
    ret = sentinel_max(typeof(f(first(A))))
    for a in A
        ret = maxfinite_scalar(f(a), ret)
    end
    ret
end

minfinite_scalar(a::T, b::T) where {T} = isfinite(a) ? (b < a ? b : a) : b
maxfinite_scalar(a::T, b::T) where {T} = isfinite(a) ? (b > a ? b : a) : b
minfinite_scalar(a::T, b::T) where {T<:Union{Integer,FixedPoint}} = b < a ? b : a
maxfinite_scalar(a::T, b::T) where {T<:Union{Integer,FixedPoint}} = b > a ? b : a
minfinite_scalar(a, b) = minfinite_scalar(promote(a, b)...)
maxfinite_scalar(a, b) = maxfinite_scalar(promote(a, b)...)

function minfinite_scalar(c1::C, c2::C) where C<:AbstractRGB
    C(minfinite_scalar(c1.r, c2.r),
      minfinite_scalar(c1.g, c2.g),
      minfinite_scalar(c1.b, c2.b))
end
function maxfinite_scalar(c1::C, c2::C) where C<:AbstractRGB
    C(maxfinite_scalar(c1.r, c2.r),
      maxfinite_scalar(c1.g, c2.g),
      maxfinite_scalar(c1.b, c2.b))
end

sentinel_min(::Type{T}) where {T<:Union{Integer,FixedPoint}} = typemax(T)
sentinel_max(::Type{T}) where {T<:Union{Integer,FixedPoint}} = typemin(T)
sentinel_min(::Type{T}) where {T<:AbstractFloat} = convert(T, NaN)
sentinel_max(::Type{T}) where {T<:AbstractFloat} = convert(T, NaN)
sentinel_min(::Type{C}) where {C<:AbstractRGB} = _sentinel_min(C, eltype(C))
_sentinel_min(::Type{C},::Type{T}) where {C<:AbstractRGB,T} = (s = sentinel_min(T); C(s,s,s))
sentinel_max(::Type{C}) where {C<:AbstractRGB} = _sentinel_max(C, eltype(C))
_sentinel_max(::Type{C},::Type{T}) where {C<:AbstractRGB,T} = (s = sentinel_max(T); C(s,s,s))
sentinel_min(::Type{C}) where {C<:AbstractGray} = _sentinel_min(C, eltype(C))
_sentinel_min(::Type{C},::Type{T}) where {C<:AbstractGray,T} = C(sentinel_min(T))
sentinel_max(::Type{C}) where {C<:AbstractGray} = _sentinel_max(C, eltype(C))
_sentinel_max(::Type{C},::Type{T}) where {C<:AbstractGray,T} = C(sentinel_max(T))


################################################################################
# Spike Rasters
################################################################################

function spikeraster(spikes::Array{Spike,1}; kwargs...)
    points = [Point2f0(s.t, s.n) for s ∈ spikes]
    scatter(points; kwargs...)
end

function spikeraster!(ax, spikes::Array{Spike,1}; kwargs...)
    points = [Point2f0(s.t, s.n) for s ∈ spikes]
    scatter!(ax, points; kwargs...)
end

# function spikeraster(fig::T, spikes::Array{Spike,1}; kwargs...) where
#                      {T <: Union{Figure, AbstractPlotting.FigurePosition}}
#     points = [Point2f0(s.t, s.n) for s ∈ spikes]
#     scatter(fig, points; kwargs...)
# end

# spikeraster(spikes::Array{Spike,1}; kwargs...) = spikeraster(Scene(), spikes; kwargs...)

################################################################################
# Spatial Plots and Animation
################################################################################

function spatialplot(neurontypes, positions, colors::Dict{SimpleNeuronType,Any};
                     size = 5.0, scale=1.0,
                     kwargs...)
    positions = [p .* scale for p ∈ positions]
    scene = Scene()
    colors = [colors[neurontypes[i]] for i ∈ 1:length(neurontypes)]
    scatter!(scene, positions;
             markersize=size, color=colors, kwargs...)
    scene
end

function spatialplot(neurontypes, positions, colors::Dict{SimpleNeuronType,Any},
                     firedidcs::Array{Int,1};
                     firedcolor = :red,
                     firedalpha = 0.5,
                     firedsize = 7.5,
                     kwargs...)
    scene = spatialplot(neurontypes, positions, colors; kwargs...)
    if haskey(kwargs, :scale)
        positions = [p .* kwargs[:scale] for p ∈ positions]
    end
    scatter!(scene, [positions[i] for i ∈ firedidcs];
             color=(firedcolor, firedalpha), markersize=firedsize)
    scene
end

function spatialplot(neurontypes, positions, color::Dict{SimpleNeuronType,Any},
                     graph::Array{Int64,2}; kwargs...)
    scene = spatialplot(neurontypes, positions, color; kwargs...)
	n = size(graph)[1]
    for i ∈ 1:n
        for j ∈ 1:n
			if graph[i,j] != 0
				lines!(scene,
                       [positions[i][1], positions[j][1]],
  					   [positions[i][2], positions[j][2]],
                       [positions[i][3], positions[j][3]];
                       color=(:black, 0.2))
			end
		end
	end
    return scene
end

function spatialanim(neurontypes, positions, colors::Dict{SimpleNeuronType,Any},
                     times::Tuple{Float64,Float64},
                     dt::Float64,
                     spikes::Array{Spike,1};
                     name="spikes",
                     size=0.0100,
                     framerate=20,
                     firedsize=0.0150,
                     numfiringsteps=5,
                     alphas=(0.4,0.8),
                     kwargs...)
    # Set up base plot
    #scene = spatialplot(neurontypes, positions, colors; kwargs...)
    scene = Scene()
    colors = [colors[neurontypes[i]] for i ∈ 1:length(neurontypes)]
    scatter!(scene, positions;
             markersize=size, color=colors, kwargs...)
    # Add overlay of red dots
    firedcolors = [(:red, 0.0) for i ∈ 1:length(neurontypes)]
    firedsizes = [firedsize for _ ∈ 1:length(neurontypes)]
    scatter!(scene, positions;
             markersize=firedsizes, color=firedcolors, strokewidth=0)
    # Bookeeping for animation
    ts = times[1]:dt:times[2]-dt
    prioralphas = LinRange(alphas[2], alphas[1], numfiringsteps)
    priorsizes = LinRange(firedsize, size, numfiringsteps)
    #firedidcs = Array{Array{Int64,1},1}(undef, numfiringsteps)
    firedidcs = [zeros(Int64,0) for _ ∈ 1:numfiringsteps]
    # Start animation
    record(scene, name*".mp4", ts; framerate=framerate) do t
        for i ∈ numfiringsteps:-1:2
            firedidcs[i] = firedidcs[i-1]
        end
        firedcolors = [(:red, 0.0) for i ∈ 1:length(neurontypes)]
        firedsizes = [firedsize for _ ∈ 1:length(neurontypes)]
        firedidcs[1] = [Int64(s.n) for s ∈ filter(s -> t <= s.t < t+dt, spikes)]
        for (i,idcs) ∈ enumerate(firedidcs)
            for idx ∈ idcs
                fcolor = neurontypes[idx] == SimpleNeuronType(:exc) ? (:red) : (:blue)
                firedcolors[idx] = (fcolor,prioralphas[i])
                firedsizes[idx] = priorsizes[i]
            end
        end
        scene[end].color[] = firedcolors
        scene[end].markersize[] = firedsizes
        update!(scene)
    end
end

## Move to plotting functions -- ok, now convert to GLMakie equivalent
# function psthistograms(spikes::Array{Spike,1}, intimes, windur; offset=0.0)
# 	spikeblocks = pstspikes(spikes, intimes, windur; offset=offset)
# 	acs = map(sparseac, spikeblocks)
# 	# acs = [filter(x-> x >= 0, ac) for ac ∈ acs]
# 	hists = [histogram(ac) for ac ∈ acs]
# 	p = plot(hists..., reuse=false)
# end


Array{T}(::UndefInitializer, ::Tuple{PosInfinity}) where T = throw(ArgumentError("Cannot create infinite Array"))
Array{T}(::UndefInitializer, ::Tuple{Integer, PosInfinity}) where {T,N} = throw(ArgumentError("Cannot create infinite Array"))
Array{T}(::UndefInitializer, ::Tuple{PosInfinity, Integer}) where {T,N} = throw(ArgumentError("Cannot create infinite Array"))
Matrix{T}(::UndefInitializer, ::Tuple{Integer, PosInfinity}) where T = throw(ArgumentError("Cannot create infinite Array"))
Matrix{T}(::UndefInitializer, ::Tuple{PosInfinity, Integer}) where T = throw(ArgumentError("Cannot create infinite Array"))
Array{T}(::UndefInitializer, ::Tuple{PosInfinity, PosInfinity}) where {T,N} = throw(ArgumentError("Cannot create infinite Array"))
Matrix{T}(::UndefInitializer, ::Tuple{PosInfinity, PosInfinity}) where T = throw(ArgumentError("Cannot create infinite Array"))

Array{T}(::UndefInitializer, ::PosInfinity) where T = throw(ArgumentError("Cannot create infinite Array"))

Array{T}(::UndefInitializer, ::PosInfinity, ::PosInfinity) where T = throw(ArgumentError("Cannot create infinite Array"))
Array{T}(::UndefInitializer, ::PosInfinity, ::Integer) where T = throw(ArgumentError("Cannot create infinite Array"))
Array{T}(::UndefInitializer, ::Integer, ::PosInfinity) where T = throw(ArgumentError("Cannot create infinite Array"))

Matrix{T}(::UndefInitializer, ::PosInfinity, ::PosInfinity) where T = throw(ArgumentError("Cannot create infinite Array"))
Matrix{T}(::UndefInitializer, ::PosInfinity, ::Integer) where T = throw(ArgumentError("Cannot create infinite Array"))
Matrix{T}(::UndefInitializer, ::Integer, ::PosInfinity) where T = throw(ArgumentError("Cannot create infinite Array"))

Vector{T}(::UndefInitializer, ::Tuple{PosInfinity}) where T = throw(ArgumentError("Cannot create infinite Array"))
Vector{T}(::UndefInitializer, ::PosInfinity) where T = throw(ArgumentError("Cannot create infinite Array"))

similar(A::AbstractArray, ::Type{T}, axes::Tuple{OneToInf{Int}}) where T = cache(Zeros{T}(axes))
similar(A::AbstractArray, ::Type{T}, axes::Tuple{OneToInf{Int},OneToInf{Int}}) where T = cache(Zeros{T}(axes))
similar(A::AbstractArray, ::Type{T}, dims::Tuple{PosInfinity}) where T = cache(Zeros{T}(dims))
similar(A::AbstractArray, ::Type{T}, dims::Tuple{PosInfinity,PosInfinity}) where T = cache(Zeros{T}(dims))
similar(A::AbstractArray, ::Type{T}, dims::Tuple{Integer,PosInfinity}) where T = cache(Zeros{T}(dims))
similar(A::AbstractArray, ::Type{T}, dims::Tuple{PosInfinity,Integer}) where T = cache(Zeros{T}(dims))

similar(::Type{<:AbstractArray{T}}, axes::Tuple{OneToInf{Int}}) where T = cache(Zeros{T}(axes))
similar(::Type{<:AbstractArray{T}}, axes::Tuple{OneToInf{Int},OneToInf{Int}}) where T = cache(Zeros{T}(axes))
similar(::Type{<:AbstractArray{T}}, axes::Tuple{OneToInf{Int},OneTo{Int}}) where T = cache(Zeros{T}(axes))
similar(::Type{<:AbstractArray{T}}, axes::Tuple{OneTo{Int},OneToInf{Int}}) where T = cache(Zeros{T}(axes))
similar(::Type{<:AbstractArray{T}}, dims::Tuple{PosInfinity}) where T = cache(Zeros{T}(dims))
similar(::Type{<:AbstractArray{T}}, dims::Tuple{PosInfinity,PosInfinity}) where T = cache(Zeros{T}(dims))
similar(::Type{<:AbstractArray{T}}, dims::Tuple{Integer,PosInfinity}) where T = cache(Zeros{T}(dims))
similar(::Type{<:AbstractArray{T}}, dims::Tuple{PosInfinity,Integer}) where T = cache(Zeros{T}(dims))

similar(arr::AbstractArray{T}, sz::Union{Infinity,Integer,AbstractUnitRange}...) where T = similar(arr, Base.to_shape(sz)...)
similar(arr::AbstractArray, ::Type{T}, sz::Union{Infinity,Integer,AbstractUnitRange}...) where T = similar(arr, T, Base.to_shape(sz)...)

for Typ in (:Zeros, :Ones)
    @eval begin
        @inline $Typ{T, N}(sz::Tuple{Vararg{Union{Infinity,Integer}, N}}) where {T, N} = $Typ{T,N}(oneto.(sz))
        @inline $Typ{T, N}(sz::Vararg{Union{Infinity,Integer}, N}) where {T, N} = $Typ{T,N}(sz)
        @inline $Typ{T}(sz::Vararg{Union{Infinity,Integer},N}) where {T, N} = $Typ{T, N}(sz)
        @inline $Typ{T}(sz::Infinity) where T = $Typ{T,1}(sz)
    end
end

Eye{T}(::Infinity) where T = Eye{T}(ℵ₀)
Eye{T}(::Infinity, ::Infinity) where T = Eye{T}(ℵ₀, ℵ₀)
Eye{T}(::Infinity, n::Integer) where T = Eye{T}(ℵ₀, n)
Eye{T}(m::Integer, ::Infinity) where T = Eye{T}(m, ℵ₀)
Eye(::Infinity) = Eye(ℵ₀)
Eye(::Infinity, ::Infinity) = Eye(ℵ₀, ℵ₀)
Eye(::Infinity, n::Integer) = Eye(ℵ₀, n)
Eye(m::Integer, ::Infinity) = Eye(m, ℵ₀)

@inline Fill(x, sz::Union{Infinity,Integer}...) = Fill(x, Base.to_shape(sz)...)
@inline Fill{T}(x, sz::Union{Infinity,Integer}...) where T = Fill{T}(x, Base.to_shape(sz)...)

zeros(sz::Union{Infinity, Integer, AbstractUnitRange}...) = zeros(Base.to_shape(sz)...)
zeros(::Type{T}, sz::Union{Infinity, Integer, AbstractUnitRange}...) where T = zeros(T, Base.to_shape(sz)...)
zeros(::Type{T}, ::Tuple{PosInfinity}) where T = cache(Zeros{T}(∞))
zeros(::Type{T}, nm::Tuple{Integer, PosInfinity}) where T = cache(Zeros{T}(nm...))
zeros(::Type{T}, nm::Tuple{PosInfinity, Integer}) where T = cache(Zeros{T}(nm...))
zeros(::Type{T}, nm::Tuple{PosInfinity, PosInfinity}) where T = cache(Zeros{T}(nm...))

ones(sz::Union{Infinity, Integer, AbstractUnitRange}...) = ones(Base.to_shape(sz)...)
ones(::Type{T}, ::Tuple{PosInfinity}) where T = cache(Ones{T}(∞))
ones(::Type{T}, nm::Tuple{Integer, PosInfinity}) where T = cache(Ones{T}(nm...))
ones(::Type{T}, nm::Tuple{PosInfinity, Integer}) where T = cache(Ones{T}(nm...))
ones(::Type{T}, nm::Tuple{PosInfinity, PosInfinity}) where T = cache(Ones{T}(nm...))

fill(x, sz::Union{Infinity, Integer, AbstractUnitRange}...) = fill(x, Base.to_shape(sz)...)
fill(x, ::Tuple{PosInfinity}) = cache(Fill(x,∞))
fill(x, nm::Tuple{Integer, PosInfinity}) = cache(Fill(x,nm...))
fill(x, nm::Tuple{PosInfinity, Integer}) = cache(Fill(x,nm...))
fill(x, nm::Tuple{PosInfinity, PosInfinity}) = cache(Fill(x,nm...))

# This gets called when infinit number of columns
axes_print_matrix_row(_, io, X, A, i, ::AbstractVector{<:PosInfinity}, sep) = nothing
print_matrix_row(io::IO, X::AbstractVecOrMat, A::Vector, i::Integer, cols::AbstractVector{<:PosInfinity}, sep::AbstractString) = nothing
Base.print_matrix_row(io::IO,
        X::Union{LayoutMatrix,
        LayoutVector,
        AbstractTriangular{<:Any,<:LayoutMatrix},
        AdjOrTrans{<:Any,<:LayoutMatrix},
        AdjOrTrans{<:Any,<:LayoutVector},
        HermOrSym{<:Any,<:LayoutMatrix},
        SubArray{<:Any,2,<:LayoutMatrix},
        Diagonal{<:Any,<:LayoutVector}}, A::Vector,
        i::Integer, cols::AbstractVector{<:PosInfinity}, sep::AbstractString) =
    axes_print_matrix_row(axes(X), io, X, A, i, cols, sep)
Base.print_matrix_row(io::IO,
        X::Union{AbstractFill{<:Any,1},
                 AbstractFill{<:Any,2},
                 Diagonal{<:Any,<:AbstractFill{<:Any,1}},
                 RectDiagonal,
                 AbstractTriangular{<:Any,<:AbstractFill{<:Any,2}}
                 }, A::Vector,
        i::Integer, cols::AbstractVector{<:PosInfinity}, sep::AbstractString) =
    axes_print_matrix_row(axes(X), io, X, A, i, cols, sep)

print_matrix_vdots(io::IO, vdots::AbstractString,
        A::Vector, sep::AbstractString, M::Integer, ::NotANumber, pad_right::Bool = true) = nothing

# Avoid infinite loops on maximum
Base.mapreduce_impl(f, op, A::AbstractArray, ifirst::Integer, ::PosInfinity) =
    throw(ArgumentError("Cannot call mapreduce on an infinite length $(typeof(A))"))

function show_delim_array(io::IO, itr::AbstractArray, op, delim, cl,
                          delim_one, i1, ::PosInfinity)
    print(io, op)
    l = 20
    if !show_circular(io, itr)
        recur_io = IOContext(io, :SHOWN_SET => itr)
        if !haskey(io, :compact)
          recur_io = IOContext(recur_io, :compact => true)
        end
        first = true
        i = i1
        if 20 >= i1
          while true
              if !isassigned(itr, i)
                  print(io, undef_ref_str)
              else
                  x = itr[i]
                  show(recur_io, x)
              end
              i += 1
              if i > l
                  print(io, delim)
                  print(io, ' ')
                  print(io, '…')
                  delim_one && first && print(io, delim)
                  break
              end
              first = false
              print(io, delim)
              print(io, ' ')
          end
        end
    end
    print(io, cl)
end

#####
# FillArrays
#####

# Lazy Broadacasting
for typ in (:Ones, :Zeros, :Fill)
    @eval begin
        BroadcastStyle(::Type{$typ{T,N,NTuple{N,<:OneToInf}}}) where {T,N} = LazyArrayStyle{N}()
        BroadcastStyle(::Type{$typ{T,2,<:Tuple{<:Any,<:OneToInf}}}) where {T} = LazyArrayStyle{2}()
        BroadcastStyle(::Type{$typ{T,2,<:Tuple{<:OneToInf,<:Any}}}) where {T} = LazyArrayStyle{2}()
    end
end

BroadcastStyle(::Type{<:Diagonal{T,<:AbstractFill{T,1,Tuple{OneToInf{I}}}}}) where {T,I} = LazyArrayStyle{2}()

## Support broadcast(*, ::AbstractFill, A)

function copy(bc::Broadcasted{<:BroadcastStyle,<:Any,typeof(*),<:Tuple{Ones{T,1,Tuple{OneToInf{Int}}},AbstractArray{V,N}}}) where {N,T,V}
    a,b = bc.args
    @assert bc.axes == axes(b)
    convert(AbstractArray{promote_type(T,V),N}, b)
end

function copy(bc::Broadcasted{<:BroadcastStyle,<:Any,typeof(*),<:Tuple{AbstractArray{T,N},Ones{V,1,Tuple{OneToInf{Int}}}}}) where {N,T,V}
    a,b = bc.args
    @assert bc.axes == axes(a)
    convert(AbstractArray{promote_type(T,V),N}, a)
end

function copy(bc::Broadcasted{<:BroadcastStyle,<:Any,typeof(*),<:Tuple{AbstractFill{T,1,Tuple{OneToInf{Int}}},AbstractArray{V,N}}}) where {N,T<:Number,V}
    a,b = bc.args
    @assert bc.axes == axes(b)
    getindex_value(a) * b
end

function copy(bc::Broadcasted{<:BroadcastStyle,<:Any,typeof(*),<:Tuple{AbstractArray{T,N},AbstractFill{V,1,Tuple{OneToInf{Int}}}}}) where {N,T,V<:Number}
    a,b = bc.args
    @assert bc.axes == axes(a)
    a * getindex_value(b)
end

function copy(bc::Broadcasted{<:BroadcastStyle,<:Any,typeof(*),<:Tuple{AbstractFill{T,1,Tuple{OneToInf{Int}}},AbstractArray{V,N}}}) where {N,T,V}
    a,b = bc.args
    @assert bc.axes == axes(b)
    Ref(getindex_value(a)) .* b # Use broadcast in-case a is array-valued
end

function copy(bc::Broadcasted{<:BroadcastStyle,<:Any,typeof(*),<:Tuple{AbstractArray{T,N},AbstractFill{V,1,Tuple{OneToInf{Int}}}}}) where {N,T,V}
    a,b = bc.args
    @assert bc.axes == axes(a)
    a .* Ref(getindex_value(b)) # Use broadcast in-case b is array-valued
end

# row Vector
function copy(bc::Broadcasted{<:BroadcastStyle,<:Any,typeof(*),<:Tuple{Adjoint{T,Ones{T,1,Tuple{OneToInf{Int}}}},AbstractMatrix{V}}}) where {T,V}
    a,b = bc.args
    @assert bc.axes == axes(b)
    convert(AbstractMatrix{promote_type(T,V)}, b)
end

function copy(bc::Broadcasted{<:BroadcastStyle,<:Any,typeof(*),<:Tuple{AbstractMatrix{T},Adjoint{V,Ones{V,1,Tuple{OneToInf{Int}}}}}}) where {T,V}
    a,b = bc.args
    @assert bc.axes == axes(a)
    convert(AbstractMatrix{promote_type(T,V)}, a)
end

function copy(bc::Broadcasted{<:BroadcastStyle,<:Any,typeof(*),<:Tuple{Adjoint{T,<:AbstractFill{T,1,Tuple{OneToInf{Int}}}},AbstractMatrix{V}}}) where {T,V}
    a,b = bc.args
    @assert bc.axes == axes(b)
    getindex_value(a') * b
end

function copy(bc::Broadcasted{<:BroadcastStyle,<:Any,typeof(*),<:Tuple{AbstractMatrix{T},Adjoint{V,<:AbstractFill{V,1,Tuple{OneToInf{Int}}}}}}) where {T,V}
    a,b = bc.args
    @assert bc.axes == axes(a)
    a * getindex_value(b')
end

#####
# Diagonal
#####

one(D::Diagonal{T,<:AbstractFill{T,1,Tuple{OneToInf{Int}}}}) where T = Eye{T}(size(D,1))
copy(D::Diagonal{T,<:AbstractFill{T,1,Tuple{OneToInf{Int}}}}) where T = D

BroadcastStyle(::Type{<:Diagonal{<:Any,<:AbstractInfUnitRange}}) = LazyArrayStyle{2}()
sub_materialize(::AbstractBandedLayout, V, ::Tuple{InfAxes,InfAxes}) = V
sub_materialize(::AbstractBandedLayout, V, ::Tuple{OneTo{Int},InfAxes}) = V
sub_materialize(::AbstractBandedLayout, V, ::Tuple{InfAxes,OneTo{Int}}) = V

##
# banded columns are padded
##

sublayout(::DiagonalLayout{L}, ::Type{<:Tuple{KR,Integer}}) where {L,KR<:InfAxes} =
    sublayout(PaddedLayout{UnknownLayout}(), Tuple{KR})
sublayout(::DiagonalLayout{L}, ::Type{<:Tuple{Integer,JR}}) where {L,JR<:InfAxes} =
    sublayout(PaddedLayout{UnknownLayout}(), Tuple{JR})
-

sublayout(::AbstractBandedLayout, ::Type{<:Tuple{KR,Integer}}) where {KR<:InfAxes} =
    sublayout(PaddedLayout{UnknownLayout}(), Tuple{KR})
sublayout(::AbstractBandedLayout, ::Type{<:Tuple{Integer,JR}}) where {JR<:InfAxes} =
    sublayout(PaddedLayout{UnknownLayout}(), Tuple{JR})

function sub_paddeddata(::AbstractBandedLayout, S::SubArray{T,1,<:AbstractMatrix,<:Tuple{InfAxes,Integer}}) where T
    P = parent(S)
    (kr,j) = parentindices(S)
    view(P,first(kr):last(colsupport(P,j)),j)
end

function sub_paddeddata(::AbstractBandedLayout, S::SubArray{T,1,<:AbstractMatrix,<:Tuple{Integer,InfAxes}}) where T
    P = parent(S)
    (k,jr) = parentindices(S)
    view(P,k,first(jr):last(rowsupport(P,k)))
end

#####
# Concat: Hcat/Vcat
#####

function getindex(f::Vcat{T,1}, k::PosInfinity) where T
    length(f) == ℵ₀ || throw(BoundsError(f,k))
    ∞
end

_gettail(k, a::Number, b...) = k ≤ 1 ? tuple(a, b...) : _gettail(k - length(a), b...)
_gettail(k, a, b...) = k ≤ length(a) ? tuple(a[k:end], b...) : _gettail(k - length(a), b...)
_vcat(a) = a
_vcat(a, b, c...) = Vcat(a, b, c...)
getindex(A::Vcat, r::InfUnitRange) = Base.invoke(getindex, Tuple{AbstractArray, Any}, A, r)
_unsafe_getindex(::IndexLinear, A::Vcat, r::InfUnitRange) = _vcat(_gettail(first(r), A.args...)...)

# some common cases not catched by LayoutArrays + ambiguities
for InfColMatrix in (:(SubArray{<:Any,2,<:Any,<:Tuple{Any,InfIndexRanges}}),
                     :(SubArray{<:Any,2,<:LayoutVecOrMat,<:Tuple{Any,InfIndexRanges}}),
                     :(AbstractFill{<:Any,2,Tuple{OneTo{Int},OneToInf{Int}}}))
    @eval begin
        Base.typed_vcat(::Type{T}, A::$InfColMatrix, B::AbstractVecOrMat...) where T = Vcat{T}(A, B...)
        Base.typed_vcat(::Type{T}, A::$InfColMatrix, B::LayoutVecOrMats, C::AbstractVecOrMat...) where T = Vcat{T}(A, B, C...)
        Base.hcat(A::Number, B::$InfColMatrix) = Hcat(A, B)
        Base.typed_hcat(::Type{T}, A::AbstractVecOrMat, B::$InfColMatrix) where T = Hcat{T}(A, B)
    end
end

for InfRowArray in (:(AbstractFill{<:Any,1,Tuple{OneToInf{Int}}}),
                    :(AbstractFill{<:Any,2,Tuple{OneToInf{Int},OneTo{Int}}}),
                    :(SubArray{<:Any,2,<:Any,<:Tuple{InfIndexRanges,Any}}),
                    :(SubArray{<:Any,2,<:LayoutVecOrMat,<:Tuple{InfIndexRanges,Any}}),
                    :(SubArray{<:Any,1,<:Any,<:Tuple{Any,InfIndexRanges}}),
                    :(SubArray{<:Any,1,<:LayoutMatrix,<:Tuple{Any,InfIndexRanges}}),
                    :(SubArray{<:Any,1,<:Any,<:Tuple{InfIndexRanges,Any}}),
                    :(SubArray{<:Any,1,<:LayoutMatrix,<:Tuple{InfIndexRanges,Any}}))
    @eval begin
        Base.typed_hcat(::Type{T}, A::$InfRowArray, B::AbstractVecOrMat...) where T = Hcat{T}(A, B...)
        Base.typed_hcat(::Type{T}, A::$InfRowArray, B::LayoutVecOrMats, C::AbstractVecOrMat...) where T = Hcat{T}(A, B, C...)
        Base.typed_hcat(::Type{T}, A::AbstractVecOrMat, B::$InfRowArray, C::AbstractVecOrMat...) where T = Hcat{T}(A, B, C...)
        Base.typed_hcat(::Type{T}, A::LayoutVecOrMat, B::$InfRowArray, C::AbstractVecOrMat...) where T = Hcat{T}(A, B, C...)
    end
end

ArrayLayouts.typed_hcat(::Type{T}, ::Tuple{InfiniteCardinal{0},Any}, A...) where T = Hcat{T}(A...)
ArrayLayouts.typed_hcat(::Type{T}, ::Tuple{InfiniteCardinal{0},InfiniteCardinal{0}}, A...) where T = Hcat{T}(A...)
ArrayLayouts.typed_hcat(::Type{T}, ::Tuple{Any,InfiniteCardinal{0}}, A...) where T = Hcat{T}(A...)

ArrayLayouts.typed_vcat(::Type{T}, ::Tuple{InfiniteCardinal{0},Any}, A...) where T = Vcat{T}(A...)
ArrayLayouts.typed_vcat(::Type{T}, ::Tuple{InfiniteCardinal{0},InfiniteCardinal{0}}, A...) where T = Vcat{T}(A...)
ArrayLayouts.typed_vcat(::Type{T}, ::Tuple{Any,InfiniteCardinal{0}}, A...) where T = Vcat{T}(A...)

##
# lazy sub_materialize
##

sub_materialize(_, V, ::Tuple{InfAxes}) = V
sub_materialize(_, V, ::Tuple{InfAxes,InfAxes}) = V
sub_materialize(_, V, ::Tuple{<:Any,InfAxes}) = V
sub_materialize(_, V, ::Tuple{InfAxes,Any}) = V

sub_materialize(::ApplyLayout{typeof(vcat)}, V::AbstractVector, ::Tuple{InfAxes}) = ApplyArray(V)
sub_materialize(::ApplyLayout{typeof(vcat)}, V::AbstractMatrix, ::Tuple{InfAxes,InfAxes}) = ApplyArray(V)
sub_materialize(::ApplyLayout{typeof(vcat)}, V::AbstractMatrix, ::Tuple{<:Any,InfAxes}) = ApplyArray(V)
sub_materialize(::ApplyLayout{typeof(vcat)}, V::AbstractMatrix, ::Tuple{InfAxes,Any}) = ApplyArray(V)

sub_materialize(::ApplyLayout{typeof(hcat)}, V, ::Tuple{InfAxes}) = V
sub_materialize(::ApplyLayout{typeof(hcat)}, V::AbstractMatrix, ::Tuple{InfAxes,InfAxes}) = ApplyArray(V)
sub_materialize(::ApplyLayout{typeof(hcat)}, V::AbstractMatrix, ::Tuple{<:Any,InfAxes}) = ApplyArray(V)
sub_materialize(::ApplyLayout{typeof(hcat)}, V::AbstractMatrix, ::Tuple{InfAxes,Any}) = ApplyArray(V)

sub_materialize(::PaddedLayout, v::AbstractVector{T}, ::Tuple{InfAxes}) where T =
    _padded_sub_materialize(v)

Base._unsafe_getindex(::IndexStyle, A::AbstractVector, r::InfAxes) = layout_getindex(A, r)
Base._unsafe_getindex(::IndexStyle, A::AbstractFill{<:Any,1}, r::InfAxes) = FillArrays._fill_getindex(A, r)
getindex(A::AbstractCachedVector, r::InfAxes) = layout_getindex(A, r)

Base._unsafe_getindex(::IndexStyle, A::AbstractMatrix, kr::InfAxes, jr::InfAxes) = layout_getindex(A, kr, jr)
Base._unsafe_getindex(::IndexStyle, A::AbstractMatrix, kr::Union{Real, AbstractArray}, jr::InfAxes) = layout_getindex(A, kr, jr)
Base._unsafe_getindex(::IndexStyle, A::AbstractMatrix, kr::InfAxes, jr::Union{Real, AbstractArray}) = layout_getindex(A, kr, jr)

Base._unsafe_getindex(::IndexStyle, A::AbstractFill{<:Any,2}, kr::InfAxes, jr::InfAxes) = FillArrays._fill_getindex(A, kr, jr)
Base._unsafe_getindex(::IndexStyle, A::AbstractFill{<:Any,2}, kr::Union{Real, AbstractArray}, jr::InfAxes) = FillArrays._fill_getindex(A, kr, jr)
Base._unsafe_getindex(::IndexStyle, A::AbstractFill{<:Any,2}, kr::InfAxes, jr::Union{Real, AbstractArray}) = FillArrays._fill_getindex(A, kr, jr)

@inline getindex(A::ApplyMatrix{<:Any,typeof(hcat)}, kr::InfAxes, j::Integer) = layout_getindex(A, kr, j)

Base.checkindex(::Type{Bool}, inds::AbstractUnitRange, I::AbstractFill) = Base.checkindex(Bool, inds, getindex_value(I))
LazyArrays.cache_getindex(::InfiniteCardinal{0}, A::AbstractVector, I, J...) = layout_getindex(A, I, J...)


## these are pre-defined particle types, only for convenience / as examples

module Particles

using StaticArrays

mutable struct Int64Particle
  x::Int64
  Int64Particle() = new()
end

@inline function Base.:(==)(x::Int64Particle, y::Int64Particle)
  return x.x == y.x
end

mutable struct Float64Particle
  x::Float64
  Float64Particle() = new()
end

@inline function Base.:(==)(x::Float64Particle, y::Float64Particle)
  return x.x == y.x
end

struct MVFloat64Particle{d}
  x::MVector{d, Float64}
  MVFloat64Particle{d}() where d = new(MVector{d, Float64}(undef))
end

@inline function Base.:(==)(x::MVFloat64Particle{d}, y::MVFloat64Particle{d}) where d
  return x.x == y.x
end

end


# this file defines iterators used for looping over a grid
struct UpdateFlags
    nodes::Bool
    coords::Bool
end

UpdateFlags(; nodes::Bool=true, coords::Bool=true) =
    UpdateFlags(nodes, coords)

struct CellIterator{dim,N1,N2,N3,T}
    flags::UpdateFlags
    mesh::PolygonalMesh{dim,N1,N2,N3,T}
    current_cellid::ScalarWrapper{Int}
    nodes::Vector{Int}
    coords::Vector{Vec{dim,T}}
    function CellIterator{dim,N1,N2,N3,T}(mesh::PolygonalMesh{dim,N1,N2,N3,T}, flags::UpdateFlags) where {dim,N1,N2,N3,T}
        cell = ScalarWrapper(0)
        nodes = zeros(Int, N1)
        coords = zeros(Vec{dim,T}, N1)
        return new{dim,N1,N2,N3,T}(flags, mesh, cell, nodes, coords)
    end
end

CellIterator(mesh::PolygonalMesh{dim,N1,N2,N3,T},     flags::UpdateFlags=UpdateFlags()) where {dim,N1,N2,N3,T} =
    CellIterator{dim,N1,N2,N3,T}(mesh, flags)

# iterator interface
function Base.iterate(ci::CellIterator, state = 1)
    if state > getncells(ci.mesh)
        return nothing
    else
        return (reinit!(ci, state), state+1)
    end
end
Base.length(ci::CellIterator)  = getncells(ci.mesh)

Base.IteratorSize(::Type{T})   where {T<:CellIterator} = Base.HasLength() # this is default in Base
Base.IteratorEltype(::Type{T}) where {T<:CellIterator} = Base.HasEltype() # this is default in Base
Base.eltype(::Type{T})         where {T<:CellIterator} = T

# utility
@inline getnodes(ci::CellIterator) = ci.nodes
@inline getcoordinates(ci::CellIterator) = ci.coords
@inline nfaces(ci::CellIterator) = nfaces(eltype(ci.mesh.cells))
@inline cellid(ci::CellIterator) = ci.current_cellid[]
@inline celldofs!(v::Vector, dh::DofHandler, ci::CellIterator) = celldofs!(v, dh, ci.current_cellid[])

function reinit!(ci::CellIterator{dim,N}, i::Int) where {dim,N}
    nodeids = ci.mesh.cells[i].nodes
    ci.current_cellid[] = i
    @inbounds for j in 1:N
        nodeid = nodeids[j]
        ci.flags.nodes  && (ci.nodes[j] = nodeid)
        ci.flags.coords && (ci.coords[j] = ci.mesh.nodes[nodeid].x)
    end
    return ci
end

@inline reinit!(fs::DiscreteFunctionSpace{dim,T,FE}, ci::CellIterator{dim,N1,N2,N3,T}) where {dim,N1,N2,N3,T,FE} = reinit!(fs, ci.coords)
@inline reinit!(u::TrialFunction{dim,T}, ci::CellIterator{dim,N1,N2,N3,T}) where {dim,N1,N2,N3,T} = reinit!(u.fs, ci.coords)
function_value(f::Function, fs::DiscreteFunctionSpace{dim,T,FE}, ci::CellIterator,q_point::Int) where {dim,T,FE} = function_value(f,fs, ci.current_cellid[],q_point)
@inline getnfaces(ci::CellIterator{dim,N}) where {dim,N} = getnfaces(ci.mesh.cells[ci.current_cellid[]])
@inline getnodes(ci::CellIterator{dim,N}) where {dim,N} =  ci.mesh.cells[ci.current_cellid[]].nodes
value(u_h::TrialFunction{dim,T}, node::Int, ci::CellIterator{dim,N}) where {dim, T, N} = value(u_h, node, ci.current_cellid[])
@inline face_orientation(ci::CellIterator{dim,N}, face_idx::Int) where {dim,N} = face_orientation(ci.mesh, ci.current_cellid[], face_idx)


module HMCUtilities

export free,
    constrain,
    constrain_with_pushlogpdf,
    constrain_with_pushlogpdf_grad

using ForwardDiff
using DiffResults
using Zygote
using Einsum

include("constraint.jl")
include("advanced_hmc.jl")

end # module


using Fire, Distributed, MKtest

"
	julia abcmk_cli.jl rates --samples 661 --gam_neg -1000,-200 --gL 1,10 --gH 400,1000 --rho 0.001 --theta 0.001 --iterations 1000000 --output rates.jld2 --dac 1,2,4,5,10,20,50,100,200,400,500,661,925,1000 --nprocs 7

Function to solve fixation and polymorphic rates analitically. The function will create N random models from prior values. Use the arguments to defined the input range for each parameter.

If rho and/or theta are set to nothing, the function will input random values given the range 0.0005:0.0005:0.01. Otherwise you can fix the values.

If gL is set to nothing, the function will not account the role of the weakly selected alleles in the estimation.

If scheduler is set to threads the software will be run using multi-threading, not distributed computing. Please be sure you start up Julia using the same number of threads as the argument nprocs using the option julia -t nprocs

The function returns a HDF5 file containing models solved and rates. The rates will be used to compute summary statistics required at ABC.

Please check the documentation to get more info about models parameters or detailed arguments description https://jmurga.github.io/MKtest.jl/dev/cli/ to check model
"
@main function rates(;ne::Int64=1000,
		samples::Int64=661,
		alpha::String="0.1,0.9",
		gam_neg::String="-1000,-200",
		gL::String="5,10",
		gH::String="400,1000",
		dac::String="1,2,4,5,10,20,50,100,200,400,500,661,925,1000",
		shape::Float64=0.184,
		rho::String="nothing",
		theta::String="nothing",
		iterations::Int64=100000,
		output::String="/home/jmurga/rates.jld2",
		scheduler::String="local",
		nprocs::Int64=1
	)

	α          = parse.(Float64,split(alpha,","));
	tmp_neg    = parse.(Int,split(gam_neg,","));
	tmp_strong = parse.(Int,split(gH,","));
	dac        = parse.(Int,split(dac,","));

	if (gL == "nothing")
		tmp_weak = nothing
	else
		tmp_weak = parse.(Int,split(gL,","))
	end

	if (rho == "nothing")
		rho = nothing
	else
		rho = parse(Float64,rho)
	end

	if (theta == "nothing")
		theta = nothing
	else
		theta = parse(Float64,theta)
	end

	threads=false;
	if (scheduler == "slurm")
		@eval using ClusterManagers
		@eval addprocs_slurm($nprocs)
	elseif (scheduler == "htcondor")
		@eval using ClusterManagers
		@eval addprocs_htc($nprocs)
	elseif (scheduler == "local")
		@eval addprocs($nprocs)
	else
		threads=true;
	end

	@eval @everywhere using MKtest
	@eval adap = MKtest.parameters(N=$ne,n=$samples,dac=$dac,al=$shape);

	@eval MKtest.rates(
		param      = $adap,
		α          = $α,
		gH         = $tmp_strong[1]:$tmp_strong[2],
		gL         = $tmp_weak[1]:$tmp_weak[2],
		gam_neg    = $tmp_neg[1]:$tmp_neg[2],
		iterations = $iterations,
		rho        = $rho,
		theta      = $theta,
		output     = $output,
		threads    = $threads
	);

	if scheduler != "threads"
		for i in workers()
			rmprocs(i)
		end
	end
end

"
	julia abcmk_cli.jl parse_data --analysis_folder analysis/ --gene_list analysis/dnaVipsList.txt

Function to parse polymorphic and divergence data from Uricchio et. al (2019) and Murga-Moreno et al (2019). Please input a path to create a new analysis folder. You can filter the dataset using a file containing a list of Ensembl IDs.

The function returns files containing raw polymorphic and divergence data, parsed SFS and parsed divegence required to estimate summary statistics.

Please check the documentation to get more info https://jmurga.github.io/MKtest.jl/dev/cli/
"
@main function data(;
	analysis_folder::String="<folder>",
	dataset::String="tgp",
	gene_list::String="false",
	bins::String="false")

	@eval using DataFrames, CSV

	mkpath(analysis_folder)

	dataset = lowercase(dataset)
	data    = analysis_folder * "/" * dataset * ".txt"

	download("https://raw.githubusercontent.com/jmurga/MKtest.jl/master/data/"* dataset * ".txt",data)

	# Check if bins or genelist are defined
	@eval if $gene_list != "false"
		@eval g_list = String.(Array(CSV.read($gene_list,DataFrame,header=false)))[:,2:end]
	else
		g_list = nothing
	end

	@eval if $bins != "false"
		@eval bins_size = parse(Int,$bins)
	else
		bins_size = nothing
	end

	# Parsing TGP data
	if dataset == "tgp"
		@eval α,sfs, divergence = MKtest.parse_sfs(sample_size=661,data=$data,gene_list=$g_list,bins=$bins_size)
	elseif occursin("zi",dataset)
		@eval α,sfs, divergence = MKtest.parseSfs(sample_size=154,data=$data,gene_list=$g_list,bins=$bins_size,isolines=true)
	elseif occursin("ral",dataset)
		@eval α,sfs, divergence = MKtest.parseSfs(sample_size=160,data=$data,gene_list=$g_list,bins=$bins_size,isolines=true)
	end

	# Writting data to folder
	@eval s_name = $analysis_folder .* "/sfs".*string.(1:length(sfs)).*".tsv"
	@eval d_name = $analysis_folder .* "/div".*string.(1:length(sfs)).*".tsv"

	@eval CSV.write.($s_name,DataFrame.($sfs,:auto),delim='\t',header=false)
	@eval CSV.write.($d_name,DataFrame.($divergence,:auto),delim='\t',header=false)

end

"
	julia abcmk_cli.jl summaries --analysis_folder analysis/ --rates analysis/rates.jld2 --samples 661 --dac 2,4,5,10,20,50,200,661,925 --summstat_size 1000000 --nprocs 7

Estimate summary statistics from analytical rates. You must provide a path containing the parsed SFS and divergence file.

The function returns files containing bootstrapped datasets (alphas.txt) and summary statistics (summstat.txt)

Check the documentation to get more info https://jmurga.github.io/MKtest.jl/dev/cli"
@main function summaries(;analysis_folder::String="<folder>",h5_file::String="rates.jld2",ne::Int64=1000, samples::Int64=661,dac::String="2,4,5,10,20,50,200,661,925",summstat_size::Int64=100000,bootstrap::Int64=0)

	@eval  using JLD2, DataFrames, CSV

	sfs     = CSV.read.(filter(x -> occursin("sfs",x), readdir(analysis_folder,join=true)),DataFrame,header=false) .|> Array;
	divergence     = CSV.read.(filter(x -> occursin("div",x), readdir(analysis_folder,join=true)),DataFrame,header=false) .|> Array;

	@eval adap      = MKtest.parameters(N=$ne,n=$samples,dac =parse.(Int,split($dac,",")))

	@eval if $bootstrap != 0
		@eval summstat  = MKtest.summary_statistics(
            param           = adap,
            h5_file         = $h5_file,
            sfs             = $sfs,
            divergence      = $divergence,
            analysis_folder = $analysis_folder,
            summstat_size   = $summstat_size,
            bootstrap       = $bootstrap
		);
	else
		@eval summstat  = MKtest.summary_statistics(
            param           = $adap,
            h5_file         = $h5_file,
            sfs             = $sfs,
            divergence      = $divergence,
            analysis_folder = $analysis_folder,
            summstat_size   = $summstat_size
		);
	end
end

"ABCreg inference.

The function returns posterior distributions from ABC inference. Each posterior file contains information about alpha_w, alpha_s, alpha, gam_neg and shape parameter. The number of posterior distributions will depend on the number of bootstrap replicas.

Check the documentation to get more info https://jmurga.github.io/MKtest.jl/dev/cli
"
@main function inference(;analysis_folder::String="<folder>",P::Int64=5,S::Int64=9,tol::Float64=0.025,ABCreg::String="/home/jmurga/ABCreg/src/reg")

	@eval MKtest.ABCreg(
        analysis_folder = $analysis_folder,
        P               = $P,
        S               = $S,
        tol             = $tol,
        abcreg          = $ABCreg
	);

end

#="Plot Maximum a posterior distribution"
@main function plotMap(;analysis_folder::String="<folder>")
	try
		@eval using RCall, GZip, DataFrames, CSV

		@eval MKtest.sourcePlotMapR(script=$analysis_folder)
		@eval MKtest.plotMap(analysis_folder=$analysis_folder)
		@eval RCall.endEmbeddedR()
	catch
		println("Please install R, ggplot2, data.table and locfit in your system before execute this function")
	end
end=#


using MolecularGraph:
    fgroupquery, fgroupcond, fgrouprecord, getterm, setterm!

@testset "funcgroup" begin

@testset "fgroupquery" begin
    glyc = smilestomol("C(O)C(O)CO")
    res = fgroupquery(glyc, "[#6][OD1]")
    @test length(res) == 3
end

@testset "fgroupcond" begin
    alkoxide = smilestomol("C(O)C(O)C[O-]")
    res = fgroupcond(alkoxide, Dict("any" => ["[#6][OD1]", "[#6][O-]"]))
    @test length(res) == 3
end

@testset "fgrouprecord" begin
    coumarin = smilestomol("C1=CC(=O)OC2=C1C=CC=C2")
    fgc = FunctionalGroupClassifier()
    setterm!(fgc, :Carbonyl, fgroupquery(coumarin, "[#6]=[OD1]"))
    res1 = fgrouprecord(
        coumarin, fgc, Dict("have" => ["Carbonyl"], "query" => "[#6](=O)[#8]"))
    @test issetequal(collect(res1)[1], [3, 4, 5])

    res2 = fgrouprecord(
        coumarin, fgc,
        Dict("have" => ["Carbonyl", "Something"], "query" => "[#6](=O)[#8]"))
    @test isempty(res2)

    setterm!(fgc, :Carboxyl, res1)
    res3 = fgrouprecord(
        coumarin, fgc, Dict("isa" => ["Carboxyl"], "query" => "c(=O)o"))
    @test issetequal(collect(res3)[1], [3, 4, 5])
    res4 = fgrouprecord(
        coumarin, fgc, Dict("isa" => ["Carboxyl"], "query" => "[#6](=O)O"))
    @test isempty(res4)
end

@testset "functionalgroupgraph" begin
    paclitaxel = smilestomol(
        "CC1=C2[C@@]([C@]([C@H]([C@@H]3[C@]4([C@H](OC4)C[C@@H]([C@]3(C(=O)[C@@H]2OC(=O)C)C)O)OC(=O)C)OC(=O)c5ccccc5)(C[C@@H]1OC(=O)[C@H](O)[C@@H](NC(=O)c6ccccc6)c7ccccc7)O)(C)C"
    )
    paclitaxel = removehydrogens(paclitaxel)
    fgc = functionalgroupgraph(paclitaxel)
    @test length(getterm(fgc, :Carboxyl)) == 4
    @test length(getterm(fgc, :SecAlcohol)) == 2
    @test length(getterm(fgc, :FourMemberedRing)) == 1
    @test length(getterm(fgc, :EightMemberedRing)) == 1
end

end # funcgroup


#####
##### Sources
#####

eq_tends(pv::AbstractPrognosticVariable, m::LandModel, tt::Source) =
    (m.source_dt[pv]...,)

#####
##### First order fluxes
#####

eq_tends(::PV, ::LandModel, ::Flux{FirstOrder}) where {PV} = ()

#####
##### Second order fluxes
#####

# Empty by default
eq_tends(pv::PV, ::AbstractSoilComponentModel, ::Flux{SecondOrder}) where {PV} =
    ()

eq_tends(pv::PV, land::LandModel, tt::Flux{SecondOrder}) where {PV} =
    (eq_tends(pv, land.soil.heat, tt)..., eq_tends(pv, land.soil.water, tt)...)

eq_tends(
    pv::PV,
    land::LandModel,
    tt::Flux{SecondOrder},
) where {PV <: VolumetricInternalEnergy} =
    (eq_tends(pv, land.soil.heat, land.soil.water, tt)...,)

# TODO: move to soi_heat.jl?
eq_tends(
    ::VolumetricInternalEnergy,
    ::SoilHeatModel,
    ::SoilWaterModel,
    ::Flux{SecondOrder},
) = (DiffHeatFlux(), DarcyDrivenHeatFlux())

# TODO: move to soi_water.jl?
eq_tends(
    ::VolumetricInternalEnergy,
    ::SoilHeatModel,
    ::PrescribedWaterModel,
    ::Flux{SecondOrder},
) = (DiffHeatFlux(),)

eq_tends(::VolumetricLiquidFraction, ::SoilWaterModel, ::Flux{SecondOrder}) =
    (DarcyFlux(),)


__precompile__(false)

module NMRDecompose

using NMR

include("xcorr.jl")
include("util.jl")
include("distributions.jl")

end


mutable struct Item{I<:Inventory, S<:Tuple, P} <: AbstractItem
    inventory::I
    sources::S
    policy::P
    position_log::Vector{Float64}
    name::String
end

function Item(inventory::Inventory, sources::Union{Assembly, Supplier}...; policy = sSPolicy(), name = "item")
    Item(inventory, sources, policy, zeros(0), name)
end

RLBase.state(e::Item) = [state(e.inventory); reduce(vcat, [state(source) for source in e.sources])]
state_size(e::Item) = state_size(e.inventory) + sum(state_size.(e.sources))
action_size(e::Item) = action_size(e.policy)*length(e.sources)
function print_state(e::Item)
    ps = [print_state(e.inventory); reduce(vcat, print_state.(e.sources))]
    return [e.name*" "*first(p) => last(p) for p in ps]
end
print_action(e::Item) = reduce(vcat, [e.name .* " " .* source.name .* " " .* print_action(e.policy) for source in e.sources])

function pull!(e::Item, quantity::Number, issuer)
    pull!(e.inventory, quantity, issuer)
end

function (e::Item)(Qs)
    for (source, Q) in zip(e.sources, Qs)
        pull!(source, Q, e.inventory)
    end
end

function dispatch!(e::Item)
    for source in e.sources
        dispatch!(source)
    end
    dispatch!(e.inventory)
end

function reward!(e::Item)
    r = 0.
    r += reward!(e.inventory)
    for source in e.sources
        r += reward!(source)
    end
    push!(e.position_log, inventory_position(e))
    return r
end

function RLBase.reset!(e::Item)
    empty!(e.position_log)
    reset!(e.inventory)
    for source in e.sources
        reset!(source)
    end
    return nothing
end

function inventory_position(e::Item)
    ip = inventory_position(e.inventory)
    for source in e.sources
        ip += inventory_position(source)
    end
    return ip
end

function children(e::Item)
    mapreduce(children, (x,y) -> tuple(x..., y...), e.sources)
end

function Base.show(io::IO, e::Item)
    print(io, "Item(", e.name,": ")
    show(io, e.inventory)
    print(io, ", ")
    for source in e.sources
        show(io, source)
        print(io, ", ")
    end
    show(io, e.policy)
    print(io, ")")
end


# RELOG: Reverse Logistics Optimization
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.

using DataStructures
using JSON
using JSONSchema
using Printf
using Statistics

"""
    _compress(instance::Instance)

Create a single-period instance from a multi-period one. Specifically,
replaces every time-dependent attribute, such as initial_amounts,
by a list with a single element, which is either a sum, an average,
or something else that makes sense to that specific attribute.
"""
function _compress(instance::Instance)::Instance
    T = instance.time
    compressed = deepcopy(instance)
    compressed.time = 1
    compressed.building_period = [1]

    # Compress products
    for p in compressed.products
        p.transportation_cost = [mean(p.transportation_cost)]
        p.transportation_energy = [mean(p.transportation_energy)]
        for (emission_name, emission_value) in p.transportation_emissions
            p.transportation_emissions[emission_name] = [mean(emission_value)]
        end
    end

    # Compress collection centers
    for c in compressed.collection_centers
        c.amount = [maximum(c.amount) * T]
    end

    # Compress plants
    for plant in compressed.plants
        plant.energy = [mean(plant.energy)]
        for (emission_name, emission_value) in plant.emissions
            plant.emissions[emission_name] = [mean(emission_value)]
        end
        for s in plant.sizes
            s.capacity *= T
            s.variable_operating_cost = [mean(s.variable_operating_cost)]
            s.opening_cost = [s.opening_cost[1]]
            s.fixed_operating_cost = [sum(s.fixed_operating_cost)]
        end
        for (prod_name, disp_limit) in plant.disposal_limit
            plant.disposal_limit[prod_name] = [sum(disp_limit)]
        end
        for (prod_name, disp_cost) in plant.disposal_cost
            plant.disposal_cost[prod_name] = [mean(disp_cost)]
        end
    end

    return compressed
end


########################################################################
#                         CONVERSION TO EXGRAPH                        #
########################################################################

make_name(id::Int) = Symbol("%$id")
make_name(op::AbstractOp) = Symbol("%$(op.id)")

unmake_name(x::Symbol) = parse(Int, string(x)[2:end])

Espresso.to_expr(op::Input) = :()
Espresso.to_expr(op::Assign) = Expr(:(=), make_name(op), make_name(op.src_id))

function Espresso.to_expr(op::Constant)
    val = op.val isa Symbol ? QuoteNode(op.val) : op.val
    return Expr(:(=), make_name(op), val)
end

function Espresso.to_expr(op::Call)
    arg_names = map(make_name, op.args)
    call = Expr(:call, op.fn, arg_names...)
    # assign = ((op.val isa Array || is_cuarray(op.val)) && op.fn == broadcast) ? :(.=) : :(=)
    assign = :(=)
    return Expr(assign, make_name(op.id), call)
end

function Espresso.to_expr(tape::Tape)
    body = Expr(:block)
    for op in tape
        push!(body.args, to_expr(op))
    end
    return body
end

# alternatives for simplegrad
function to_simple_expr(op::Call)
    arg_names = map(make_name, op.args)
    call = Expr(:call, op.fn, arg_names...)
    return Expr(:(=), make_name(op.id), call)
end

to_simple_expr(op::AbstractOp) = to_expr(op)

########################################################################
#                       CODE GENERATION                                #
########################################################################

"""
Rewrite :(Z = X * Y) into :(mul!(Z, X, Y)), but only if X and Y are arrays
"""
function rewrite_mul(tape::Tape, ex::Expr)
    @assert ex.head == :block
    new_ex = Expr(:block)
    for subex in ex.args
        if (matchingex(:(_R = $*(_X, _Y)), subex)
            && tape[unmake_name(subex.args[1])].val isa AbstractArray
            && all(tape[unmake_name(subex.args[2].args[i])].val isa AbstractArray for i=2:3))
            new_subex = rewrite(subex, :(_Z = $*(_X, _Y)), :($mul!(_Z, _X, _Y)))
            push!(new_ex.args, new_subex)
        else
            push!(new_ex.args, subex)
        end
    end
    return new_ex
end

"""
Generate initialization block of function code
"""
function generate_prologue(tape::Tape)
    ex = Expr(:body)
    for op in tape
        name = make_name(op)
        push!(ex.args, :($name = $(op).val))
    end
    return ex
end

"""
Main part of generated function code
"""
function generate_body(tape::Tape)
    # ret_var_ids = vcat(values(tape.derivs) |> collect, [tape.resultid])
    # ret_var_names = [Symbol("%$id") for id in ret_var_ids]
    # exg = to_exgraph(tape)
    # exg = Espresso.fuse_broadcasting(exg)  # TODO: this breaks test_examples.jl
    # exg = Espresso.fuse_assigned(exg; outvars=ret_var_names)
    # exg = Espresso.eliminate_common(exg)
    # ex = Espresso.to_expr_kw(exg)
    # ex = rewrite_all(ex, INPLACE_RULES; phs=Set([:X, :Y, :Z]))
    ex = to_expr(tape)
    ex = rewrite_mul(tape, ex)
    return ex
end

"""
Generate last block of function code
"""
function generate_epilogue(tape::Tape)
    ex = Expr(:body)
    for op in tape
        name = make_name(op)
        push!(ex.args, :($(op).val = $name))
    end
    return ex
end

"""
Generate function expression from the tape, binding all variables to tape's buffers
and optimizing code.
"""
function generate_function_expr(tape::Tape)
    fn_ex = :(function $(gensym("tape_fn"))() end)
    fn_ex_body = fn_ex.args[2]
    prologue = generate_prologue(tape)
    body = generate_body(tape)
    epilogue = generate_epilogue(tape)
    # abusing LineNumberNode to insert comments
    push!(fn_ex_body.args, LineNumberNode(0, :prologue))
    push!(fn_ex_body.args, prologue.args...)
    push!(fn_ex_body.args, LineNumberNode(0, :body))
    push!(fn_ex_body.args, body.args...)
    push!(fn_ex_body.args, LineNumberNode(0, :epilogue))
    push!(fn_ex_body.args, epilogue.args...)
    return fn_ex
end

"""
Generate function expression from the tape without any optimizations or binding to
tape's buffers.

`ret_grad` optional parameter controls whether function should return only
value of tape[resultid] (default) or also values of gradient nodes
"""
function generate_function_expr_unbound(tape::Tape; ret_grad=false)
    # fn_args = [Expr(:(::), make_name(inp), typeof(inp.val)) for inp in tape if isa(inp, Input)]
    fn_args = [make_name(inp) for inp in tape if isa(inp, Input)]
    fn_ex = :(function $(gensym("tape_fn"))($(fn_args...)) end)
    fn_ex_body = fn_ex.args[2]
    for op in tape
        if !isa(op, Input)
            ex = op |> to_simple_expr
            push!(fn_ex_body.args, ex)
        end
    end
    if ret_grad
        res_var = make_name(tape[tape.resultid])
        grad_vars = []
        for op in tape
            if op isa Input
                if haskey(tape.derivs, op.id)
                    push!(grad_vars, make_name(tape.derivs[op.id]))
                else
                    push!(grad_vars, nothing)
                end
            end
        end
        ret_tuple = Expr(:tuple, res_var, grad_vars...)
        push!(fn_ex_body.args, :(return $ret_tuple))
    else
        push!(fn_ex_body.args, :(return $(make_name(tape[tape.resultid]))))
    end
    return fn_ex
end

########################################################################
#                            COMPILATION                               #
########################################################################

function compile(tape::Tape; bind=true, ret_grad=false)
    fn_ex = bind ? generate_function_expr(tape) : generate_function_expr_unbound(tape; ret_grad=ret_grad)
    return Core.eval(@__MODULE__, fn_ex)
end

function compile!(tape::Tape)
    tape.compiled = compile(tape)
end