You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
291 lines
7.0 KiB
Julia
291 lines
7.0 KiB
Julia
using LinearAlgebra
|
|
using Distributions
|
|
using StatsFuns
|
|
using Plots
|
|
|
|
abstract type DestructionProbability end
|
|
|
|
struct LinearProbability <: DestructionProbability
|
|
spontaneous
|
|
intra_constellation
|
|
inter_constellation
|
|
debris
|
|
end
|
|
function (lp::LinearProbability)(Sᵢ, S, D,)
|
|
min(1,max(0,
|
|
lp.spontaneous + lp.intra_constellation * Sᵢ + lp.inter_constellation * (S-Sᵢ) + lp.debris * D)
|
|
)
|
|
end
|
|
|
|
function individual_loss(Sᵢ, S, D,n=1,return_prob=false)
|
|
#=
|
|
This function simulates how many satellites will be lost
|
|
from a given constellation.
|
|
|
|
TODO: Requires calibration of some sort
|
|
=#
|
|
β₀ = 3e-4 #probability of spontaneous destruction
|
|
β₁ = 1e-6 #proability of collision with satellite in same constellation
|
|
β₂ = 1e-5 #probability of collision with satellite in different constellation
|
|
β₃ = 1e-4 #probability of collision with debris
|
|
|
|
#linear probability (with constraints)
|
|
p = min(1,max(0,β₀ + β₁ * Sᵢ + β₂ * (S-Sᵢ) + β₃ * D))
|
|
#p = logistic(β₀ + β₁ * Sᵢ + β₂ * (S-Sᵢ) + β₃ * D)
|
|
|
|
dist = Binomial(Sᵢ, p)
|
|
draw = rand(dist)
|
|
|
|
if return_prob
|
|
return draw, p
|
|
else
|
|
return draw
|
|
end
|
|
end
|
|
|
|
|
|
function debris_transition(D,S⃗, Y⃗, X⃗, δ=0.005, Γ=0.5)
|
|
#δ: 1/δ year decay rate
|
|
#Γ: proportion of the mass of the satellite that is released into orbit during launch
|
|
|
|
#new debris released equals the debris lost to deorbit + debris generated by collisions + debris due to launches
|
|
new_debris = (1-δ) * D .+ sum(Y⃗) .+ Γ*sum(X⃗)
|
|
return new_debris
|
|
end
|
|
|
|
|
|
function stocks_transition(S⃗, Y⃗, X⃗)
|
|
return S⃗ .+ X⃗ .- Y⃗
|
|
end
|
|
|
|
|
|
function overall_losses(S⃗, D)
|
|
#=
|
|
Simulate the losses for each constellation
|
|
=#
|
|
|
|
individual_outcomes = [ individual_loss(s, sum(S⃗),D,1,true) for s in S⃗ ]
|
|
|
|
losses = []
|
|
prob_distruction = []
|
|
for (loss,prob) in individual_outcomes
|
|
append!(losses,loss)
|
|
append!(prob_distruction,prob)
|
|
end
|
|
|
|
return losses,prob_distruction
|
|
|
|
end
|
|
|
|
# The demand function for the market
|
|
|
|
function inverse_demand_cournot(S⃗,A=100)
|
|
A - sum(S⃗)
|
|
end
|
|
|
|
function profit_function(s,x,C_operation,C_launch,T_launch, T_operation,Price)
|
|
#=
|
|
The profit function each participant recieves
|
|
Includes place for taxes and costs.
|
|
=#
|
|
(Price-T_operation-C_operation)*s - (C_launch + T_launch)*x
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
#Get histogram of loss numbers per 10_000
|
|
histogram(
|
|
[sum(individual_loss(33,67,500,10_000)) for i in 1:10000]
|
|
,bins=range(0,200,length=201)
|
|
,label="Satellites lost per 10,000"
|
|
)
|
|
|
|
|
|
|
|
#=
|
|
Basic simulations
|
|
1. What happens when both participants always launch a single satellite?
|
|
2. What happens when a single participant always launches a single satellite?
|
|
3. What happens when both participants maintain the cournot equilibrium?
|
|
=#
|
|
|
|
#Specify policies
|
|
|
|
abstract type Policy end
|
|
#=
|
|
Represent any policy as a struct holding parameters that can be called with
|
|
state variables to get the chosen policy.
|
|
=#
|
|
struct ConstantLaunchRatePolicy <: Policy
|
|
#=
|
|
Operator launches fixed number of satellites per turn
|
|
=#
|
|
rate_numerator
|
|
end
|
|
(p::ConstantLaunchRatePolicy)(s,S,D) = p.rate_numerator
|
|
|
|
struct SustainmentPolicy <: Policy
|
|
#=
|
|
Operator launches enough satellites to maintain a certain size
|
|
=#
|
|
level
|
|
end
|
|
(p::SustainmentPolicy)(s,S,D) = max(0,p.level - s)
|
|
|
|
struct simulation_result
|
|
state_path
|
|
profit_path
|
|
destruction_events
|
|
launch_choices
|
|
destruction_probabilities
|
|
end
|
|
|
|
|
|
function simulate_system(policies,starting_state,n)
|
|
#=
|
|
Given a set of policies and a starting state, calculate n steps ahead.
|
|
=#
|
|
|
|
state = fill(starting_state,n)
|
|
profit_path = []
|
|
decay_events=[]
|
|
launch_choices = []
|
|
destruction_probabilities = []
|
|
|
|
for i in 2:n
|
|
current_state = state[i-1]
|
|
S⃗ = current_state[2:end]
|
|
D = current_state[1]
|
|
|
|
#Calculate actions from policies
|
|
X = [policy(s,sum(S⃗),D) for (s,policy) in zip(S⃗,policies)]
|
|
push!(launch_choices, X)
|
|
|
|
#Information generation
|
|
price = inverse_demand_cournot(S⃗,10000)
|
|
push!(profit_path,[profit_function(s,x,4,100,0, 0,price) for (s,x) in zip(S⃗,X) ])
|
|
|
|
#calculate transition to next state
|
|
#loss of satellites
|
|
(Y⃗,prob_distruction) = overall_losses(S⃗, D)
|
|
push!(decay_events,Y⃗)
|
|
push!(destruction_probabilities, prob_distruction)
|
|
|
|
#debris
|
|
new_debris = debris_transition(D, S⃗, Y⃗, X,0.05, )
|
|
|
|
#stocks
|
|
new_stocks = stocks_transition(S⃗,Y⃗,X)
|
|
|
|
#update vector of states
|
|
next_state = [new_debris,new_stocks...]
|
|
|
|
#update vector
|
|
state[i] = next_state
|
|
println(current_state,next_state)
|
|
end
|
|
|
|
k = length(starting_state)
|
|
|
|
return simulation_result(
|
|
[ [v[i] for v in state] for i in 1:k], #states
|
|
[ [v[i] for v in profit_path] for i in 1:3 ], #profit paths
|
|
[ [v[i] for v in decay_events ] for i in 1:3], #counts of satellite distructions
|
|
[ [v[i] for v in launch_choices ] for i in 1:3], #counts of satellite launches
|
|
[ [v[i] for v in destruction_probabilities] for i in 1:3]
|
|
)
|
|
end
|
|
|
|
#setup state
|
|
n=2000
|
|
starting_state = [10.0,0,0,0]
|
|
policies = [SustainmentPolicy(x) for x in 25:5:35]
|
|
|
|
simulation = simulate_system(policies,starting_state,n)
|
|
|
|
|
|
plot(
|
|
simulation.state_path[2:4]
|
|
,labels=["stocks 1" "stocks 2" "Stocks 3"]
|
|
)
|
|
|
|
plot(
|
|
simulation.state_path[1]
|
|
,labels="debris"
|
|
)
|
|
|
|
|
|
plot(
|
|
simulation.state_path[1][3:end] .- simulation.state_path[1][2:end-1]
|
|
,labels="debris growth"
|
|
)
|
|
|
|
plot( simulation.state_path
|
|
,labels=["debris" "stocks 1" "stocks 2" "Stocks 3"]
|
|
)
|
|
|
|
plot(simulation.profit_path)
|
|
|
|
bar(
|
|
simulation.destruction_events
|
|
,layout = grid(3,1)
|
|
,legend=:none
|
|
)
|
|
|
|
plot(
|
|
simulation.launch_choices
|
|
,layout = grid(3,1)
|
|
,legend=:none
|
|
)
|
|
|
|
plot(simulation.destruction_probabilities)
|
|
#= Notes
|
|
With the (maintain equilibrium) approach, the probability
|
|
of distruction rises to 100% within a certain amount of time.
|
|
=#
|
|
|
|
bar(
|
|
[simulation.launch_choices, -1 .* simulation.destruction_events]
|
|
,layout = grid(3,1)
|
|
,ylims = (-25,25)
|
|
#,label = ["Launches 1" "Launches 2" "Launches 3" "Destruction 1" "Destruction 2" "Destruction 3"]
|
|
, legend = :none
|
|
, title = ["Operator 1" "Operator 2" "Operator 3"]
|
|
#,yaxis = (-20:5:20)
|
|
)
|
|
|
|
|
|
#Many simulations
|
|
sims = [simulate_system(policies,starting_state,n) for x in 1:1000]
|
|
|
|
|
|
|
|
state_plots = []
|
|
for sim in sims
|
|
push!(state_plots,plot(sim.state_path[2:end]))
|
|
end
|
|
|
|
state_plots[2]
|
|
|
|
for p in state_plots
|
|
display(p)
|
|
end
|
|
|
|
|
|
plot(sim1.state_path[2:end], color=:skyblue, alpha=0.5)
|
|
|
|
|
|
arr = [sim.state_path[2:end] for sim in sims]
|
|
plot(arr, color=:blue, alpha=0.01, legend=:none)
|
|
|
|
|
|
debarr = [ sim.state_path[1] for sim in sims]
|
|
plot(debarr, color=:blue, alpha=0.05, legend=:none)
|
|
|
|
profitarr = [sim.profit_path for sim in sims]
|
|
plot(profitarr, color=:blue, alpha=0.01, legend=:none)
|
|
|
|
|
|
# Plot out probability of distruction |