function out_of_sample(net_data,forecast,sol_stochastic)
    """
    runs out-of-sample analysis
    """
    Ï†Ìƒ = zeros(length(net_data[:E]),forecast[:S])
    Ï‘Ìƒ = zeros(length(net_data[:N]),forecast[:S])
    ÎºÌƒ = zeros(length(net_data[:E]),forecast[:S])
    Ï€Ìƒ = zeros(length(net_data[:N]),forecast[:S])
    Ï�Ìƒ = zeros(length(net_data[:N]),forecast[:S])
    exp_cost = zeros(forecast[:S])

    for s in 1:forecast[:S]
        Ï†Ìƒ[:,s] = sol_stochastic[:Ï†] .+ (lin_res[:Ï‚Ì€2]*(sol_stochastic[:Î±] .- diagm(ones(length(net_data[:N])))) .- lin_res[:Ï‚Ì€3]*sol_stochastic[:Î²])*forecast[:Î¾][:,s]
        Ï‘Ìƒ[:,s] = sol_stochastic[:Ï‘] .+ sol_stochastic[:Î±]*forecast[:Î¾][:,s]
        ÎºÌƒ[:,s] = sol_stochastic[:Îº] .+ sol_stochastic[:Î²]*forecast[:Î¾][:,s]
        Ï€Ìƒ[:,s] = sol_stochastic[:ğ�›‘] .+ (lin_res[:Ï‚Ì†2]*(sol_stochastic[:Î±] .- lin_res[:Ï‚Ì‚3]*sol_stochastic[:Î²] .- diagm(ones(length(net_data[:N])))))*forecast[:Î¾][:,s]
        Ï�Ìƒ[:,s] = sqrt.(max.(Ï€Ìƒ[:,s],0))
        exp_cost[s] = Ï‘Ìƒ[:,s]'*diagm(vec(net_data[:c]))*Ï‘Ìƒ[:,s]
    end

    inf_flag = zeros(forecast[:S])
    num_tolerance = 0.001
    for s in 1:forecast[:S]
        for n in net_data[:N]
            if n in findall(x->x>0, net_data[:Ï‘Ì…])
                Ï‘Ìƒ[n,s] >= net_data[:Ï‘Ì…][n] + num_tolerance ? inf_flag[s] = 1 : NaN
                Ï‘Ìƒ[n,s] <= 0 - num_tolerance ? inf_flag[s] = 1 : NaN
            end
            Ï�Ìƒ[n,s] >= net_data[:Ï�Ì…][n] + num_tolerance ? inf_flag[s] = 1 : NaN
            Ï�Ìƒ[n,s] <= net_data[:Ï�Ì²][n] - num_tolerance ? inf_flag[s] = 1 : NaN
        end
        for p in net_data[:E_a]
            Ï†Ìƒ[p,s] <= 0  - num_tolerance ? inf_flag[s] = 1 : NaN
            ÎºÌƒ[p,s] >= net_data[:ÎºÌ…][p]  + num_tolerance ? inf_flag[s] = 1 : NaN
            ÎºÌƒ[p,s] <= net_data[:ÎºÌ²][p]  - num_tolerance ? inf_flag[s] = 1 : NaN
        end
    end
    @info("done out-of-sample results")
    return Dict(:Ï† => Ï†Ìƒ, :Ï‘ => Ï‘Ìƒ, :Îº => ÎºÌƒ, :ğ�›‘ => Ï€Ìƒ, :Ï� => Ï�Ìƒ,
                :cost => mean(exp_cost), :Îµ_stat => sum(inf_flag)/forecast[:S])
end
function projection_opt(net_data,forecast,sol_ofs,s)
    """
    solves projection problem for a single uncertainty sample
    """
    N_Ï‘ = findall(x->x>0, net_data[:Ï‘Ì…])
    N_Îº = net_data[:E_a]
    # build model
    model = Model(optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0))
    # variable declaration
    @variable(model, ğ�›‘[1:length(net_data[:N])])
    @variable(model, Ï†[1:length(net_data[:E])])
    @variable(model, Îº[1:length(net_data[:E])])
    @variable(model, Ï‘[1:length(net_data[:N])])
    # minimize gas production cost
    @objective(model, Min, (sol_ofs[:Ï‘][N_Ï‘,s] .- Ï‘[N_Ï‘])' * diagm(ones(length(N_Ï‘))) * (sol_ofs[:Ï‘][N_Ï‘,s] .- Ï‘[N_Ï‘])
                            + (sol_ofs[:Îº][N_Îº,s] .- Îº[N_Îº])' * diagm(ones(length(N_Îº))) * (sol_ofs[:Îº][N_Îº,s] .- Îº[N_Îº])
                )
    # gas variable limits
    @constraint(model, inj_lim_max[i=net_data[:N]], Ï‘[i] <= net_data[:Ï‘Ì…][i])
    @constraint(model, inj_lim_min[i=net_data[:N]], Ï‘[i] >= net_data[:Ï‘Ì²][i])
    @constraint(model, pre_lim_max[i=net_data[:N]], ğ�›‘[i] <= net_data[:Ï�Ì…][i]^2)
    @constraint(model, pre_lim_min[i=net_data[:N]], ğ�›‘[i] >= net_data[:Ï�Ì²][i]^2)
    @constraint(model, com_lim_max[i=net_data[:E]], Îº[i] <= net_data[:ÎºÌ…][i])
    @constraint(model, com_lim_min[i=net_data[:E]], Îº[i] >= net_data[:ÎºÌ²][i])
    # gas flow equations
    @NLconstraint(model, w_eq[l=net_data[:E]], Ï†[l]*abs(Ï†[l]) - net_data[:k][l]^2 *(ğ�›‘[ns(l)] + Îº[l] - ğ�›‘[nr(l)]) == 0)
    @constraint(model, gas_bal, Ï‘ .- net_data[:Î´] .- forecast[:Î¾][:,s] .- net_data[:B]*Îº .== net_data[:A]*Ï†)
    @constraint(model, Ï†_pl[l=net_data[:E_a]], Ï†[l] >= 0)
    @constraint(model, ğ�›‘[net_data[:ref]] == lin_res[:ğ�›‘Ì‡][net_data[:ref]])
    # solve model
    optimize!(model)
    # return solution
    solution = Dict(
    :Î”Ï‘ => sum(norm(JuMP.value.(Ï‘)[n] - sol_ofs[:Ï‘][n,s]) for n in N_Ï‘),
    :Î”Îº => sum(sqrt(norm(JuMP.value.(Îº)[n] - sol_ofs[:Îº][n,s])) for n in N_Îº),
    )
end
function projection_analysis(net_data,forecast,sol_ofs)
    """
    computes projection statistics
    """
    S = forecast[:S]
    Î”Ï‘ = zeros(S) # gas injection projection
    Î”Îº = zeros(S) # pressure regulation projection
    for s in 1:S
        sol_proj = projection_opt(net_data,forecast,sol_ofs,s)
        Î”Ï‘ = sol_proj[:Î”Ï‘]
        Î”Îº = sol_proj[:Î”Îº]
    end
    return Dict(:Î”Ï‘_mean => mean(Î”Ï‘), :Î”Îº_mean => mean(Î”Îº))
end
