2 Insoluble plus 1 Soluble species Equilibrium

This program checks expected equilibrium concentrations for a two insoluble (a,b) plus one common soluble (c) species undergoing simultaneous equilibration.  Subscripts 1 refer to the  a/c  rate constanats and 2 to the  b/c  constants. Parameters such as az (azero) represent initial concentrations.   The red parameters are things that one might wish to change. Note that NMinimize produces 3 results (minimum value of function, then first and second variable values).  The last 2 of these are the desired values of ac and bc.  This explains the 2 evaluate statements that follow NMinimize. 

See the program 2 solubles plus 2 insolubles for comments about the need for extra constraints to permit proper convergence.  Initially, it seemed that they were not needed in this simple case, but certain combinations of constants during evaluation produced problems that were solved only by adding con1a, con2a. Ironically, it seems more difficult to converge on a correct value of an equilibrium this way than it is by solving the more difficult differential equations.

az  =  1;bz  =  1;cz  =  1;kfr1 = 1;krr1 =  .01;kfr2 = 1;krr2 = 0.1;subs3 = Dispatch[{ azero  ->  az, bzero  -> bz, czero  -> cz, kf1  ->  kfr1, kr1  ->  krr1,kf2  -> kfr2,kr2  ->  krr2}];
subs  =  
        Dispatch[{a  ->  azero  -  ac,  b  ->  bzero  -  bc, 
            c  -> czero - ac - bc}] ;
eq3  =  
        Abs[a*c*kf1 - ac*kr1] + Abs[b*c*kf2 - bc*kr2];
eq4 =  
        eq3  /. subs;
eq5  =  
        eq4  /. subs3;
con1  = (a*c*kf1) == (ac*
                kr1)  /.  subs;
con1a  =  
        con1  /.  subs3;
con2  =  (b*c*
                kf2) == (bc*kr2)  /.  
          subs;
con2a  =  
        con2  /.  subs3;
x  =  
        NMinimize[{eq5 , ac + bc ≤ cz, az - ac ≥ 0, 
            bz - bc ≥ 0,  ac ≥ 0.00001, 
            bc ≥ 0.00001, con1a, con2a}, {ac, 
            bc}];
y = 
        Evaluate[
            ac] /. Flatten[x][[2]];
z = 
        Evaluate[
            bc] /. Flatten[
              x][[3]];
Print["    ac  ,   \
 bc   ,   a   ,    b    ,    c"] ;
Print[
        N[{y, z, az - y, bz - z, 
            cz - y - 
              z}]];
Print[];
Print["for checking: re-calculated equilibrium \
consts. are:"];
Print[
      y/((az - y)*(cz - y - z)), ", ",  
      z/((bz - z)*(cz - y - z))]; 
    Print["Residuals are:", 
      N[ (az - y)*(cz - y - z)*kfr1 - y*krr1], ",  ", 
      N[(bz - z)*(cz - y - z)*kfr2  -  z*krr2]]

Correct equilibrium constants recovered;
Residual results are very small;  
    Apparently this calculation process worked just fine.

Here is a simple statement to show what happens in Evaluate:

zz  =  Evaluate[yy] /. (yy -> 1); Print [zz]

2 soluble plus 2 immobilized species
                   (with interference from opposite species)

This program finds expected equilibrium concentrations for a two soluble (c1,c2) plus two common immobilized (a,b) species undergoing simultaneous equilibration.  Subscripts 1 refer to the  a/c1  rate constants, 2 to the  b/c1  constants, 3 to the a/c2 constants and 4 to the b/c2 constants. Parameters such as c1z (c1zero) represent initial concentrations.   The red parameters are things that one might wish to change. 

c1*a*kf1 = kr1*c1a     kf1 = kf4; kr1 = kr4
c1*b*kf2 = kr2*c1b     kf2 = kf3; kr2 = kr3
c2*a*kf3 = kr3*c2a
c2*b*kf4 = kr4*c2b

In this version with the example constants shown, c1 is assumed to bind  strongly to a and weakly to b. The opposite is true of c2 (it binds strongly to b and weakly to a). The variables ending with z represent initial concentration values. The values kfr represent forward rate constants and krr represent reverse rate constants. Two new constraints are needed: c1a=c2b and c2a=c1b.  This provides reciprocity of binding interactions.

c1z  =  1; c2z  =  1; caz  =  1; cbz  =  1; 
kfr1 = 1; krr1 = 0.01; kfr2 = 1; krr2 = 0.1;
subs3 = Dispatch[{ c1zero  ->  c1z,c2zero  -> c2z, cazero  -> caz, cbzero = cbz; 
                  kf1  ->  kfr1, kr1  ->  krr1,kf2  -> kfr2,kr2  ->  krr2}];
subs  =  
            Dispatch[{c1  ->  c1zero  -  c1a - c1b,  
                c2  ->  c2zero  -  c2a - c2b, 
                ca -> cazero - c1a - c2a,  
                cb  -> cbzero - c1b - c2b}] ;
eq3  = ( 
              Abs[(c1*ca*kf1) - (c1a*kr1)] + 
                Abs[(c2*cb*kf1) - (c2b*
                        kr1)] + 
Abs[(c1*cb*
                        kf2) - (c1b*kr2)] + 
                Abs[(c2*ca*kf2) - (c2a*kr2)]);
eq4 =  eq3  /. subs;
eq5  =  eq4  /. subs3;
con1  =  (c1*ca*kf1) == (c1a*kr1) /. subs;
con1a  =  con1  /. subs3;
con2  =  (c2*cb*kf1) == (c2b*kr1) /. 
              subs;
con2a  =  con2 /. subs3;
con3  =  (c1*cb*kf2) == (c1b*kr2) /. 
              subs;
con3a  =  con3 /. subs3;
con4  =  (c2*ca*kf2) == (c2a*kr2) /. 
              subs;
con4a = con4 /. subs3;
x  =  
            NMinimize[{eq5 , c1a + c1b ≤ c1z, 
                c2a + c2b ≤ c2z, c1a + c2a ≤ caz,  
                c1b + c2b ≤ cbz, c1a ≥ 0, 
                c2a ≥ 0, c1b ≥ 0, 
                c2b ≥ 0, con1a, con2a, con3a, con4a}, {c1a, 
                c2a, c2b, c1b, }];
y = Evaluate[c1a] /. Flatten[x][[2]];
z = Evaluate[c1b] /. Flatten[x][[3]];
w = Evaluate[c2a] /. Flatten[x][[4]];
v = Evaluate[c2b] /. Flatten[x][[5]];
Print[];
Print["    c1a  ,    c2b   ,   c1b   ,  c2a"];
Print[N[{y, v, z, w}]];
Print[];
Print["    c1   ,    c2    ,    ca    ,    cb   "] ;
Print[
          N[{c1z - y - z, c2z - w - v,  caz - y - w, cbz - z - v}]]; 
        Print[];
Print["for checking: re-calculated equilibrium consts. are \
(for c1a,c2b):"];
Print[
            y/((caz - y - w)*(c1z - y - z)), ", ",  
            v/((c2z - w - v)*(cbz - v - z))];
Print[];
Print["for checking: re-calculated equilibrium consts. are \
(for c2a,c1b):"];
Print[w/((caz - y - w)*(c2z - w - v)), ", ", 
            z/((c1z - y - z)*(cbz - v - z))];
Print[];
Print["Residual is  ", Flatten[
                x][[1]]];

The following shows what the individual residues are:

Print[
      N[Abs[(c1z - y - z)*(caz - y - w)*kfr1 - y*krr1]]];  
    Print[N[Abs[(c2z - w - v)*(cbz - z - v)*kfr2 - v*krr2]]]; 
    Print[N[Abs[(c1z - y - z)*(cbz - z - v)*kfr1 - z*krr1]]];  
    Print[N[Abs[(c2z - w - v)*(caz - y - w)*kfr2 - 
            w*krr2]]]

For kr1,kr4 reverse rate constants = 0.01, the above solution did not converge at all for c1z = 0.01, c2z = 0.001 (table 3, line5).  It did not quite converge for c1z=1, c2z=0.1 (table 3, line 2).
    
For kr1, kr4 = 0.001, the above solution did not converge at all for c1z=1, c2z=0.1 or for c1z=0.1, c2z=0.01 (table 3, lines 7 and 9).  It did not quite converge for c1z=1, c2z=0.001 (table 3, line 11).

For these cases, the differential equation solution will be checked below by substituting in the original equilibrium relationships for t = 10,000.  The numerical results inserted below were obtained from the previous numerical solution of the differential equation.  Appropriate initial concentrations were used.  The expected equilibrium constants were obtained in all cases.

table 3 line 2 :

c2b  =  0.09527215079821393`;
c1b  =  0.2146742440729666`;
c2a  = 0.003347200653758734`;
c1a  =  0.7542159635170813`;
c1  =  1 - c1a - c1b;  
    c2  =  0.1 - c2a - c2b;
ca = 1 - c1a - c2a;  cb = 1 - c1b - c2b;
Print[{c1a/(c1*ca),  c2a/(c2*ca),  
        c1b/(c1*cb), 
c2b/(c2*
              cb)}]

Above result checks exactly . 
table 3 line 5

c2b  =  0.0009014787814633593`;
c1b  =  0.0009068473246019454`;
c2a  = 0.00008949009951371099`;
c1a  =  0.00900230365828851`;
c1  =  0.01 - c1a - c1b;  
    c2  = 0.001 - c2a - c2b;
ca = 1 - c1a - c2a;  cb = 1 - c1b - c2b;
Print[{c1a/(c1*ca),  c2a/(c2*ca),  
        c1b/(c1*cb), 
c2b/(c2*cb)}]

Above result checks exactly . 
table 3 line 7

c2b  =  0.0997668871085963`;
c1b  =  0.0814707985849157`;
c2a  = 0.00011126203754130437`;
c1a  =  0.908578719185832`;
c1  =  1 - c1a - c1b;  
    c2  =  0.1 - c2a - c2b;
ca = 1 - c1a - c2a;  cb = 1 - c1b - c2b;
Print[{c1a/(c1*ca),  c2a/(c2*ca),  
        c1b/(c1*cb), 
c2b/(c2*cb)}]

Above result checks exactly . 
table 3 line 9

c2b  =  0.009899792086356158`;
c1b  =  0.0010844541576428477`;
c2a  = 0.00009019817209143551`;
c1a  =  0.09880589600578737`;
c1  =  0.1 - c1a - c1b;  
    c2  =  0.01 - c2a - c2b;
ca = 1 - c1a - c2a;  cb = 1 - c1b - c2b;
Print[{c1a/(c1*ca),  c2a/(c2*ca),  
        c1b/(c1*cb), 
c2b/(c2*cb)}]

Above result checks exactly . 
table 3 line 11

c2b  =  0.0009978605370999458`;
c1b  =  0.08625016963476856`;
c2a  = 1.0462189848558453`*^-6;
c1a  =  0.9043003662735779`;
c1  =  1 - c1a - c1b;  
    c2  =  0.001 - c2a - c2b;
ca = 1 - c1a - c2a;  cb = 1 - c1b - c2b;
Print[{c1a/(c1*ca),  c2a/(c2*ca),  
        c1b/(c1*cb), 
c2b/(c2*cb)}]

Above result checks exactly.