#!/usr/bin/env ruby
# Self-similar solution of the Sedev-Taylor equation,
# which describes spherically expanding gas
# triggered by a supernova explosion.

require("gsl")
include Math

# Sedov-Taylor equation
#     logx: (log of) the radial coordinate in the gas shpere, x = r/R.
#           logx = 0 at the surface, and negative within the shell
#        y: GSL::GSL::Vector::View::ReadOnly
#           y[0]: gas velocity at the radius
#           y[1]: density
#           y[2]: pressure
#  dydlogx: GSL::GSL::Vector::View, derivatives
#       sh: specific heat of the gas (5/3)
Sedov = Proc.new { |logx, y, dydlogx, sh|
  a = -5.0*(6.0*y[2] - 15.0*y[2]*y[0]*sh + 2.0*y[0]*y[1] - 7.0*y[0]*y[0]*y[1] + 5.0*GSL::pow_3(y[0])*y[1])
  b = -25.0*y[2]*sh + 4.0*y[1] - 20.0*y[0]*y[1] + 25.0*y[0]*y[0]*y[1]
  dydlogx[0] = a/b             

  bb = -5.0*(6.0*y[2] - 15.0*y[2]*y[0]*sh + 2.0*y[0]*y[1] - 7.0*y[0]*y[0]*y[1])

  a = -5.0*(-30.0*y[2]*y[1] + 2.0*y[0]*y[1]*y[1] - 25.0*GSL::pow_2(y[0]*y[1]) + 50.0*GSL::pow_3(y[0])*GSL::pow_2(y[1]))
  bb = (-2.0 + 5.0*y[0])*(-25.0*y[2]*sh + 4.0*y[1] - 20.0*y[0]*y[1] + 25.0*y[0]*y[0]*y[1])
  dydlogx[1] = a/bb       

  a = -5.0*y[2]*(-10.0*y[2]*sh + 4.0*y[1] - 14.0*y[0]*y[1] + 10.0*y[0]*y[0]*y[1] - y[0]*sh*y[1] + 10.0*y[0]*y[0]*sh*y[1])
  dydlogx[2] = a/b             
}

DIM = 3
sh = 5.0/3.0
solver = GSL::Odeiv::Solver.alloc(GSL::Odeiv::Step::RKF45, [1e-5, 1e-5], Sedov, nil, DIM)
solver.set_params(sh)

def boundary_condition(sh)
  delta = 2.0/5.0
  xx = 1
  y = GSL::Vector[DIM]
  y[0] = 2.0/(sh + 1.0)*delta
  y[1] = (sh + 1.0)/(sh - 1.0)
  y[2] = 2.0/(sh + 1.0)*delta*delta
  y00 = xx*y[0]
  y10 = y[1]
  y20 = xx*xx*y[2]
  c = adiabatic_integ(xx, y, sh);
  return y, y00, y10, y20, c
end

def adiabatic_integ(xx, y, sh)
  GSL::pow(y[1], 1.0 - sh)*y[2]*(y[0] - 2.0/5.0)*GSL::pow_5(xx)
end

# y: GSL::Vector (velocity, density, pressure)
# y00, y10, y20: initial values
# c: adiabatic integral (this must be constant throughout the computation)
y, y00, y10, y20, c = boundary_condition(sh)

# x: the radial coordinates in the shell, x = r/R.
#     x = 1 at the surface (r = R), and zero at the center (r = 0)
x = 1
logx = log(x)
logxend = log(1e-5)
h = -0.000001

N = 150
R = GSL::Vector[N]
V = GSL::Vector[N]
RHO = GSL::Vector[N]
PRESS = GSL::Vector[N]

# The values are normalized to [0, 1]
R[0] = x
V[0] = x*y[0]/y00
RHO[0] = y[1]/y10
PRESS[0] = x*x*y[2]/y20

GSL::ieee_env_setup()

n = 1
while logx > logxend
  logx, h, status = solver.apply(logx, logxend, h, y)
  break if status != GSL::SUCCESS
  break if y[0] < 0.0
  x = exp(logx)
  c = adiabatic_integ(x, y, sh);
  R[n] = x
  V[n] = x*y[0]/y00
  RHO[n] = y[1]/y10
  PRESS[n] = x*x*y[2]/y20
  n += 1
end

GSL::graph(R.subvector(n), V.subvector(n), RHO.subvector(n), PRESS.subvector(n),
      "-T X -C -g 3 -X 'x = r/R' -S 4 -L 'red: v, green: rho, blue: p'")
