Newton-Raphson routine

Hello everyone,

I'm trying to implement a Newton-Raphson routine to solve chemical equilibrium . I found some code for this routine in Matlab and I've translated it into Igor, but at the moment my routine fails to minimize. I've tested the code and everything works well except for the calculation of the shift vector. The shift vector is computed with the lines
MatrixOp /O mDelta = Inv(mJcb)*Diagonal(Cf)
and
  delta_C	= dif_C[p]*mDelta[p][p]*FxC 
. I'm not sure if I've translated this code properly.

I would appreciated if anyone with experience in Matlab and Igor could help me sort this out. For reference, I've posted my code and the code written for Matlab.

I would also appreciated if anyone has suggestions on how to solve this same problem with the FindRoots function in Igor. The mass balance equations that I'm trying to solve have the general form for components [A],[B], and [H] as

At= [A] + k110[A][B] + 2*k210[A]^2[B]
Bt= [B] + k110[A][B] + k210[A]^2[B]+k011[B][H]
Ht= [H] + k011[B][H]

or in matrix form m=

1 0 0 1 2 0
0 1 0 1 1 1
0 0 1 0 0 1

k = {1,1,1,k110,k120,k011}
Ct= {At,Bt,Ht}
fxC={1,1,0} //Constant pH

Here's the code I wrote in Igor


Function NewtonRaphson(m, k , Ct , Ci, fxC, It)

// m		= model in matrix form
// k		= binding constants for species (i.e. K=[HX]/([H][X]) , by definitition K=1 for the free components, (i.e. K= [H]/[H]=1)
// Ct		= total concentration for Components
// Ci		= Initial guess values for Components
// FxC		=wave used to fix concentration of components in Co (i.e. fx={1,1,1,0,1}, fourth component's concentration is fixed)
  		
wave m, k , Ct , Ci, fxC
variable	It

Variable n_comp 	= numpnts (Ct) //number of componets 
Variable n_spec  	= numpnts (k)	 //numer of species
Variable ii = 0
Variable jj,kk=0
	//Check for zero values in Co and replace them with 1e-15
	Ct=Ct[p]==0?1e-15:Ct[p]
	
Duplicate /Free k	 	C_spec, C_Jcb
Duplicate /Free Ct	 	calc_Ct, dif_C, sq_dif, delta_C,absD_C
Duplicate /Free Ci		Cf
Duplicate /Free m		wM						 //Dummy matrix of similar dimentions to model used to perform matrix calculations
Make       /Free /N=(n_comp,n_comp), mJcb, mDelta  //square Jacobian and delta matrix

do
	ii+=1
	//Concentration for species
	wM		= Cf[p]
	wM		= wM^m
	C_spec	= Prod(wM,1)*K
	//Calculated total concentration for components
	wM		= C_spec[q]	
	wM		= m*wM
	MatrixOp /O calc_Ct  = sumRows(wM)
	//Calculate difference between total concentration and calculated concentration for components
	dif_C 	= Ct-calc_Ct
	absD_C	= abs(dif_C)
	
		if (all(absD_C, "<", 1e-15))
			print "small dif"
			return Cf
		else
			//Calculate Jacobian from model matrix and the concentration of species
			for(jj=0;jj<n_comp;jj+=1)
				for (kk=jj;kk<n_comp;kk+=1)
					C_Jcb=m[jj][p]*m[kk][p]*C_spec
					mJcb[jj][kk]=sum(C_Jcb)
					mJcb[kk][jj]=mJcb[jj][kk]
				endfor
			endfor
		endif
	//Calculate shift in concentrations 
	//at the moment the next two lines are not calculating the shift wave properly	
	MatrixOp /O mDelta = Inv(mJcb)*Diagonal(Cf)
	delta_C	= dif_C[p]*mDelta[p][p]*FxC
	Cf		+= delta_C

		if (any(Cf,"<=",0))
			do
				delta_C *=	0.5
				Cf		=	Cf-delta_C
				absD_C	=	abs(delta_C)
				if (all(absD_C, "<", 1e-15))
					break
				endif				
			while (any(Cf,"<=",0))				
		endif
		
while (ii < It)
return Cf
End


And here's the code I found for Matlab:

<br />
function c_spec=NewtonRaphson(Model, beta, c_tot, c,i)<br />
<br />
ncomp=length(c_tot); % number of components<br />
nspec=length(beta); % number of species<br />
c_tot(c_tot==0)=1e-15; % numerical difficulties if c_tot=0<br />
<br />
it=0;<br />
while it<=99<br />
   it=it+1;<br />
   c_spec =beta.*prod(repmat(c',1,nspec).^Model,1); %species conc<br />
   c_tot_calc=sum(Model.*repmat(c_spec,ncomp,1),2)'; %comp ctot calc<br />
   d =c_tot-c_tot_calc; % diff actual and calc total conc<br />
<br />
   if all(abs(d) <1e-15) % return if all diff small<br />
      return<br />
   end<br />
   for j=1:ncomp % Jacobian (J*)<br />
      for k=j:ncomp<br />
         J_s(j,k)=sum(Model(j,:).*Model(k,:).*c_spec);<br />
         J_s(k,j)=J_s(j,k); % J_s is symmetric<br />
      end<br />
   end<br />
   delta_c=(d/J_s)*diag(c); % equation (2.43)<br />
   c=c+delta_c;<br />
<br />
   while any(c <= 0) % take shift back if conc neg.<br />
      delta_c=0.5*delta_c;<br />
      c=c-delta_c;<br />
      if all(abs(delta_c)<1e-15)<br />
         break<br />
      end<br />
   end<br />
end<br />


I also wrote a couple of subroutines for which I couldn't find an equivalent in Igor. They have limited functionality at the moment... I only wrote code for the cases that I needed.

Function all (w, cond , n)
	wave 	w
	string	cond
	variable 	n

	variable 	np 	= numpnts (w)
	variable	ii	= 0
	variable 	boolean=0
		strswitch(cond)	// string switch
			case "<":		
					do	
						if (w[ii] < n)
							boolean = 1
						else
							boolean = 0 
						endif
					  	  ii +=1	 
					while (ii < np && boolean==1)
					return boolean
				break		
			case ">":		
					do	
						if (w[ii] > n)
							boolean = 1
						else
							boolean = 0 
						endif
					  	  ii +=1	 
					while (ii < np && boolean==1)
					return boolean
			break
			default:
				return boolean
		endswitch
return boolean

End

Function any ( w,cond,n)
	wave 	w
	string	cond
	variable 	n

	variable 	np 	= numpnts (w)
	variable	ii	= 0
	variable	boolean = 0
		strswitch(cond)	// string switch
			case "<=":		
					do
						if (w[ii] <= n)
							boolean=1 
						endif
						ii +=1
					while (ii < np && boolean==0)
					return boolean
				break				
			case ">=":
					do
						if (w[ii] >= n)
							boolean = 1 
						endif
						ii +=1								
					while (ii < np && boolean==0)						
					return boolean				
				break
			default:
				return boolean
		endswitch
return boolean
End

Function Prod (w,dim)
wave w
variable dim
variable n  = DimSize (w,dim)
variable ii, jj

Make/O/N=(n)/Free  w1=1
	if (dim==0)
		variable col=	DimSize (w,1)
		for(ii=0; ii<n;ii+=1)
				for(jj=0; jj<col;jj+=1)	
					w1[ii] *= w[ii][jj]
				endfor					
		endfor												

	elseif (dim==1)
	variable row=	DimSize (w,0)
			for(ii=0; ii<n;ii+=1)	
					for(jj=0; jj<row;jj+=1)	
						w1[ii] *= w[jj][ii]
					endfor
			endfor	
	endif
	
return w1

End



thanks John, I'll give it a try. Would it be OK to write the multivariate function like this


Function ChemEq(w, xW, yW)
  Wave w, xW, yW

  yW[0] = w[0][0]*xW[0]+w[0][1]*xW[0]*xW[1]+2*w[0][2]*xW[0]^2*xW[1]	-w[0][4]
  yW[1] = w[1][0]*xW[1]+w[1][1]*xW[0]*xW[1]+   w[1][2]*xW[0]^2*xW[1]+ w[1][3]*xW[1]*xW[2]-w[1][4]
  yW[2] = w[2][0]*xW[2]+ w[2][3]*xW[1]*xW[2]-w[2][4]
End


and trasfer a matrix as the wave parameter of this form

w^t=

1, k110, k210, 0, At
1, k110, k210, k011, Bt
1, 0, 0, k011, Ht

I tried using FindRoots on the previous function but the values I obtained for the roots have no physical meaning.

IH







iherrera wrote:
I tried using FindRoots on the previous function but the values I obtained for the roots have no physical meaning.


Disclaimer: I haven't looked at your code. However, when I look at the equations that you mention in your original post:
iherrera wrote:
At= [A] + k110[A][B] + 2*k210[A]^2[B]
Bt= [B] + k110[A][B] + k210[A]^2[B]+k011[B][H]
Ht= [H] + k011[B][H]

I'm assuming that these are rate equations, and that "At" really means "the derivative of the concentration of species A with respect to time".

If that is the case then your equations seem to describe a system in which mass is being created out of nowhere. Typically each growth term, e.g. k110[A][B], should show up with a positive sign in one equation and a negative sign in another one. However, that is not the case in your equations.

I may be misunderstanding your intentions. But it looks odd to me.
iherrera wrote: and trasfer a matrix as the wave parameter of this form

Hm... Are you finding roots of a system of equations, or trying to optimize a function? Or maybe you talked about optimizing, because you were trying to roll your own root finder?

In either case, actually, the pWave parameter is a wave that Igor passes along to your function intact without examination. It can be anything you want it to be as long as you interpret it correctly in your function.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com
741 wrote:
I'm assuming that these are rate equations, and that "At" really means "the derivative of the concentration of species A with respect to time".


These equations are mass balance equations for a titration experiment. This means that A(total) represents the sum of the concentrations for the species that contain component A in their formula. For example, the mass balance equations for a system where component A forms complexes with B of type [AB] and [A2B] are defined as:

At= [A]+[AB]+2*[A2B]
Bt= [B]+[AB]+[A2B]

Then, I can express the concentration of [AB] and [A2B] in terms of the equilibrium constants (K) and the concentration of the free components [A] and [B] as:

[AB]= K110[A][B]
[A2B]=K210[A]^2[B]

which leads to the mass balance equations that I wrote before. In that particular system B also coordinates with H, so I added an extra term in Bt and also an additional mass balance equation for Ht.








johnweeks wrote:
Hm... Are you finding roots of a system of equations, or trying to optimize a function? Or maybe you talked about optimizing, because you were trying to roll your own root finder?


I am looking for the roots to this system of equations

[A] + k110[A][B] + 2*k210[A]^2[B]- At=0
[B] + k110[A][B] + k210[A]^2[B]+k011[B][H]-Bt=0
[H] + k011[B][H] - Ht =0

which would be the same as writing the polynomials

x + k110xy+2*k210x^2y- At =0
y + k110xy + k210x^2y+k011yz-Bt = 0
z + k011yz-Ht=0

where x=[A], y=[B], and z=[H]. I tried to create my own root finder function because it is very practical to write the chemical equilibrium in matrix form together with a vector that contains the binding constants {1,1,1,k110,k210,k011} and a second vector that contains the total concentration of the components {At,Bt,Ht} (independent variables of my experiments).

At the moment, the roots that I've obtained with FindRoots have no physical meaning because [A] is much larger than At.
iherrera wrote: At the moment, the roots that I've obtained with FindRoots have no physical meaning because [A] is much larger than At.

That sounds like a mathematical problem- your system is poorly defined because one component is small.

FindRoots is based on code written by smarter people that me- it has lots of attention paid to things like trying to do the arithmetic in ways that minimize floating-point truncation. Perhaps your system is nearly singular- that would mean you simply can't solve it as it stands, no matter whose code you use. It might be that you need to use one system at the start when the products are at very low concentration, and another at the end where the products are significant.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com
iherrera wrote:
741 wrote:
I'm assuming that these are rate equations, and that "At" really means "the derivative of the concentration of species A with respect to time".


These equations are mass balance equations for a titration experiment. This means that A(total) represents the sum of the concentrations for the species that contain component A in their formula. For example, the mass balance equations for a system where component A forms complexes with B of type [AB] and [A2B] are defined as:

At= [A]+[AB]+2*[A2B]
Bt= [B]+[AB]+[A2B]

Then, I can express the concentration of [AB] and [A2B] in terms of the equilibrium constants (K) and the concentration of the free components [A] and [B] as:

[AB]= K110[A][B]
[A2B]=K210[A]^2[B]

which leads to the mass balance equations that I wrote before. In that particular system B also coordinates with H, so I added an extra term in Bt and also an additional mass balance equation for Ht.



I believe there is nothing wrong with the solver.
There is one missing term in Bt (total concentration of B) that is [BH]
Bt= [B]+[BH]+[AB]+[A2B]

In addition, equation 4 doesn't makes much sense ; [A2B]=K210[A]^2[B]
because it is equivalent to three body collision, but lets assume it holds for the moment.
It should be [A2B]=K210[A][AB], a two body reaction between AB and A

Prof. V. Calvo-Perez
Universidad de Chile