## 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{res} = }  __osc_residual_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_residual_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);
  G = data.G(t,x); 

  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); 

  adot = ydot(1:N)*sc(1);
  bdot = ydot([1:N]+N)*sc(2);
  cdot = ydot([1:N]+N*2)*sc(3);
  ddot = ydot([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, 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);




  retvala = Aa * a + M * adot + M1 * a.* b - M2 * c ;
  retvala(1)   += - g_a(1);  
  retvala(end) += - g_a(2);

  retvalb = Ab * b + M * bdot + M1 * a.* b - M2 * c;
  retvalb(1)   += - g_b(1);  
  retvalb(end) += - g_b(2);

  retvalc = M * (cdot + k3 * c - G) - M1 * a .* b + M2 * c;

  retvald = Ad(2:end-1,2:end-1) * d + Ad(2:end-1,[1 end]) * d0gamma +...
            M(2:end-1,:) *  (k4 * a - k5 * b);


  retval = [retvala/scl(1);retvalb/scl(2);retvalc/scl(3);retvald/scl(4)];

  __osc_default_outfn__ (a, b, c, d, retvala, retvalb, retvalc,  ...
  			 retvald, x, t, sc, dgrad);
endfunction

function __osc_default_outfn__ (a, b, c, d, retvala, retvalb,  ...
				retvalc, retvald, x, t, sc, dgrad)

  persistent tmax = 0
  if (t>tmax)
    tmax = t;
  endif
  
  h = diff(x);

  clc
  am = sum ((a(2:end)+a(1:end-1)).*h) / (2*sum(h));
  bm = sum ((b(2:end)+b(1:end-1)).*h) / (2*sum(h)); 
  cm = sum ((c(2:end)+c(1:end-1)).*h) / (2*sum(h)); 
  printf("tmax=%g t=%g mean(a)=%g mean(b)=%g mean(c)=%g\n", tmax, t, am, bm, cm); 
  printf(" r_a=%g r_b=%g r_c=%g r_d=%g\n", 
	 norm(retvala), norm(retvalb), norm(retvalc), norm(retvald))

  return 

  figure(1)
  plot(x,a/sc(1),x,b/sc(2),x,c/sc(3))
  figure(2)
  plot(x(2:end-1), d)
  figure(3)
  plot(x (1:end-1), -dgrad); 
  drawnow

endfunction

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