## Copyright (C) 2009 Carlo de Falco <carlo DOT defalco AT gmail DOT com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; If not, see <http://www.gnu.org/licenses/>.


## -*- texinfo -*-
## @deftypefn {Function File} {@var{jac} = }  __osc_jacobian_tpl__ @ (@var{y},@
## @var{ydot}, @var{t}, @var{N}, @var{d0gamma}, @var{Da},@
## @var{Db}, @var{Dd}, @var{k1}, @var{k2}, @var{k3}, @var{k4}, @var{k5},@
## @var{Ca}, @var{Cb}, @var{a_a}, @var{b_a}, @var{g_a}, @var{a_b},@
## @var{b_b}, @var{g_b}, @var{G}, @var{x}, @var{h}, @var{Ad}, @var{M},@
## @var{sc}, @var{scl}) 
##
## Undocumented internal function.
## @end deftypefn


function retval = __osc_jacobian_tpl__ (data, y, ydot, t)

  ## FIXME unneeded code duplication!!
  persistent e0 = physical_constant("electric constant");
  persistent q  = physical_constant("elementary charge");
  persistent Kb = physical_constant("boltzmann constant");
    
  N  = data.Nx;
  x  = data.x;
  sc = data.sc;
  scl= data.scl;
  T  = data.temp;
  d0gamma = data.d0([1 end]) * sc(4);

  a = y(1:N)*sc(1);
  b = y([1:N]+N)*sc(2);
  c = y([1:N]+N*2)*sc(3);
  d = y([1:N-2]+3*N)*sc(4); 
  
  am = (a(2:end)+a(1:end-1))/2;
  bm = (b(2:end)+b(1:end-1))/2;
  
  h     = diff(x);
  dgrad = diff([d0gamma(1);d;d0gamma(2)])./h;

  Ca  =  data.un (data, -dgrad);
  Cb  = -data.up (data, -dgrad);

  Da  =  Ca * Kb*T/q;
  Db  = -Cb * Kb*T/q;
  Dd  =  data.er*e0;

  k1  = data.gamma(data, -dgrad);
  k2  = data.kdiss(data, -dgrad);
  k3  = data.krec;
  k4  = k5 = q;

  M  = spdiag(([0;h]+[h;0])/2);
  M1 = spdiag(([0;k1.*h]+[k1.*h;0])/2);
  M2 = spdiag(([0;k2.*h]+[k2.*h;0])/2);

  Ad  = bim1a_advection_diffusion (x, Dd, ones(size(x)), ones(size(x)), 0);
  E     = Ad([1 end],:) * [d0gamma(1);d;d0gamma(2)] ./ Dd;

  abg = data.bccoeffs(data, E);
  a_a = abg(1,:);
  b_a = abg(2,:);
  g_a = abg(3,:);
  a_b = abg(4,:);
  b_b = abg(5,:);
  g_b = abg(6,:);

  Aa = bim1a_advection_diffusion (x, Da .* ones(size(h)), ones(size(x)),  ...
		     ones(size(x)), Ca .* dgrad./Da);

  Aa(1,:) *= b_a(1);   Aa(end,:)   *= b_a(2);
  Aa(1,1) += a_a(1);   Aa(end,end) += a_a(2);

  Aad = bim1a_advection_diffusion (x, Ca .* am, ones(size(x)), ones(size(x)), 0);

  Ab  = bim1a_advection_diffusion (x, Db .* ones(size(h)), ones(size(x)),  ...
		     ones(size(x)), Cb .* dgrad./Db);

  Ab(1,:) *= b_b(1);   Ab(end,:)   *= b_b(2);
  Ab(1,1) += a_b(1);   Ab(end,end) += a_b(2);

  Abd = bim1a_advection_diffusion (x, Cb .* bm, ones(size(x)), ones(size(x)), 0);



  aa = Aa + M1 .* spdiag(b);
  ab = M1 .* spdiag(a); 
  ac = M2;
  ad = Aad(:,2:end-1);

  ba = M1 .* spdiag(b); 
  bb = Ab + M1 .* spdiag(a);
  bc = M2;
  bd = Abd(:,2:end-1);

  ca = M1.*spdiag(b);
  cb = M1.*spdiag(a);
  cc = (k3 * M + M2);
  cd = zeros (length(c), length(d));

  da =  k4*M(2:end-1,:);
  db = -k5*M(2:end-1,:); 
  dc =  zeros (length(d), length(c));
  dd =  Ad(2:end-1,2:end-1);

  retval = [[aa*sc(1),ab*sc(2),ac*sc(3),ad*sc(4)]/scl(1);
	    [ba*sc(1),bb*sc(2),bc*sc(3),bd*sc(4)]/scl(2);
	    [ca*sc(1),cb*sc(2),cc*sc(3),cd*sc(4)]/scl(3);
	    [da*sc(1),db*sc(2),dc*sc(3),dd*sc(4)]/scl(4)];

endfunction

function M = spdiag (v)
  M = spdiags (v, 0, numel(v), numel(v));
endfunction
