iphreeqc/model.cpp
David L Parkhurst 60a1544019 Copying new classes (cxx) and cpp files to src
Will remove cpp and header files and make phreeqc an external directory.




git-svn-id: svn://136.177.114.72/svn_GW/phreeqcpp/trunk@785 1feff8c3-07ed-0310-ac33-dd36852eb9cd
2006-02-16 00:21:39 +00:00

3103 lines
100 KiB
C++

#define EXTERNAL extern
#include "global.h"
#include "phqalloc.h"
#include "output.h"
#include "phrqproto.h"
static char const svnid[] = "$Id: model.c 632 2005-11-08 17:59:00Z dlpark $";
static LDBLE s_s_root(LDBLE a0, LDBLE a1, LDBLE kc, LDBLE kb, LDBLE xcaq, LDBLE xbaq);
static LDBLE s_s_halve(LDBLE a0, LDBLE a1, LDBLE x0, LDBLE x1, LDBLE kc, LDBLE kb, LDBLE xcaq, LDBLE xbaq);
static LDBLE s_s_f(LDBLE xb, LDBLE a0, LDBLE a1, LDBLE kc, LDBLE kb, LDBLE xcaq, LDBLE xbaq);
#ifdef SKIP
static int adjust_step_size(void);
#endif
/*#define SLNQ*/
#ifdef SLNQ
static int add_trivial_eqns(int rows, int cols, LDBLE *matrix);
extern int slnq (int n, LDBLE *a, LDBLE *delta, int ncols, int print);
#endif
static int calc_gas_pressures(void);
static int calc_s_s_fractions(void);
static int gammas (LDBLE mu);
static int initial_guesses(void);
static int revise_guesses(void);
static int s_s_binary(struct s_s *s_s_ptr);
static int s_s_ideal(struct s_s *s_s_ptr);
static int remove_unstable_phases;
static int gas_in;
LDBLE min_value = 1e-10;
/* ---------------------------------------------------------------------- */
int model(void)
/* ---------------------------------------------------------------------- */
{
/*
* model is called after the equations have been set up by prep
* and initial guesses have been made in set.
*
* Here is the outline of the calculation sequence:
* residuals--residuals are calculated, if small we are done
* sum_jacobian--jacobian is calculated
* ineq--inequality solver is called
* reset--estimates of unknowns revised, if changes are small solution
* has been found, usually convergence is found in residuals.
* gammas--new activity coefficients
* molalities--calculate molalities
* mb_sums--calculate mass-balance sums
* mb_gases--decide if gas_phase exists
* mb_s_s--decide if solid_solutions exists
* switch_bases--check to see if new basis species is needed
* reprep--rewrite equations with new basis species if needed
* revise_guesses--revise unknowns to get initial mole balance
* check_residuals--check convergence one last time
* sum_species--calculate sums of elements from species concentrations
*
* An additional pass through may be needed if unstable phases still exist
* in the phase assemblage.
*/
int kode, return_kode;
int r;
int count_infeasible, count_basis_change;
int debug_model_save;
int mass_water_switch_save;
if (svnid == NULL) fprintf(stderr," ");
/* debug_model = TRUE; */
/* debug_prep = TRUE; */
/* debug_set = TRUE; */
/* mass_water_switch == TRUE, mass of water is constant */
if (pitzer_model == TRUE) return model_pz();
mass_water_switch_save = mass_water_switch;
if (mass_water_switch_save == FALSE && delay_mass_water == TRUE) {
mass_water_switch = TRUE;
}
debug_model_save = debug_model;
pe_step_size_now = pe_step_size;
step_size_now = step_size;
status(0, NULL);
iterations=0;
count_basis_change = count_infeasible = 0;
stop_program = FALSE;
remove_unstable_phases = FALSE;
for (; ; ) {
mb_gases();
mb_s_s();
kode = 1;
while ( ( r = residuals() ) != CONVERGED || remove_unstable_phases == TRUE) {
#if defined(PHREEQCI_GUI)
if (WaitForSingleObject(g_hKill /*g_eventKill*/, 0) == WAIT_OBJECT_0)
{
error_msg("Execution canceled by user.", CONTINUE);
RaiseException(USER_CANCELED_RUN, 0, 0, NULL);
}
#endif
iterations++;
if (iterations > itmax - 1 && debug_model == FALSE && pr.logfile == TRUE) {
set_forward_output_to_log(TRUE);
debug_model = TRUE;
}
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"\nIteration %d\tStep_size = %f\n",
iterations, (double) step_size_now);
output_msg(OUTPUT_MESSAGE,"\t\tPe_step_size = %f\n\n", (double) pe_step_size_now);
}
/*
* Iterations exceeded
*/
if (iterations > itmax ) {
sprintf(error_string,"Maximum iterations exceeded, %d\n",itmax);
warning_msg(error_string);
stop_program = TRUE;
break;
}
/*
* Calculate jacobian
*/
jacobian_sums();
/*
* Full matrix with pure phases
*/
if ( r == OK || remove_unstable_phases == TRUE) {
return_kode = ineq(kode);
if ( return_kode != OK ) {
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE, "Ineq had infeasible solution, "
"kode %d, iteration %d\n",
return_kode, iterations);
}
output_msg(OUTPUT_LOG, "Ineq had infeasible solution, "
"kode %d, iteration %d\n", return_kode, iterations);
count_infeasible++;
}
if ( return_kode == 2 ) {
ineq(0);
}
reset();
}
gammas(mu_x);
if (molalities(FALSE) == ERROR) {
revise_guesses();
/* adjust_step_size(); */
}
if(use.surface_ptr != NULL &&
use.surface_ptr->diffuse_layer == TRUE &&
use.surface_ptr->related_phases == TRUE)
initial_surface_water();
mb_sums();
mb_gases();
mb_s_s();
/*
* Switch bases if necessary
*/
if (switch_bases() == TRUE ) {
count_basis_change++;
reprep();
gammas(mu_x);
molalities(TRUE);
if(use.surface_ptr != NULL &&
use.surface_ptr->diffuse_layer == TRUE &&
use.surface_ptr->related_phases == TRUE)
initial_surface_water();
revise_guesses();
mb_sums();
mb_gases();
mb_s_s();
}
/* debug
species_list_sort();
sum_species();
print_species();
print_exchange();
print_surface();
*/
if (stop_program == TRUE) {
break;
}
}
/*
* Check for stop_program
*/
if (stop_program == TRUE) {
break;
}
if (check_residuals() == ERROR) {
stop_program = TRUE;
break;
}
if (remove_unstable_phases == FALSE && mass_water_switch_save == FALSE &&
mass_water_switch == TRUE) {
output_msg(OUTPUT_LOG,"\nChanging water switch to FALSE. Iteration %d.\n", iterations);
mass_water_switch = FALSE;
continue;
}
if (remove_unstable_phases == FALSE) break;
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"\nRemoving unstable phases. Iteration %d.\n", iterations);
}
output_msg(OUTPUT_LOG,"\nRemoving unstable phases. Iteration %d.\n", iterations);
}
output_msg(OUTPUT_LOG,"\nNumber of infeasible solutions: %d\n",count_infeasible);
output_msg(OUTPUT_LOG,"Number of basis changes: %d\n\n",count_basis_change);
output_msg(OUTPUT_LOG,"Number of iterations: %d\n\n", iterations);
debug_model = debug_model_save;
set_forward_output_to_log(FALSE);
if (stop_program == TRUE) {
return(ERROR);
}
return(OK);
}
#ifdef SKIP
/* ---------------------------------------------------------------------- */
int adjust_step_size(void)
/* ---------------------------------------------------------------------- */
{
/*
* Step sizes are cut down if overflow occurs in molalities
*/
pe_step_size_now -= (pe_step_size_now - 1.0) / 2.0;
step_size_now -= (step_size_now - 1.) / 2.0;
if (pe_step_size_now < 1.5) pe_step_size_now = 1.5;
if (step_size_now < 1.5) step_size_now = 1.5;
output_msg(OUTPUT_LOG,"\tNew step sizes: %f\t%f\t%d\n",
step_size_now, pe_step_size_now, iterations);
return(OK);
}
#endif
/* ---------------------------------------------------------------------- */
int check_residuals(void)
/* ---------------------------------------------------------------------- */
{
/*
* Checks for convergence of all equations, prints any nonconvergence
* Sets variable remove_unstable_phases if a phase is present,
* but undersaturated (i.e. aragonite in calcite-saturated solution).
*/
int i, return_value;
LDBLE epsilon;
#ifdef SKIP
if (punch.high_precision == FALSE)
epsilon = 1e-8;
else
epsilon = 1.e-12;
#endif
epsilon = convergence_tolerance;
return_value = OK;
if (stop_program == TRUE) {
warning_msg("The program has failed to converge to a numerical solution.\n\nThe following equations were not satisfied:");
/*error_msg("The program has failed to converge to a numerical solution.\n\nThe following equations were not satisfied:", CONTINUE);*/
}
for (i=0; i < count_unknowns; i++) {
if (x[i]->type == MB || x[i]->type == ALK) {
if ( fabs(residual[i]) >= epsilon*x[i]->moles && x[i]->moles > MIN_TOTAL /* || stop_program == TRUE */) {
#ifdef SKIP
if ( fabs(residual[i]) >= epsilon*x[i]->moles /* || stop_program == TRUE */) {
#endif
sprintf(error_string,"%20s has not converged. Total: %e\tCalculated: "
"%e\tResidual: %e\n", x[i]->description, (double) x[i]->moles,
(double) x[i]->f, (double) residual[i]);
error_msg(error_string, CONTINUE);
if (x[i]->type == ALK) {
error_msg("Is non-carbonate alkalinity "
"greater than total alkalinity?\n", CONTINUE);
}
return_value = ERROR;
}
} else if (x[i]->type == SOLUTION_PHASE_BOUNDARY) {
if (fabs (residual[i]) >= epsilon /* || stop_program == TRUE */) {
sprintf(error_string,"%20s solution phase boundary has not converged. "
"\tResidual: %e\n", x[i]->description, (double) residual[i]);
error_msg(error_string, CONTINUE);
}
} else if (x[i]->type == CB) {
if (fabs (residual[i]) >= epsilon * mu_x *mass_water_aq_x /* || stop_program == TRUE */) {
sprintf(error_string,"%20s Charge balance has not converged. "
"\tResidual: %e\n", x[i]->description, (double) residual[i]);
error_msg(error_string, CONTINUE);
}
} else if (x[i]->type == MU && pitzer_model == FALSE) {
if (fabs (residual[i]) >= epsilon * mu_x * mass_water_aq_x /* || stop_program == TRUE */) {
sprintf(error_string,"%20s Ionic strength has not converged. "
"\tResidual: %e\n", x[i]->description, (double) residual[i]);
error_msg(error_string, CONTINUE);
}
} else if (x[i]->type == AH2O && pitzer_model == FALSE) {
if (fabs (residual[i]) >= epsilon /* || stop_program == TRUE */) {
sprintf(error_string,"%20s Activity of water has not converged. "
"\tResidual: %e\n", x[i]->description, (double) residual[i]);
error_msg(error_string, CONTINUE);
}
} else if (x[i]->type == MH && pitzer_model == FALSE) {
#define COMBINE
/*#define COMBINE_CHARGE*/
#ifdef COMBINE
#ifndef COMBINE_CHARGE
if (fabs(residual[i]) > epsilon * (x[i]->moles + 2 * mass_oxygen_unknown->moles))
#else
if (fabs(residual[i]) > epsilon * (x[i]->moles + 2 * mass_oxygen_unknown->moles + charge_balance_unknown->moles))
#endif
#else
if (fabs (residual[i]) >= epsilon * x[i]->moles /* || stop_program == TRUE */)
#endif
{
sprintf(error_string,"%20s Mass of hydrogen has not converged. "
"\tResidual: %e\n", x[i]->description, (double) residual[i]);
error_msg(error_string, CONTINUE);
}
} else if (x[i]->type == MH2O) {
if (mass_water_switch == TRUE) continue;
if (fabs (residual[i]) >= 0.01 * epsilon * x[i]->moles /* || stop_program == TRUE */) {
sprintf(error_string,"%20s Mass of oxygen has not converged. "
"\tResidual: %e\n", x[i]->description, (double) residual[i]);
error_msg(error_string, CONTINUE);
}
} else if (x[i]->type == PP) {
if (x[i]->pure_phase->add_formula == NULL) {
if (x[i]->dissolve_only == TRUE) {
if ((residual[i] > epsilon && x[i]->moles > 0.0) || ((residual[i] < -epsilon && (x[i]->pure_phase->initial_moles - x[i]->moles) > 0))) {
output_msg(OUTPUT_LOG,"%20s Dissolve_only pure phase has not converged. \tResidual: %e\n", x[i]->description, (double) residual[i]);
}
} else {
if ((residual[i] >= epsilon && x[i]->moles > 0.0) /* || stop_program == TRUE */) {
remove_unstable_phases = TRUE;
output_msg(OUTPUT_LOG,"%20s Pure phase has not converged. \tResidual: %e\n", x[i]->description, (double) residual[i]);
} else if (residual[i] <= -epsilon ) {
sprintf(error_string,"%20s Pure phase has not converged. "
"\tResidual: %e\n", x[i]->description, (double) residual[i]);
error_msg(error_string, CONTINUE);
}
}
} else {
if ((fabs(residual[i]) >= epsilon && x[i]->moles > 0.0) /* || stop_program == TRUE */) {
output_msg(OUTPUT_LOG,"%s, Pure phase has not converged. \tResidual: %e\n", x[i]->description, (double) residual[i]);
sprintf(error_string,"%s, Pure phase with add formula has not converged.\n\t SI may be a local minimum."
"\tResidual: %e\n", x[i]->description, (double) residual[i]);
warning_msg(error_string);
}
}
} else if (x[i]->type == EXCH) {
if (/* stop_program == TRUE || */
(x[i]->moles <= MIN_RELATED_SURFACE && fabs(residual[i]) > epsilon) ||
(x[i]->moles > MIN_RELATED_SURFACE && (fabs(residual[i]) > epsilon * x[i]->moles))) {
sprintf(error_string,"%20s Exchanger mass balance has not converged. "
"\tResidual: %e\n", x[i]->description, (double) residual[i]);
error_msg(error_string, CONTINUE);
}
} else if (x[i]->type == SURFACE) {
if (/* stop_program == TRUE || */
(x[i]->moles <= MIN_RELATED_SURFACE && fabs(residual[i]) > epsilon) ||
(x[i]->moles > MIN_RELATED_SURFACE && (fabs(residual[i]) > epsilon * x[i]->moles))) {
sprintf(error_string,"%20s Surface mass balance has not converged. "
"\tResidual: %e\n", x[i]->description, (double) residual[i]);
error_msg(error_string, CONTINUE);
}
} else if (x[i]->type == SURFACE_CB) {
if ((x[i]->surface_charge->grams > MIN_RELATED_SURFACE && fabs(residual[i]) > epsilon) /* || stop_program == TRUE */) {
sprintf(error_string,"%20s Surface charge/potential has not converged. "
"\tResidual: %e\n", x[i]->description, (double) residual[i]);
error_msg(error_string, CONTINUE);
}
} else if (x[i]->type == GAS_MOLES) {
if (gas_in == FALSE) continue;
if (residual[i] >= epsilon || residual[i] <= -epsilon /* || stop_program == TRUE */) {
sprintf(error_string,"%20s Total moles in gas phase has not converged. "
"\tResidual: %e\n", x[i]->description, (double) residual[i]);
error_msg(error_string, CONTINUE);
}
} else if (x[i]->type == PITZER_GAMMA) {
if (fabs(residual[i]) > epsilon ) {
sprintf(error_string,"%20s log gamma not converged.\tResidual: %e\n", x[i]->description, (double) residual[i]);
}
} else if (x[i]->type == S_S_MOLES) {
if (x[i]->s_s_in == FALSE) continue;
if (residual[i] >= epsilon || residual[i] <= -epsilon /* || stop_program == TRUE */) {
sprintf(error_string,"%20s Total moles in solid solution has not converged. "
"\tResidual: %e\n", x[i]->description, (double) residual[i]);
error_msg(error_string, CONTINUE);
}
}
}
if (remove_unstable_phases == TRUE) {
output_msg(OUTPUT_LOG, "%20sRemoving unstable phases, iteration %d."," ", iterations);
}
return(return_value);
}
/* ---------------------------------------------------------------------- */
int gammas (LDBLE mu)
/* ---------------------------------------------------------------------- */
{
/*
* Calculates gammas and [moles * d(ln gamma)/d mu] for all aqueous
* species.
*/
int i, j;
int ifirst, ilast;
LDBLE f, a_llnl, b_llnl, bdot_llnl, log_g_co2, dln_g_co2, c2_llnl;
LDBLE s1, s2, s3;
LDBLE c1, c2, a, b;
LDBLE muhalf;
/* Initialize */
if (pitzer_model == TRUE) return gammas_pz();
a_llnl = b_llnl = bdot_llnl = log_g_co2 = dln_g_co2 = c2_llnl = 0;
/*
* compute temperature dependence of a and b for debye-huckel
*/
s1=374.11-tc_x;
s2=pow(s1,1.0/3.0);
s3=1.0+0.1342489*s2-3.946263e-03*s1;
s3=s3/(3.1975-0.3151548*s2-1.203374e-03*s1+7.48908e-13*(s1*s1*s1*s1));
s3=sqrt(s3);
if (tk_x >= 373.15) {
c1=5321.0/tk_x+233.76-tk_x*(tk_x*(8.292e-07*tk_x-1.417e-03)+0.9297);
} else {
/* replaced by wateq4f expression
c1=87.74-tc_x*(tc_x*(1.41e-06*tc_x-9.398e-04)+0.4008);
*/
c1 = 2727.586+0.6224107*tk_x-466.9151*log(tk_x)-52000.87/tk_x;
}
c1=sqrt(c1*tk_x);
/* replaced by wateq4f expressions
a=1824600.0*s3/(c1 * c1 * c1);
b=50.29*s3/c1;
*/
a = 1824827.7 * s3 / (c1 * c1 * c1);
b = 50.2905 * s3 / c1;
/*
* LLNL temperature dependence
*/
if (llnl_count_temp > 0) {
ifirst = 0;
ilast = llnl_count_temp;
if (tc_x < llnl_temp[0] || tc_x > llnl_temp[llnl_count_temp - 1]) {
error_msg("Temperature out of range of LLNL_AQUEOUS_MODEL parameters", STOP);
}
for (i = 0; i < llnl_count_temp; i++) {
if (tc_x >= llnl_temp[i]) ifirst = i;
if (tc_x <= llnl_temp[i]) {
ilast = i;
break;
}
}
if (ilast == ifirst) {
f = 1;
} else {
f = (tc_x - llnl_temp[ifirst])/(llnl_temp[ilast] - llnl_temp[ifirst]);
}
a_llnl = (1 - f)*llnl_adh[ifirst] + f*llnl_adh[ilast];
b_llnl = (1 - f)*llnl_bdh[ifirst] + f*llnl_bdh[ilast];
bdot_llnl = (1 - f)*llnl_bdot[ifirst] + f*llnl_bdot[ilast];
/*
* CO2 activity coefficient
*/
log_g_co2 = (llnl_co2_coefs[0] + llnl_co2_coefs[1]*tk_x + llnl_co2_coefs[2]/tk_x)*mu -
(llnl_co2_coefs[3] + llnl_co2_coefs[4]*tk_x)*(mu/(mu+1));
log_g_co2 /= LOG_10;
dln_g_co2 = (llnl_co2_coefs[0] + llnl_co2_coefs[1]*tk_x + llnl_co2_coefs[2]/tk_x) -
(llnl_co2_coefs[3] + llnl_co2_coefs[4]*tk_x)*(1/((mu+1)*(mu+1)));
}
/*
* constants for equations
*/
muhalf = sqrt(mu);
c1=(-a)*LOG_10*(1.0/(2*muhalf*(muhalf+1.0)*(muhalf+1.0))-0.3);
c2=-a/(2*muhalf);
if (llnl_count_temp > 0) {
c2_llnl = -a_llnl/(2*muhalf);
}
/*
* Calculate activity coefficients
*/
for (i=0; i < count_s_x; i++) {
switch (s_x[i]->gflag) {
case 0: /* uncharged */
s_x[i]->lg = s_x[i]->dhb * mu;
s_x[i]->dg = s_x[i]->dhb * LOG_10 * s_x[i]->moles;
break;
case 1: /* Davies */
s_x[i]->lg = - s_x[i]->z * s_x[i]->z * a *
( muhalf / (1.0 + muhalf) - 0.3 * mu);
s_x[i]->dg = c1 * s_x[i]->z * s_x[i]->z * s_x[i]->moles;
break;
case 2: /* Extended D-H, WATEQ D-H */
s_x[i]->lg = -a * muhalf * s_x[i]->z * s_x[i]->z /
(1.0 + s_x[i]->dha * b * muhalf) + s_x[i]->dhb * mu;
s_x[i]->dg = (c2 * s_x[i]->z * s_x[i]->z /
((1.0 + s_x[i]->dha * b * muhalf)*(1.0 + s_x[i]->dha * b * muhalf)) + s_x[i]->dhb) * LOG_10 * s_x[i]->moles;
/* if (mu_x < 1e-6) s_x[i]->dg = 0.0; */
break;
case 3: /* Always 1.0 */
s_x[i]->lg = 0.0;
s_x[i]->dg = 0.0;
break;
case 4: /* Exchange */
/*
* Find CEC
* z contains valence of cation for exchange species, alk contains cec
*/
/* !!!!! */
for (j=1; s_x[i]->rxn_x->token[j].s != NULL; j++) {
if (s_x[i]->rxn_x->token[j].s->type == EX) {
s_x[i]->alk = s_x[i]->rxn_x->token[j].s->primary->unknown->moles;
break;
}
}
if (s_x[i]->exch_gflag == 1 && s_x[i]->alk > 0) {
/* Davies */
s_x[i]->lg = - s_x[i]->equiv * s_x[i]->equiv * a *
( muhalf / (1.0 + muhalf) - 0.3 * mu) +
log10(fabs(s_x[i]->equiv)/s_x[i]->alk);
s_x[i]->dg = c1 * s_x[i]->equiv * s_x[i]->equiv * s_x[i]->moles;
} else if (s_x[i]->exch_gflag == 2 && s_x[i]->alk > 0) {
/* Extended D-H, WATEQ D-H */
s_x[i]->lg = -a * muhalf * s_x[i]->equiv * s_x[i]->equiv /
(1.0 + s_x[i]->dha * b * muhalf) + s_x[i]->dhb * mu +
log10(fabs(s_x[i]->equiv)/s_x[i]->alk);
s_x[i]->dg = (c2 * s_x[i]->equiv * s_x[i]->equiv /
((1.0 + s_x[i]->dha * b * muhalf)*(1.0 + s_x[i]->dha * b * muhalf)) + s_x[i]->dhb) * LOG_10 * s_x[i]->moles;
} else if (s_x[i]->exch_gflag == 7 && s_x[i]->alk > 0) {
if (llnl_count_temp >0 ) {
s_x[i]->lg = -a_llnl * muhalf * s_x[i]->equiv * s_x[i]->equiv / (1.0 + s_x[i]->dha * b_llnl * muhalf) + bdot_llnl * mu + log10(fabs(s_x[i]->equiv)/s_x[i]->alk);
s_x[i]->dg = (c2_llnl * s_x[i]->equiv * s_x[i]->equiv / ((1.0 + s_x[i]->dha * b_llnl * muhalf)*(1.0 + s_x[i]->dha * b_llnl * muhalf)) + bdot_llnl) * LOG_10 * s_x[i]->moles;
} else {
error_msg("LLNL_AQUEOUS_MODEL_PARAMETERS not defined.", STOP);
}
} else {
/*
* Master species is a dummy variable with meaningless activity and mass
*/
if (s_x[i]->primary != NULL) {
s_x[i]->lg = 0.0;
s_x[i]->dg = 0.0;
} else {
if (s_x[i]->alk <= 0) {
s_x[i]->lg = 0.0;
} else {
s_x[i]->lg = log10(fabs(s_x[i]->equiv)/s_x[i]->alk);
}
s_x[i]->dg = 0.0;
}
}
break;
case 5: /* Always 1.0 */
s_x[i]->lg = 0.0;
s_x[i]->dg = 0.0;
break;
case 6: /* Surface */
/*
* Find moles of sites.
* s_x[i]->equiv is stoichiometric coefficient of sites in species
*/
for (j=1; s_x[i]->rxn_x->token[j].s != NULL; j++) {
if (s_x[i]->rxn_x->token[j].s->type == SURF) {
s_x[i]->alk = s_x[i]->rxn_x->token[j].s->primary->unknown->moles;
break;
}
}
if (s_x[i]->alk > 0) {
s_x[i]->lg = log10(s_x[i]->equiv / s_x[i]->alk);
s_x[i]->dg = 0.0;
} else {
s_x[i]->lg = 0.0;
s_x[i]->dg = 0.0;
}
break;
case 7: /* LLNL */
if (llnl_count_temp >0 ) {
if (s_x[i]->z == 0) {
s_x[i]->lg = 0.0;
s_x[i]->dg = 0.0;
} else {
s_x[i]->lg = -a_llnl * muhalf * s_x[i]->z * s_x[i]->z /
(1.0 + s_x[i]->dha * b_llnl * muhalf) + bdot_llnl * mu;
s_x[i]->dg = (c2_llnl * s_x[i]->z * s_x[i]->z / ((1.0 + s_x[i]->dha * b_llnl * muhalf)*(1.0 + s_x[i]->dha * b_llnl * muhalf)) + bdot_llnl) * LOG_10 * s_x[i]->moles;
break;
}
} else {
error_msg("LLNL_AQUEOUS_MODEL_PARAMETERS not defined.", STOP);
}
break;
case 8: /* LLNL CO2*/
if (llnl_count_temp > 0) {
s_x[i]->lg = log_g_co2;
s_x[i]->dg = dln_g_co2 * s_x[i]->moles;
} else {
error_msg("LLNL_AQUEOUS_MODEL_PARAMETERS not defined.", STOP);
}
break;
case 9: /* activity water */
s_x[i]->lg = log10(exp( s_h2o->la * LOG_10) * gfw_water);
s_x[i]->dg = 0.0;
break;
}
/*
if (mu_unknown != NULL) {
if (fabs(residual[mu_unknown->number]) > 0.1 &&
fabs(residual[mu_unknown->number])/mu_x > 0.5) {
s_x[i]->dg = 0.0;
}
}
*/
}
return(OK);
}
/* ------------------------------------------------------------------------------- */
int ineq(int in_kode)
/* ------------------------------------------------------------------------------- */
{
/*
* Sets up equations and inequalities for Cl1.
* Scales columns if necessary.
* Eliminates equations that are not necessary because
* gas_phase, s_s, or phase equation is not needed
* Mallocs space
* Calls Cl1
* Rescales results if necessary
*/
int i, j;
int return_code;
int *back;
int count_rows;
int count_optimize, count_equal;
extern int max_row_count, max_column_count;
int k, l, m, n;
int klmd, nklmd, n2d;
int iter;
LDBLE error;
LDBLE *ineq_array, *res, *cu;
LDBLE *zero, *delta1;
int *iu, *is;
LDBLE *normal;
LDBLE max;
int kode;
#ifdef SKIP
LDBLE *save_row;
#endif
LDBLE min;
#ifdef SLNQ
LDBLE *slnq_array;
LDBLE *slnq_delta1;
#endif
/* Debug
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE, "\narray:\n\n");
array_print(array, count_unknowns, count_unknowns + 1, count_unknowns + 1);
}
*/
/*
* Special case for removing unstable phases
*/
if ( remove_unstable_phases == TRUE ) {
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE, "\nSolution vector for removing unstable phases:\n");
}
for (i=0; i< count_unknowns; i++) {
if (x[i]->type == PP && residual[i] > 0e-8 && x[i]->moles > 0 &&
x[i]->pure_phase->add_formula == NULL && x[i]->dissolve_only == FALSE) {
/*
* Set mass transfer to all of phase
*/
delta[i] = x[i]->moles;
} else {
delta[i] = 0.0;
}
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE, "%6d %-12.12s %10.2e\n",i,
x[i]->description, (double) delta[i]);
}
}
remove_unstable_phases = FALSE;
return(OK);
}
/*
* Pitzer model does not have activity of water or mu
*/
if (pitzer_model == TRUE) {
for (i=0; i < count_unknowns; i++) {
if ( (x[i]->type == AH2O && full_pitzer == FALSE) ||
x[i]->type == MH ||
x[i]->type == MU ||
(x[i]->type == PITZER_GAMMA && full_pitzer == FALSE)) {
for (j=0; j<count_unknowns; j++) {
array[j*(count_unknowns +1) + i] = 0.0;
}
for (j=0; j<count_unknowns + 1; j++) {
array[i*(count_unknowns +1) + j] = 0.0;
}
}
}
}
/*
* Normalize column
*/
normal = (LDBLE *) PHRQ_malloc((size_t) count_unknowns * sizeof(LDBLE));
if (normal == NULL) malloc_error();
for (i=0; i<count_unknowns; i++) normal[i]=1.0;
for (i=0; i < count_unknowns; i++) {
max = 0.0;
if (x[i]->type == MB || x[i]->type == ALK || x[i]->type == EXCH || x[i]->type == SURFACE || x[i]->type == SURFACE_CB) {
/* !!!! */ if ( x[i]->moles <= MIN_RELATED_SURFACE && (x[i]->type == EXCH || x[i]->type == SURFACE)) continue;
for (j = 0; j < count_unknowns; j++) {
if (x[i]->type == SURFACE && x[j]->type == SURFACE_CB ) continue;
if (fabs(array[j *(count_unknowns + 1) + i]) > max) {
max = fabs(array[j * (count_unknowns + 1) + i]);
if (max > min_value) break;
}
}
if (diagonal_scale == TRUE) {
if (fabs(array[i *(count_unknowns + 1) + i]) < min_value) {
max = fabs(array[i * (count_unknowns + 1) + i]);
}
}
if (max == 0) {
array[i *(count_unknowns + 1) + i] = 1e-5*x[i]->moles;
max = fabs(1e-5*x[i]->moles);
}
}
if (x[i]->type == MH && pitzer_model == FALSE) {
/* make absolute value of diagonal at least 1e-12 */
min = 1e-12;
min = MIN_TOTAL;
array[x[i]->number * (count_unknowns + 1) + x[i]->number] += min;
if (fabs(array[x[i]->number * (count_unknowns + 1) + x[i]->number]) < min)
array[x[i]->number * (count_unknowns + 1) + x[i]->number] = min;
max = 0.0;
for (j=0; j<count_unknowns; j++) {
if (x[j]->type != MB &&
x[j]->type != SURFACE &&
x[j]->type != SURFACE_CB &&
x[j]->type != EXCH &&
x[j]->type != MH &&
x[j]->type != MH2O) continue;
if (fabs(array[j * (count_unknowns + 1) + i]) > max) {
max = fabs(array[j * (count_unknowns + 1) + i]);
if (max > min_value) break;
}
}
}
if (max > 0.0 && max < min_value) {
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE, "Scaling column for %s, max= %e\n",
x[i]->description, (double) max);
}
for (j=0; j < count_unknowns; j++) {
array[j * (count_unknowns + 1) + i] *= min_value / max ;
}
normal[i] = min_value / max;
}
}
/*
* Allocate arrays for inequality solver
*/
max_row_count = 2*count_unknowns + 2;
max_column_count = count_unknowns + 2;
ineq_array = (LDBLE *) PHRQ_malloc((size_t) max_row_count * max_column_count *
sizeof(LDBLE));
if (ineq_array == NULL) malloc_error();
back = (int *) PHRQ_malloc((size_t) max_row_count * sizeof(int));
if (back == NULL) malloc_error();
zero = (LDBLE *) PHRQ_malloc((size_t) max_row_count*sizeof(LDBLE));
if (zero == NULL) malloc_error();
for (i=0; i<max_row_count; i++) zero[i]=0.;
res = (LDBLE *) PHRQ_malloc((size_t) max_row_count*sizeof(LDBLE));
if (res == NULL) malloc_error();
memcpy( (void *) &(res[0]), (void *) &(zero[0]), (size_t) max_row_count * sizeof(LDBLE));
delta1 = (LDBLE *) PHRQ_malloc( (size_t) max_column_count * sizeof(LDBLE));
if (delta1 == NULL) malloc_error();
memcpy( (void *) &(delta1[0]), (void *) &(zero[0]), (size_t) max_column_count * sizeof(LDBLE));
/*
* Copy equations to optimize into ineq_array
*/
count_rows = 0;
for (i=0; i < count_unknowns; i++) {
if (iterations < aqueous_only) continue;
/*
* Pure phases
*/
if ( x[i]->type == PP ) {
/* not in model, ignore */
if (x[i]->pure_phase->phase->in == FALSE) continue;
/* Undersaturated and no mass, ignore */
if (x[i]->f > 0e-8 && x[i]->moles <= 0 && x[i]->pure_phase->add_formula == NULL) {
continue;
} else if (x[i]->f < 0e-8 && x[i]->dissolve_only == TRUE && (x[i]->moles - x[i]->pure_phase->initial_moles >= 0)) {
continue;
} else {
/* Copy in saturation index equation (has mass or supersaturated) */
memcpy((void *) &(ineq_array[count_rows*max_column_count]),
(void *) &(array[i*(count_unknowns + 1)]),
(size_t) (count_unknowns + 1) * sizeof(LDBLE));
back[count_rows] = i;
if (x[i]->pure_phase->add_formula == NULL && x[i]->dissolve_only == FALSE) {
res[count_rows] = 1.0;
}
/*
* If infeasible solution on first attempt, remove constraints on IAP
*/
#ifdef SKIP
#endif
if (pp_scale != 1){
for (j = 0; j < count_unknowns +1; j++) {
ineq_array[count_rows * max_column_count + j] *= pp_scale;
}
}
if (in_kode != 1) {
res[count_rows] = 0.0;
}
count_rows++;
}
} else if (x[i]->type == ALK ||
x[i]->type == SOLUTION_PHASE_BOUNDARY ) {
/*
* Alkalinity and solution phase boundary
*/
memcpy((void *) &(ineq_array[count_rows*max_column_count]),
(void *) &(array[i*(count_unknowns + 1)]),
(size_t) (count_unknowns + 1) * sizeof(LDBLE));
back[count_rows] = i;
count_rows++;
/*
* Gas phase
*/
} else if (x[i]->type == GAS_MOLES && gas_in == TRUE) {
memcpy((void *) &(ineq_array[count_rows*max_column_count]),
(void *) &(array[i*(count_unknowns + 1)]),
(size_t) (count_unknowns + 1) * sizeof(LDBLE));
back[count_rows] = i;
res[count_rows] = 1.0;
if (in_kode != 1) {
res[count_rows] = 0.0;
}
count_rows++;
/*
* Solid solution
*/
} else if (x[i]->type == S_S_MOLES && x[i]->s_s_in == TRUE) {
memcpy((void *) &(ineq_array[count_rows*max_column_count]),
(void *) &(array[i*(count_unknowns + 1)]),
(size_t) (count_unknowns + 1) * sizeof(LDBLE));
back[count_rows] = i;
res[count_rows] = 1.0;
if (in_kode != 1) {
res[count_rows] = 0.0;
}
count_rows++;
}
}
count_optimize = count_rows;
/*
* Copy equality equations into ineq_array
*/
for (i=0; i< count_unknowns; i++) {
if (x[i]->type != SOLUTION_PHASE_BOUNDARY &&
x[i]->type != ALK &&
x[i]->type != GAS_MOLES &&
x[i]->type != S_S_MOLES &&
x[i]->type != PP ) {
if(x[i]->type == MH && pitzer_model == TRUE) continue;
if(mass_water_switch == TRUE && x[i] == mass_oxygen_unknown) continue;
/*
* Mass balance, CB, MU, AH2O, MH, MH2O, others
*/
if (x[i]->type == EXCH && x[i]->moles <= MIN_RELATED_SURFACE) continue;
if (x[i]->type == SURFACE &&
x[i]->phase_unknown == NULL &&
x[i]->moles <= MIN_RELATED_SURFACE) continue;
if (x[i]->type == SURFACE_CB &&
/* x[i-1]->phase_unknown == NULL && */
/* x[i-1]->moles <= MIN_RELATED_SURFACE) continue; */
x[i]->surface_charge->grams <= MIN_RELATED_SURFACE) continue;
if (x[i]->type == SURFACE &&
x[i]->phase_unknown != NULL &&
/* x[i]->phase_unknown->f > 0e-8 && */
x[i]->phase_unknown->moles <= MIN_RELATED_SURFACE &&
x[i]->phase_unknown->pure_phase->add_formula == NULL) continue;
if (x[i]->type == SURFACE_CB &&
x[i-1]->phase_unknown != NULL &&
/* x[i-1]->phase_unknown->f > 0e-8 && */
/* x[i-1]->phase_unknown->moles <= MIN_RELATED_SURFACE && */
x[i]->surface_charge->grams <= MIN_RELATED_SURFACE &&
x[i-1]->phase_unknown->pure_phase->add_formula == NULL) continue;
memcpy( (void *) &(ineq_array[count_rows*max_column_count]),
(void *) &(array[i*(count_unknowns + 1)]),
(size_t) (count_unknowns + 1) * sizeof(LDBLE));
back[count_rows] = i;
if(mass_water_switch == TRUE && x[i] == mass_hydrogen_unknown) {
k = mass_oxygen_unknown->number;
for (j = 0; j < count_unknowns; j++) {
ineq_array[count_rows * max_column_count + j] -= 2*array[k * (count_unknowns + 1) + j];
}
}
count_rows++;
} else if (x[i]->type == PITZER_GAMMA) {
memcpy( (void *) &(ineq_array[count_rows*max_column_count]),
(void *) &(array[i*(count_unknowns + 1)]),
(size_t) (count_unknowns + 1) * sizeof(LDBLE));
back[count_rows] = i;
}
}
count_equal = count_rows - count_optimize;
/*
* Copy inequality constraints into ineq
*/
if (pure_phase_unknown != NULL) {
for (i=0; i < count_unknowns; i++) {
if (x[i]->type == PP) {
/* not in model, ignore */
if (x[i]->pure_phase->phase->in == FALSE) continue;
/* No moles and undersaturated, ignore */
if (x[i]->moles <= 0.0 && x[i]->f > 0e-8 &&
x[i]->pure_phase->add_formula == NULL) {
continue;
/* No moles of pure phase present, must precipitate */
} else if (x[i]->moles <= 0.0 ) {
delta1[i] = -1.0;
} else if (x[i]->f < 0e-8 && x[i]->dissolve_only == TRUE && (x[i]->moles - x[i]->pure_phase->initial_moles >= 0)) {
continue;
} else {
/* Pure phase is present, force Mass transfer to be <= amount of mineral remaining */
memcpy( (void *) &(ineq_array[count_rows*max_column_count]),
(void *) &(zero[ 0 ]),
(size_t) (count_unknowns + 1) * sizeof(LDBLE));
ineq_array[count_rows*max_column_count + i] = 1.0;
ineq_array[count_rows*max_column_count + count_unknowns ] = x[i]->moles;
back[count_rows] = i;
count_rows++;
}
/* Pure phase is present and dissolve_only, force ppt to be <= amount of dissolved so far */
if (x[i]->dissolve_only == TRUE) {
memcpy( (void *) &(ineq_array[count_rows*max_column_count]), (void *) &(zero[ 0 ]), (size_t) (count_unknowns + 1) * sizeof(LDBLE));
ineq_array[count_rows*max_column_count + i] = -1.0;
ineq_array[count_rows*max_column_count + count_unknowns ] = x[i]->pure_phase->initial_moles - x[i]->moles;
back[count_rows] = i;
count_rows++;
}
}
}
}
/*
* Add inequality for mass of oxygen greater than zero
*/
#ifdef SKIP
#endif
if (pitzer_model) {
for (i=0; i < count_unknowns; i++) {
if (x[i]->type == MH2O) {
memcpy( (void *) &(ineq_array[count_rows*max_column_count]),
(void *) &(array[i*(count_unknowns + 1)]),
(size_t) (count_unknowns + 1) * sizeof(LDBLE));
back[count_rows] = i;
for (j = 0; j < count_unknowns; j++) {
if (x[j]->type < PP) {
ineq_array[count_rows*max_column_count + j] = 0.0;
} else {
/*ineq_array[count_rows*max_column_count + j] = -ineq_array[count_rows*max_column_count + j]; */
}
}
ineq_array[count_rows*max_column_count + count_unknowns] = 0.5*x[i]->moles;
count_rows++;
}
}
}
/*
* Hydrogen mass balance is good
*/
/*
* No moles and undersaturated, mass transfer must be zero
*/
if (pure_phase_unknown != NULL) {
for (i=0; i < count_unknowns; i++) {
if (x[i]->type == PP) {
if ((x[i]->moles <= 0.0 && x[i]->f > 0e-8 &&
x[i]->pure_phase->add_formula == NULL) || x[i]->pure_phase->phase->in == FALSE) {
for (j=0; j<count_rows; j++) {
ineq_array[j*max_column_count + i] = 0.0;
}
}
if (x[i]->dissolve_only == TRUE) {
if (x[i]->f < 0e-8 && (x[i]->moles - x[i]->pure_phase->initial_moles >= 0)) {
for (j=0; j<count_rows; j++) {
ineq_array[j*max_column_count + i] = 0.0;
}
}
}
}
}
}
/*
* No moles of exchanger
*/
if (use.exchange_ptr != NULL && (use.exchange_ptr->related_phases == TRUE || use.exchange_ptr->related_rate == TRUE )) {
for (i=0; i < count_unknowns; i++) {
if (x[i]->type == EXCH && x[i]->moles <= 0) {
for (j=0; j<count_rows; j++) {
ineq_array[j*max_column_count + i] = 0.0;
}
}
}
}
/*
* No moles of surface
*/
if (use.surface_ptr != NULL && (use.surface_ptr->related_phases == TRUE || use.surface_ptr->related_rate == TRUE ) ) {
for (i=0; i < count_unknowns; i++) {
if ((x[i]->type == SURFACE &&
x[i]->phase_unknown != NULL &&
/* x[i]->phase_unknown->f > 0e-8 && */
x[i]->phase_unknown->moles <= MIN_RELATED_SURFACE &&
x[i]->phase_unknown->pure_phase->add_formula == NULL) ||
(x[i]->type == SURFACE_CB &&
x[i-1]->phase_unknown != NULL &&
/* x[i-1]->phase_unknown->f > 0e-8 && */
x[i]->surface_charge->grams <= MIN_RELATED_SURFACE &&
x[i-1]->phase_unknown->pure_phase->add_formula == NULL)) {
for (j=0; j<count_rows; j++) {
ineq_array[j*max_column_count + i] = 0.0;
}
}
}
}
/*
* No moles of surface
*/
if (use.surface_ptr != NULL){
for (i=0; i < count_unknowns; i++) {
if ((x[i]->type == SURFACE &&
x[i]->phase_unknown == NULL &&
x[i]->moles <= MIN_RELATED_SURFACE) ||
(x[i]->type == SURFACE_CB &&
x[i-1]->phase_unknown == NULL &&
x[i]->surface_charge->grams <= MIN_RELATED_SURFACE)) {
for (j=0; j<count_rows; j++) {
ineq_array[j*max_column_count + i] = 0.0;
}
}
}
}
/*
* Moles of gas must be >= zero
*/
if (gas_in == TRUE) {
i = gas_unknown->number;
memcpy( (void *) &(ineq_array[count_rows*max_column_count]),
(void *) &(zero[ 0 ]),
(size_t) (count_unknowns + 1) * sizeof(LDBLE));
ineq_array[count_rows*max_column_count + i] = -1.0;
ineq_array[count_rows*max_column_count + count_unknowns ] = x[i]->moles;
back[count_rows] = i;
count_rows++;
} else if (use.gas_phase_ptr != NULL && gas_in == FALSE) {
/*
* Moles of gas small and sum p < ptotal
*/
i = gas_unknown->number;
for (j=0; j<count_rows; j++) {
ineq_array[j*max_column_count + i] = 0.0;
}
}
/*
* Phase must be "in" and moles of solid solution must be >= zero
*/
if (s_s_unknown != NULL) {
for (i = s_s_unknown->number; i < count_unknowns; i++) {
if (x[i]->type != S_S_MOLES) break;
if (x[i]->phase->in == TRUE && x[i]->s_s_in == TRUE) {
memcpy( (void *) &(ineq_array[count_rows*max_column_count]),
(void *) &(zero[ 0 ]),
(size_t) (count_unknowns + 1) * sizeof(LDBLE));
ineq_array[count_rows*max_column_count + i] = 1.0;
ineq_array[count_rows*max_column_count + count_unknowns ] = 0.99*x[i]->moles - MIN_TOTAL;
back[count_rows] = i;
count_rows++;
} else {
for (j=0; j<count_rows; j++) {
ineq_array[j*max_column_count + i] = 0.0;
}
}
}
}
/*
* Add inequality if total moles of element is less than zero
*/
if (negative_concentrations) {
for (i=0; i < count_unknowns; i++) {
if (x[i]->type == MB && x[i]->moles < 0.0 ) {
memcpy( (void *) &(ineq_array[count_rows*max_column_count]),
(void *) &(array[i*(count_unknowns + 1)]),
(size_t) (count_unknowns + 1) * sizeof(LDBLE));
back[count_rows] = i;
for (j = 0; j < count_unknowns; j++) {
if (x[j]->type < PP) {
ineq_array[count_rows*max_column_count + j] = 0.0;
}
}
count_rows++;
}
}
}
/*
* Zero column for mass of water
*/
if(mass_oxygen_unknown != NULL && mass_water_switch == TRUE) {
k = mass_oxygen_unknown->number;
for (j = 0; j < count_rows + 1; j++) {
ineq_array[j * max_column_count + k] = 0;
}
}
/*
* Scale column for pure phases
*/
for (i=0; i < count_unknowns; i++) {
if ( (x[i]->type == PP || x[i]->type == S_S_MOLES) && pp_column_scale != 1.0) {
for (j = 0; j < count_rows; j++) {
ineq_array[j* max_column_count + i] *= pp_column_scale;
}
normal[i] = pp_column_scale;
}
}
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE, "\nA and B arrays:\n\n");
array_print(ineq_array, count_rows, count_unknowns + 1, max_column_count);
}
/*
* Calculate dimensions
*/
k = count_optimize; /* rows in A */
l = count_equal; /* rows in C */
m = count_rows - l - k; /* rows in E */
if (m < 0) m = 0;
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE, "k, l, m\t%d\t%d\t%d\n",k, l, m);
}
n = count_unknowns; /* columns in A, C, E */
klmd = max_row_count - 2;
nklmd = n+klmd;
n2d = n + 2;
/*
* Retain constraints on mineral mass transfers, even if infeasible on
* first attempt.
*/
kode = 1;
if (in_kode == 2) {
kode = 1;
}
iter = 200;
/*
* Allocate space for arrays
*/
cu = (LDBLE *) PHRQ_malloc((size_t) 2*nklmd*sizeof(LDBLE));
if (cu == NULL) malloc_error();
iu = (int *) PHRQ_malloc((size_t) 2*nklmd*sizeof(int));
if (iu == NULL) malloc_error();
is = (int *) PHRQ_malloc((size_t) klmd*sizeof(int));
if (is == NULL) malloc_error();
#ifdef SLNQ
slnq_array = (LDBLE *) PHRQ_malloc((size_t) count_unknowns*(count_unknowns+1)*sizeof(LDBLE));
if (slnq_array == NULL) malloc_error();
for (i = 0; i < k+l; i++) {
for (j = 0; j <= count_unknowns; j++) {
slnq_array[i*(count_unknowns + 1) + j] = ineq_array[i*max_column_count + j];
}
}
slnq_delta1 = (LDBLE *) PHRQ_malloc( (size_t) max_column_count * sizeof(LDBLE));
if (slnq_delta1 == NULL) malloc_error();
memcpy( (void *) &(slnq_delta1[0]), (void *) &(zero[0]), (size_t) max_column_count * sizeof(LDBLE));
#endif
/*
* Call CL1
*/
cl1(k, l, m, n,
nklmd, n2d, ineq_array,
&kode, ineq_tol, &iter,
delta1, res, &error, cu, iu, is, FALSE);
/* Set return_kode */
if ( kode == 1) {
return_code = ERROR;
} else if (kode == 2) {
return_code = 2;
} else {
return_code = OK;
}
#ifdef SLNQ
/* if (kode > 0 && ((k + l) == count_unknowns)) { */
if (kode > 0 && ((k + l) <= count_unknowns)) {
if (add_trivial_eqns(k+l, count_unknowns, slnq_array) == TRUE) {
if (debug_model == TRUE) output_msg(OUTPUT_MESSAGE, "Calling SLNQ, iteration %d\n", iterations);
output_msg(OUTPUT_LOG, "Calling SLNQ, iteration %d\n", iterations);
if (slnq(count_unknowns, slnq_array, slnq_delta1, count_unknowns+1, debug_model) == OK) {
memcpy( (void *) &(delta1[0]), (void *) &(slnq_delta1[0]),
(size_t) count_unknowns * sizeof(LDBLE));
if (debug_model == TRUE) output_msg(OUTPUT_MESSAGE, "Using SLNQ results.\n");
output_msg(OUTPUT_LOG, "Using SLNQ results.\n");
return_code = OK;
} else {
if (debug_model == TRUE) output_msg(OUTPUT_MESSAGE, "Could not use SLNQ results.\n");
output_msg(OUTPUT_LOG, "Could not use SLNQ results.\n");
}
} else {
output_msg(OUTPUT_LOG, "Could not call SLNQ, row %d, unknowns %d, iteration %d\n", k+l, count_unknowns, iterations);
}
} else if (kode > 0) {
output_msg(OUTPUT_LOG, "Could not call SLNQ, row %d, unknowns %d\n", k+l, count_unknowns);
}
#endif
/* Copy delta1 into delta and scale */
memcpy( (void *) &(delta[0]), (void *) &(delta1[0]),
(size_t) count_unknowns * sizeof(LDBLE));
for (i = 0; i < count_unknowns; i++) delta[i] *= normal[i];
/*
* Rescale columns of array
*/
for (i = 0; i < count_unknowns; i++) {
if (normal[i] != 1.0) {
for (j=0; j < count_unknowns; j++) {
array[j * (count_unknowns + 1) + i] /= normal[i];
}
}
}
/*
* Debug, write results of ineq
*/
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE, "kode: %d\titer: %d\terror: %e\n", kode, iter, (double) error);
output_msg(OUTPUT_MESSAGE, "\nsolution vector:\n");
for (i=0; i < count_unknowns; i++) {
output_msg(OUTPUT_MESSAGE, "%6d %-12.12s %10.2e",i, x[i]->description, (double) delta[i]);
if (x[i]->type == PP) {
output_msg(OUTPUT_MESSAGE, " -SI %10.2e Moles %10.2e", (double) x[i]->f, (double) x[i]->moles);
if (x[i]->f < 0e-8 || x[i]->moles > 0.0) {
output_msg(OUTPUT_MESSAGE, " **");
}
}
output_msg(OUTPUT_MESSAGE, "\n");
}
output_msg(OUTPUT_MESSAGE, "\nresidual vector:\n");
for (i=0; i < count_rows; i++) {
output_msg(OUTPUT_MESSAGE, "%6d %-12.12s %10.2e\n",i, x[back[i]]->description, (double) res[i]);
}
}
normal = (LDBLE *) free_check_null(normal);
ineq_array = (LDBLE *) free_check_null(ineq_array);
back = (int *) free_check_null(back);
zero = (LDBLE *) free_check_null(zero);
res = (LDBLE *) free_check_null(res);
delta1 = (LDBLE *) free_check_null(delta1);
cu = (LDBLE *) free_check_null(cu);
iu = (int *) free_check_null(iu);
is = (int *) free_check_null(is);
#ifdef SLNQ
slnq_array = free_check_null(slnq_array);
slnq_delta1 = free_check_null(slnq_delta1);
#endif
return(return_code);
}
/* ---------------------------------------------------------------------- */
int jacobian_sums (void)
/* ---------------------------------------------------------------------- */
{
/*
* Fills in jacobian array, uses arrays sum_jacob0, sum_jacob1, and
* sum_jacob2.
*/
int i, j, k;
LDBLE sinh_constant;
/*
* Clear array, note residuals are in array[i, count_unknowns+1]
*/
for (i=0; i < count_unknowns; i++) {
array[i]=0.0;
}
for (i=1; i < count_unknowns; i++) {
memcpy ( (void *) &(array[i*(count_unknowns + 1)]), (void *) &(array[0]),
(size_t) count_unknowns * sizeof(LDBLE));
}
/*
* Add constant terms
*/
for (k=0; k < count_sum_jacob0; k++) {
*sum_jacob0[k].target += sum_jacob0[k].coef;
}
/*
* Add terms with coefficients of 1.0
*/
for (k=0; k < count_sum_jacob1; k++) {
*sum_jacob1[k].target += *sum_jacob1[k].source;
}
/*
* Add terms with coefficients != 1.0
*/
for (k=0; k < count_sum_jacob2; k++) {
*sum_jacob2[k].target += *sum_jacob2[k].source * sum_jacob2[k].coef;
}
/*
* Make final adustments to jacobian array
*/
/*
* Ionic strength
*/
if (mu_unknown != NULL) {
for (i=0; i<count_unknowns; i++) {
array[mu_unknown->number*(count_unknowns+1)+i] *= 0.5;
}
array[mu_unknown->number*(count_unknowns+1)+mu_unknown->number] -= mass_water_aq_x;
}
/*
* Mass of oxygen
*/
if (mass_oxygen_unknown != NULL && mu_unknown != NULL) {
array[mu_unknown->number*(count_unknowns+1)+mass_oxygen_unknown->number] -= mu_x * mass_water_aq_x;
}
/*
* Activity of water
*/
if (ah2o_unknown != NULL) {
for (i=0; i<count_unknowns; i++) {
array[ah2o_unknown->number*(count_unknowns+1)+i] *= -0.017;
}
array[ah2o_unknown->number*(count_unknowns+1)+ah2o_unknown->number] -= mass_water_aq_x*exp(s_h2o->la * LOG_10);
}
if (mass_oxygen_unknown != NULL && ah2o_unknown != NULL) {
array[ah2o_unknown->number*(count_unknowns+1)+mass_oxygen_unknown->number] -=
(exp(s_h2o->la * LOG_10) - 1) * mass_water_aq_x;
}
/*
* Surface charge balance
*/
if (surface_unknown != NULL && diffuse_layer_x == FALSE) {
sinh_constant = sqrt(8*EPSILON*EPSILON_ZERO*(R_KJ_DEG_MOL*1000)*tk_x*1000);
for (i=0; i<count_unknowns; i++) {
if (x[i]->type == SURFACE_CB && x[i]->surface_charge->grams > 0 ) {
for (j=0; j < count_unknowns; j++) {
array[x[i]->number * (count_unknowns + 1) + j] *=
F_C_MOL / (x[i]->surface_charge->specific_area * x[i]->surface_charge->grams);
}
array[x[i]->number * (count_unknowns + 1) + x[i]->number] -=
sinh_constant * sqrt(mu_x) * cosh(x[i]->master[0]->s->la * LOG_10);
if (mu_unknown != NULL) {
array[x[i]->number * (count_unknowns + 1) + mu_unknown->number] -=
0.5 * sinh_constant / sqrt(mu_x) * sinh(x[i]->master[0]->s->la * LOG_10);
}
}
}
}
return(OK);
}
/* ---------------------------------------------------------------------- */
int mb_sums (void)
/* ---------------------------------------------------------------------- */
{
/*
* Calculates sums of species for calculation of mass balances, charge
* balance. Also calculates saturation indices for solution_phase_boundaries
* and pure_phases. After this routine total calcium calculated from all
* calcium species in solution is stored in x[i]->f. Also calculates
* x[i]->sum for some types of unknowns. Uses arrays sum_mb1 and
* sum_mb1, which are generated in prep and reprep.
*/
int k;
/*
* Clear functions in unknowns
*/
for (k=0; k < count_unknowns; k++) {
x[k]->f=0.0;
x[k]->sum=0.0;
}
/*
* Add terms with coefficients of 1.0
*/
for (k=0; k < count_sum_mb1; k++) {
*sum_mb1[k].target += *sum_mb1[k].source;
/* { k += 1; k -= 1;} */
}
/*
* Add terms with coefficients != 1.0
*/
for (k=0; k < count_sum_mb2; k++) {
*sum_mb2[k].target += *sum_mb2[k].source * sum_mb2[k].coef;
/* { k += 1; k -= 1;} */
}
return(OK);
}
/* ---------------------------------------------------------------------- */
int mb_gases(void)
/* ---------------------------------------------------------------------- */
{
/*
* Determines whether gas_phase equation is needed
*/
gas_in = FALSE;
if (gas_unknown == NULL || use.gas_phase_ptr == NULL) return(OK);
if (use.gas_phase_ptr->type == PRESSURE) {
if (gas_unknown->f > use.gas_phase_ptr->total_p + 1e-7 ||
gas_unknown->moles > MIN_TOTAL) {
gas_in = TRUE;
}
} else {
gas_in = FALSE;
}
return(OK);
}
/* ---------------------------------------------------------------------- */
int mb_s_s(void)
/* ---------------------------------------------------------------------- */
{
int i, j;
LDBLE lp, log10_iap, total_moles;
LDBLE iapc, iapb, kc, kb, lc, lb, xcaq, xbaq, xb, xc;
LDBLE sigmapi_aq, sigmapi_solid;
LDBLE total_p;
struct s_s *s_s_ptr;
struct rxn_token *rxn_ptr;
struct phase *phase_ptr;
/*
* Determines whether solid solution equation is needed
*/
if (s_s_unknown == NULL || use.s_s_assemblage_ptr == NULL) return(OK);
for (i = 0; i < use.s_s_assemblage_ptr->count_s_s; i++) {
s_s_ptr = &(use.s_s_assemblage_ptr->s_s[i]);
total_moles = 0;
for (j = 0; j < s_s_ptr->count_comps; j++) {
total_moles += s_s_ptr->comps[j].moles;
}
if (total_moles > 1e-13) {
s_s_ptr->s_s_in = TRUE;
} else if (s_s_ptr->a0 != 0.0 || s_s_ptr->a1 != 0.0) {
/*
* Calculate IAPc and IAPb
*/
if (s_s_ptr->comps[0].phase->rxn_x != NULL) {
log10_iap = 0;
for (rxn_ptr = s_s_ptr->comps[0].phase->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++) {
log10_iap += rxn_ptr->s->la * rxn_ptr->coef;
}
iapc = exp(log10_iap * LOG_10);
} else {
iapc = 1e-99;
}
if (s_s_ptr->comps[1].phase->rxn_x != NULL) {
log10_iap = 0;
for (rxn_ptr = s_s_ptr->comps[1].phase->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++) {
log10_iap += rxn_ptr->s->la * rxn_ptr->coef;
}
iapb = exp(log10_iap * LOG_10);
} else {
iapb = 1e-99;
}
/*
* Calculate sigma pi, aq
*/
sigmapi_aq = iapc + iapb;
/*
* Calculate xc,aq and xb, aq
*/
xcaq = iapc/(iapb + iapc);
xbaq = iapb/(iapb + iapc);
/*
* Get Kc and Kb
*/
kc = exp(s_s_ptr->comps[0].phase->lk*LOG_10);
kb = exp(s_s_ptr->comps[1].phase->lk*LOG_10);
/*
* Solve for xb
*/
xb = s_s_root(s_s_ptr->a0, s_s_ptr->a1, kc, kb, xcaq, xbaq);
/*
* Calculate lambdac and lambdab
*/
xc = 1 - xb;
lc = exp((s_s_ptr->a0 - s_s_ptr->a1*(-4*xb + 3))*xb*xb);
lb = exp((s_s_ptr->a0 + s_s_ptr->a1*(4*xb - 3))*xc*xc);
/*
* Calculate sigma pi, solid
*/
sigmapi_solid = xb*lb*kb + xc*lc*kc;
/*
* If Sigma pi, solid < sigma pi, aq, then use eqns
*/
if (sigmapi_solid < sigmapi_aq) {
s_s_ptr->s_s_in = TRUE;
} else {
s_s_ptr->s_s_in = FALSE;
}
} else {
/*
* Calculate total mole fraction from solution activities
*/
total_p = 0;
for (j = 0; j < s_s_ptr->count_comps; j++) {
phase_ptr = s_s_ptr->comps[j].phase;
if (phase_ptr->in == TRUE) {
lp=-phase_ptr->lk;
for (rxn_ptr = s_s_ptr->comps[j].phase->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++) {
lp += rxn_ptr->s->la * rxn_ptr->coef;
}
total_p += exp(lp * LOG_10);
}
}
if (total_p > 1.0) {
s_s_ptr->s_s_in = TRUE;
} else {
s_s_ptr->s_s_in= FALSE;
}
}
}
for (i = s_s_unknown->number; i < count_unknowns; i++) {
if (x[i]->type != S_S_MOLES) break;
x[i]->s_s_in = x[i]->s_s->s_s_in;
}
return(OK);
}
/* ---------------------------------------------------------------------- */
int molalities (int allow_overflow)
/* ---------------------------------------------------------------------- */
{
/*
* Calculates la for master species
* Calculates lm and moles from lk, lg, and la's of master species
* Adjusts lm of h2 and o2.
*/
int i, j, count_g;
LDBLE total_g;
struct rxn_token *rxn_ptr;
/*
* la for master species
*/
for (i=0; i < count_master; i++) {
if (master[i]->in == REWRITE) {
master[i]->s->la = master[i]->s->lm + master[i]->s->lg;
}
}
if (diffuse_layer_x == TRUE) {
s_h2o->tot_g_moles = s_h2o->moles;
s_h2o->tot_dh2o_moles = 0.0;
}
for (i=0; i < count_s_x; i++) {
if (s_x[i]->type > HPLUS && s_x[i]->type != EX && s_x[i]->type != SURF) continue;
/*
* lm and moles for all aqueous species
*/
s_x[i]->lm=s_x[i]->lk - s_x[i]->lg;
for (rxn_ptr = s_x[i]->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++) {
s_x[i]->lm += rxn_ptr->s->la * rxn_ptr->coef;
}
if (s_x[i]->type == EX ) {
s_x[i]->moles = exp(s_x[i]->lm * LOG_10);
} else if (s_x[i]->type == SURF ) {
s_x[i]->moles = exp(s_x[i]->lm * LOG_10); /* formerly * mass water */
} else {
s_x[i]->moles = under (s_x[i]->lm) * mass_water_aq_x;
if (s_x[i]->moles/mass_water_aq_x > 30) {
output_msg(OUTPUT_LOG,"Overflow: %s\t%e\t%e\t%d\n", s_x[i]->name, (double) (s_x[i]->moles/mass_water_aq_x), (double) s_x[i]->lm, iterations);
if (iterations >= 0 && allow_overflow == FALSE) {
return(ERROR);
}
}
}
/*
* other terms for diffuse layer model
*/
if (use.surface_ptr != NULL && diffuse_layer_x == TRUE && s_x[i]->type <= HPLUS) {
total_g = 0.0;
s_x[i]->tot_dh2o_moles = 0.0;
for (j = 0; j < use.surface_ptr->count_charge; j++) {
count_g = s_x[i]->diff_layer[j].count_g;
#ifdef SKIP
/*
* original formulation incorrectly mixes concentrations of surface relative
* to moles_water_aq_x and concentrations of diffuse layer in terms
* of moles_water_bulk_x. Moles_water_bulk_x is wrong anyway because
* an individual surface sees only moles_water_aq_x + moles_water_surface
* (not sum of all surfaces)
*/
s_x[i]->diff_layer[j].g_moles = s_x[i]->moles *
(s_x[i]->diff_layer[j].charge->g[count_g].g *
mass_water_bulk_x / mass_water_aq_x +
s_x[i]->diff_layer[j].charge->mass_water /
mass_water_aq_x);
/* g.dg is dg/dx(-2y**2) or dg/d(ln y) */
s_x[i]->diff_layer[j].dx_moles = s_x[i]->moles *
s_x[i]->diff_layer[j].charge->g[count_g].dg *
mass_water_bulk_x / mass_water_aq_x;
total_g += s_x[i]->diff_layer[j].charge->g[count_g].g *
mass_water_bulk_x / mass_water_aq_x +
s_x[i]->diff_layer[j].charge->mass_water / mass_water_aq_x;
s_x[i]->diff_layer[j].dh2o_moles = - s_x[i]->moles *
(s_x[i]->diff_layer[j].charge->g[count_g].g + 1) *
s_x[i]->diff_layer[j].charge->mass_water / mass_water_aq_x;
s_x[i]->tot_dh2o_moles += s_x[i]->diff_layer[j].dh2o_moles;
/* surface related to phase */
s_x[i]->diff_layer[j].drelated_moles = s_x[i]->moles * (s_x[i]->diff_layer[j].charge->g[count_g].g + 1) * use.surface_ptr->charge[j].specific_area * use.surface_ptr->thickness / mass_water_aq_x;
#endif
/*
* partially corrected formulation assumes mass of water in diffuse layer
* is insignificant. Excess is calculated on the basis of moles_water_aq_x
* instead of moles_water_bulk.
*/
/* revised eq. 61 */
s_x[i]->diff_layer[j].g_moles = s_x[i]->moles *
(s_x[i]->diff_layer[j].charge->g[count_g].g +
s_x[i]->diff_layer[j].charge->mass_water /
mass_water_aq_x);
if (s_x[i]->moles > 1e-30) {
s_x[i]->diff_layer[j].dg_g_moles = s_x[i]->dg * s_x[i]->diff_layer[j].g_moles / s_x[i]->moles;
}
/*
* first term of 63 is summed for all surfaces in
* s_x[i]->tot_g_moles. This sum is then used in
* the jacobian for species i
*/
total_g += s_x[i]->diff_layer[j].charge->g[count_g].g + s_x[i]->diff_layer[j].charge->mass_water / mass_water_aq_x;
/* revised eq. 63, second term */
/* g.dg is dg/dx(-2y**2) or dg/d(ln y) */
s_x[i]->diff_layer[j].dx_moles = s_x[i]->moles * s_x[i]->diff_layer[j].charge->g[count_g].dg;
/* revised eq. 63, third term */
s_x[i]->diff_layer[j].dh2o_moles = - s_x[i]->moles * s_x[i]->diff_layer[j].charge->mass_water / mass_water_aq_x;
s_x[i]->tot_dh2o_moles += s_x[i]->diff_layer[j].dh2o_moles;
/* surface related to phase */
s_x[i]->diff_layer[j].drelated_moles = s_x[i]->moles *
use.surface_ptr->charge[j].specific_area *
use.surface_ptr->thickness / mass_water_aq_x;
}
s_x[i]->tot_g_moles = s_x[i]->moles * (1 + total_g);
/* note that dg is for cb, act water, mu eqns */
/* dg_total_g for mole balance eqns */
/* dg_g_moles for surface cb */
if (s_x[i]->moles > 1e-30) {
s_x[i]->dg_total_g = s_x[i]->dg * s_x[i]->tot_g_moles / s_x[i]->moles;
} else {
s_x[i]->dg_total_g = 0.0;
}
#ifdef SKIP
#endif
if (debug_diffuse_layer == TRUE) {
output_msg(OUTPUT_MESSAGE, "%s\t%e\t%e\n", s_x[i]->name, (double) s_x[i]->moles, (double) s_x[i]->tot_g_moles);
output_msg(OUTPUT_MESSAGE, "\tg\n");
for (j=0; j < use.surface_ptr->count_charge; j++) {
count_g = s_x[i]->diff_layer[j].count_g;
output_msg(OUTPUT_MESSAGE, "\t%e", (double) s_x[i]->diff_layer[j].charge->g[count_g].g);
}
output_msg(OUTPUT_MESSAGE, "\n\tg_moles\n");
for (j=0; j < use.surface_ptr->count_charge; j++) {
output_msg(OUTPUT_MESSAGE, "\t%e", (double) s_x[i]->diff_layer[j].g_moles);
}
output_msg(OUTPUT_MESSAGE, "\n\tdg\n");
for (j=0; j < use.surface_ptr->count_charge; j++) {
count_g = s_x[i]->diff_layer[j].count_g;
output_msg(OUTPUT_MESSAGE, "\t%e", (double) s_x[i]->diff_layer[j].charge->g[count_g].dg);
}
output_msg(OUTPUT_MESSAGE, "\n\tdx_moles\n");
for (j=0; j < use.surface_ptr->count_charge; j++) {
output_msg(OUTPUT_MESSAGE, "\t%e", (double) s_x[i]->diff_layer[j].dx_moles);
}
output_msg(OUTPUT_MESSAGE, "\n\tdh2o_moles\t%e\n", (double) s_x[i]->tot_dh2o_moles);
for (j=0; j < use.surface_ptr->count_charge; j++) {
output_msg(OUTPUT_MESSAGE, "\t%e", (double) s_x[i]->diff_layer[j].dh2o_moles);
}
output_msg(OUTPUT_MESSAGE, "\n");
}
}
}
if (use.gas_phase_ptr != NULL) calc_gas_pressures();
if (use.s_s_assemblage_ptr != NULL) calc_s_s_fractions();
return(OK);
}
/* ---------------------------------------------------------------------- */
int calc_gas_pressures(void)
/* ---------------------------------------------------------------------- */
{
int i;
LDBLE lp;
struct rxn_token *rxn_ptr;
struct gas_comp *gas_comp_ptr;
struct phase *phase_ptr;
/*
* moles and partial pressures for gases
*/
if (use.gas_phase_ptr == NULL) return(OK);
if (use.gas_phase_ptr->type == VOLUME) {
use.gas_phase_ptr->total_p = 0;
use.gas_phase_ptr->total_moles = 0;
}
for (i=0; i < use.gas_phase_ptr->count_comps; i++) {
gas_comp_ptr = &(use.gas_phase_ptr->comps[i]);
phase_ptr = use.gas_phase_ptr->comps[i].phase;
if (phase_ptr->in == TRUE) {
lp=-phase_ptr->lk;
for (rxn_ptr=phase_ptr->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++) {
lp += rxn_ptr->s->la * rxn_ptr->coef;
}
gas_comp_ptr->phase->p_soln_x = exp(lp * LOG_10);
if (use.gas_phase_ptr->type == PRESSURE) {
gas_comp_ptr->phase->moles_x = gas_comp_ptr->phase->p_soln_x *
gas_unknown->moles / gas_unknown->gas_phase->total_p;
gas_comp_ptr->phase->fraction_x = gas_comp_ptr->phase->moles_x / gas_unknown->moles;
} else {
gas_comp_ptr->phase->moles_x = gas_comp_ptr->phase->p_soln_x *
use.gas_phase_ptr->volume / (R_LITER_ATM * tk_x);
use.gas_phase_ptr->total_p += gas_comp_ptr->phase->p_soln_x;
use.gas_phase_ptr->total_moles += gas_comp_ptr->phase->moles_x;
}
} else {
gas_comp_ptr->phase->moles_x = 0;
gas_comp_ptr->phase->fraction_x = 0;
}
}
return(OK);
}
/* ---------------------------------------------------------------------- */
int calc_s_s_fractions(void)
/* ---------------------------------------------------------------------- */
{
int i, k;
LDBLE moles, n_tot;
struct s_s *s_s_ptr;
/*
* moles and lambdas for solid solutions
*/
if (s_s_unknown == NULL) return(OK);
/*
* Calculate mole fractions and log lambda and derivative factors
*/
if (use.s_s_assemblage_ptr == NULL) return(OK);
for (i = 0; i < use.s_s_assemblage_ptr->count_s_s; i++) {
s_s_ptr = &(use.s_s_assemblage_ptr->s_s[i]);
n_tot = 0;
for (k = 0; k < s_s_ptr->count_comps; k++) {
moles = s_s_ptr->comps[k].moles;
if (s_s_ptr->comps[k].moles < 0) {
moles = MIN_TOTAL;
s_s_ptr->comps[k].initial_moles = moles;
}
n_tot += moles;
}
s_s_ptr->total_moles = n_tot;
for (k = 0; k < s_s_ptr->count_comps; k++) {
moles = s_s_ptr->comps[k].moles;
if (s_s_ptr->comps[k].moles < 0) {
moles = MIN_TOTAL;
}
s_s_ptr->comps[k].fraction_x = moles / n_tot;
s_s_ptr->comps[k].log10_fraction_x = log10(moles / n_tot);
/* all mb and jacobian items must be in x or phase to be static between models */
s_s_ptr->comps[k].phase->log10_fraction_x = s_s_ptr->comps[k].log10_fraction_x;
}
if (s_s_ptr->a0 != 0.0 || s_s_ptr->a1 != 0) {
s_s_binary(s_s_ptr);
} else {
s_s_ideal(s_s_ptr);
}
}
return(OK);
}
/* ---------------------------------------------------------------------- */
int s_s_binary(struct s_s *s_s_ptr)
/* ---------------------------------------------------------------------- */
{
LDBLE nb, nc, n_tot, xb, xc, dnb, dnc, a0, a1;
LDBLE xb2, xb3, xb4, xc2, xc3;
LDBLE xb1, xc1;
/*
* component 0 is major component
* component 1 is minor component
* xb is the mole fraction of second component (formerly trace)
* xc is the mole fraction of first component (formerly major)
*/
/*
* Calculate mole fractions and log lambda and derivative factors
*/
n_tot = s_s_ptr->total_moles;
nc = s_s_ptr->comps[0].moles;
xc = nc/n_tot;
nb = s_s_ptr->comps[1].moles;
xb = nb/n_tot;
/*
* In miscibility gap
*/
a0 = s_s_ptr->a0;
a1 = s_s_ptr->a1;
if (s_s_ptr->miscibility == TRUE && xb > s_s_ptr->xb1 && xb < s_s_ptr->xb2) {
xb1 = s_s_ptr->xb1;
xc1 = 1.0 - xb1;
s_s_ptr->comps[0].fraction_x = xc1;
s_s_ptr->comps[0].log10_fraction_x = log10(xc1);
s_s_ptr->comps[0].phase->log10_fraction_x = s_s_ptr->comps[0].log10_fraction_x;
s_s_ptr->comps[1].fraction_x = xb1;
s_s_ptr->comps[1].log10_fraction_x = log10(xb1);
s_s_ptr->comps[1].phase->log10_fraction_x = s_s_ptr->comps[1].log10_fraction_x;
s_s_ptr->comps[0].log10_lambda = xb1*xb1*(a0 - a1*(3 -4*xb1))/LOG_10;
s_s_ptr->comps[0].phase->log10_lambda = s_s_ptr->comps[0].log10_lambda;
s_s_ptr->comps[1].log10_lambda = xc1*xc1*(a0 + a1*(4*xb1 - 1))/LOG_10;
s_s_ptr->comps[1].phase->log10_lambda = s_s_ptr->comps[1].log10_lambda;
s_s_ptr->comps[0].dnb = 0;
s_s_ptr->comps[0].dnc = 0;
s_s_ptr->comps[1].dnb = 0;
s_s_ptr->comps[1].dnc = 0;
s_s_ptr->comps[0].phase->dnb = 0;
s_s_ptr->comps[0].phase->dnc = 0;
s_s_ptr->comps[1].phase->dnb = 0;
s_s_ptr->comps[1].phase->dnc = 0;
} else {
/*
* Not in miscibility gap
*/
s_s_ptr->comps[0].fraction_x = xc;
s_s_ptr->comps[0].log10_fraction_x = log10(xc);
s_s_ptr->comps[0].phase->log10_fraction_x = s_s_ptr->comps[0].log10_fraction_x;
s_s_ptr->comps[1].fraction_x = xb;
s_s_ptr->comps[1].log10_fraction_x = log10(xb);
s_s_ptr->comps[1].phase->log10_fraction_x = s_s_ptr->comps[1].log10_fraction_x;
s_s_ptr->comps[0].log10_lambda = xb*xb*(a0 - a1*(3 -4*xb))/LOG_10;
s_s_ptr->comps[0].phase->log10_lambda = s_s_ptr->comps[0].log10_lambda;
s_s_ptr->comps[1].log10_lambda = xc*xc*(a0 + a1*(4*xb - 1))/LOG_10;
s_s_ptr->comps[1].phase->log10_lambda = s_s_ptr->comps[1].log10_lambda;
xc2 = xc*xc;
xc3 = xc2*xc;
xb2 = xb*xb;
xb3 = xb2*xb;
xb4 = xb3*xb;
#ifdef SKIP
/* first component */
dnb = -2*a0*xb*xc2 - 8*a1*xb2*xc2 + 6*a1*xb*xc2 - 4*a1*xc*xb4 - 8*a1*xb3*xc2 -
4*a1*xb2*xc3 - 2*a0*xc*xb2 - 8*a1*xc*xb3 + 6*a1*xc*xb2 + 1;
s_s_ptr->comps[0].phase->dnb = dnb/n_tot;
dnc = 2*a0*xb3 + 2*a0*xc*xb2 + 8*a1*xb4 + 8*a1*xc*xb3 - 2*a1*xb3 - 6*a1*xc*xb2;
s_s_ptr->comps[0].phase->dnc = - xb/nc + dnc/n_tot;
/* second component */
dnb = 2*a0*xb*xc2 + 2*a0*xc3 + 8*a1*xb2*xc2 + 8*a1*xb*xc3 - 2*a1*xb*xc2 -6*a1*xc3;
s_s_ptr->comps[1].phase->dnb = -xc/nb + dnb/n_tot;
dnc = -2*a0*xc*xb2 - 8*a1*xc*xb3 + 2*a1*xc*xb2 - 2*a0*xb*xc2 - 8*a1*xb2*xc2 + 6*a1*xb*xc2 + 1;
s_s_ptr->comps[1].phase->dnc = dnc/n_tot;
#endif
/* used derivation that did not substitute x2 = 1-x1 */
/* first component, df1/dn1*/
dnc = 2*a0*xb2 + 12*a1*xc*xb2 + 6*a1*xb2;
s_s_ptr->comps[0].phase->dnc = - xb/nc + dnc/n_tot;
/* first component, df1/dn2*/
dnb = 1 - 2*a0*xb + 2*a0*xb2 + 8*a1*xc*xb - 12*a1*xc*xb2 - 2*a1*xb + 2*a1*xb2;
s_s_ptr->comps[0].phase->dnb = dnb/n_tot;
/* second component, df2/dn1 */
dnc = 1 -2*a0*xc +2*a0*xc2 - 8*a1*xb*xc + 12*a1*xb*xc2 + 2*a1*xc - 2*a1*xc2;
s_s_ptr->comps[1].phase->dnc = dnc/n_tot;
/* second component, df2/dn2 */
dnb = 2*a0*xc2 + 12*a1*xb*xc2 - 6*a1*xc2;
s_s_ptr->comps[1].phase->dnb = -xc/nb + dnb/n_tot;
}
return(OK);
}
/* ---------------------------------------------------------------------- */
int s_s_ideal(struct s_s *s_s_ptr)
/* ---------------------------------------------------------------------- */
{
int k, j;
LDBLE n_tot, n_tot1;
/*
* component 0 is major component
* component 1 is minor component
* xb is the mole fraction of second component (formerly trace)
* xc is the mole fraction of first component (formerly major)
*/
/*
* Calculate mole fractions and log lambda and derivative factors
*/
n_tot = s_s_ptr->total_moles;
/*
* Ideal solid solution
*/
s_s_ptr->dn = 1.0 / n_tot;
for (k = 0; k < s_s_ptr->count_comps; k++) {
n_tot1 = 0;
for (j = 0; j < s_s_ptr->count_comps; j++) {
if (j != k) {
n_tot1 += s_s_ptr->comps[j].moles;
}
}
s_s_ptr->comps[k].log10_lambda = 0;
s_s_ptr->comps[k].phase->log10_lambda = 0;
s_s_ptr->comps[k].dnb = -(n_tot1)/(s_s_ptr->comps[k].moles*n_tot);
s_s_ptr->comps[k].phase->dnb = s_s_ptr->comps[k].dnb;
s_s_ptr->comps[k].dn = s_s_ptr->dn;
s_s_ptr->comps[k].phase->dn = s_s_ptr->dn;
}
return(OK);
}
/* ---------------------------------------------------------------------- */
int reset(void)
/* ---------------------------------------------------------------------- */
{
/*
* Checks deltas (changes to unknowns) to make sure they are reasonable
* Scales deltas if necessary
* Updates unknowns with deltas
*/
int i, j;
int converge;
LDBLE up, down;
LDBLE d;
LDBLE factor, f0;
LDBLE sum_deltas;
LDBLE step_up;
#ifdef SKIP
LDBLE epsilon;
#endif
LDBLE mu_calc;
LDBLE old_moles;
/*appt char name[MAX_LENGTH]; LDBLE surf_chrg_eq;
*/
/*
* Calculate interphase mass transfers
*/
#ifdef SKIP
if (punch.high_precision == FALSE)
epsilon = 1e-9;
else
epsilon = 1.e-12;
#endif
step_up = log(step_size_now);
factor = 1.;
if ( pure_phase_unknown != NULL || s_s_unknown != NULL) {
/*
* Don't take out more mineral than is present
*/
for (i=0; i<count_unknowns; i++) {
if (x[i]->type == PP || x[i]->type == S_S_MOLES) {
if (/* delta[i] > 0.0 && */ x[i]->moles > 0.0 && delta[i] > x[i]->moles ) {
f0 = delta[i] / x[i]->moles;
if (f0 > factor) {
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"%-10.10s, Removing more than total mineral.\t%f\n",
x[i]->description, (double) f0);
}
factor=f0;
}
} else if ( delta[i] > 0.0 && x[i]->moles <= 0.0 ) {
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE, "%-10.10s\tDelta: %e\tMass: %e "
"Dissolving mineral with 0.0 mass.\n ",
x[i]->description, (double) delta[i], (double) x[i]->moles);
}
delta[i] = 0.0 ;
} else if (delta[i] < 0.0 && x[i]->moles > 0.0 && delta[i] < -100.0 ) {
f0 = -delta[i] / 100.0 ;
if (f0 > factor) {
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"%-10.10s, Precipitating too much mineral.\t%f\n", x[i]->description, (double) f0);
}
factor=f0;
}
}
}
}
}
/*
* Calculate change in element concentrations due to pure phases and gases
*/
if (pure_phase_unknown != NULL || gas_unknown != NULL || s_s_unknown != NULL) {
for(i=0; i < count_unknowns; i++) {
x[i]->delta = 0.0;
}
for (i=0; i < count_sum_delta; i++) {
*sum_delta[i].target += *sum_delta[i].source * sum_delta[i].coef;
}
/*
* Apply factor from minerals to deltas
*/
for(i=0; i < count_unknowns; i++) {
x[i]->delta /= factor;
if (x[i]->type == PP || x[i]->type == S_S_MOLES) delta[i] /= factor;
}
}
/*
* Calc factor for mass balance equations for aqueous unknowns
*/
factor=1.0;
sum_deltas=0.0;
for (i=0; i < count_unknowns; i++) {
sum_deltas += fabs(delta[i]);
up = step_up;
down = up;
if (x[i]->type <= SOLUTION_PHASE_BOUNDARY) {
up = step_up;
down = 1.3 * up;
} else if (x[i]->type == MU) {
up = 100 * mu_x;
#ifdef SKIP
if (up > 0.1) {
up = 2 * mu_x;
up = 0.1;
}
#endif
#ifdef SKIP
down = 0.7 * mu_x;
if (down < 0.1) down = 0.1;
#endif
down = mu_x;
} else if (x[i]->type == AH2O) {
down = up;
} else if (x[i]->type == MH) {
up = log(pe_step_size_now);
down = 1.3*up;
} else if (x[i]->type == MH2O) {
/* ln gH2O + delta; ln(gH2O*delta); */
/*
up = log(10.);
down = log(4.);
*/
up = log(1.3);
down = log(1.2);
} else if (x[i]->type == PP) {
continue;
} else if (x[i]->type == GAS_MOLES) {
up = 1000. * x[i]->moles;
if (up <= 0.0) up = 1e-1;
if (up >= 1.0) up = 1.;
down = x[i]->moles;
} else if (x[i]->type == S_S_MOLES) {
continue;
} else if (x[i]->type == EXCH) {
up = step_up;
down = 1.3 * up;
} else if (x[i]->type == SURFACE) {
up = step_up;
down = 1.3 * up;
} else if (x[i]->type == SURFACE_CB) {
up = step_up;
down = 1.3 * up;
}
if (delta[i] > 0.0) {
f0=delta[i]/up;
if (f0 > factor) {
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"%-10.10s\t%f\n",x[i]->description, (double) f0);
}
factor=f0;
}
} else {
f0=delta[i]/(-down);
if (f0 > factor) {
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"%-10.10s\t%f\n",x[i]->description, (double) f0);
}
factor=f0;
}
}
}
/*converge=TRUE;*/
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"\nSum of deltas: %12.6f\n", (double) sum_deltas);
output_msg(OUTPUT_MESSAGE,"Factor: %12.4e\n", (double) factor);
}
factor = 1.0/factor;
for (i=0; i<count_unknowns; i++) {
if (x[i]->type != PP && x[i]->type != S_S_MOLES) delta[i] *= factor;
}
/*
* Solution mass balances: MB, ALK, CB, SOLUTION_PHASE_BOUNDARY
*/
for (i=0; i < count_unknowns ; i++) {
if (x[i]->type == MB || x[i]->type == ALK || x[i]->type == EXCH || x[i]->type == SURFACE) {
/*if ( fabs(delta[i]) >= epsilon ) converge = FALSE;*/
d = delta[i] / LOG_10;
/* surface */
if (x[i]->type == SURFACE) {
old_moles = x[i]->moles;
if (x[i]->phase_unknown != NULL) {
x[i]->moles = x[i]->surface_comp->phase_proportion *
(x[i]->phase_unknown->moles -
delta[x[i]->phase_unknown->number]);
if (x[i]->phase_unknown->moles - delta[x[i]->phase_unknown->number] <= MIN_RELATED_SURFACE) {
x[i]->moles = 0.0;
if (fabs(x[i]->f) > MIN_RELATED_SURFACE) {
x[i]->master[0]->s->la -= 5.;
}
}
if (old_moles <= 0 && x[i]->moles > 0) {
x[i]->master[0]->s->la = log10(x[i]->moles) - 5.;
}
}
}
/* exch */
if (x[i]->type == EXCH && x[i]->moles <= MIN_RELATED_SURFACE) {
x[i]->moles = 0.0;
if (fabs(x[i]->f) > MIN_RELATED_SURFACE) {
x[i]->master[0]->s->la -= 5.;
}
}
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e "
"%-8s%10.2e\n", x[i]->description,
"old la", (double) x[i]->master[0]->s->la,
"new la", (double) x[i]->master[0]->s->la + (double) d,
"delta", (double) delta[i], "delta/c", (double) d);
}
x[i]->master[0]->s->la += d;
if (x[i]->master[0]->s->la < (double) (DBL_MIN_10_EXP+10)) x[i]->master[0]->s->la = (double) (DBL_MIN_10_EXP+10);
/*
* Surface charge balance
*/
} else if ( x[i]->type == SURFACE_CB ) {
d = delta[i] / LOG_10;
if (x[i]->phase_unknown != NULL) {
x[i]->surface_charge->grams = /* !!!! x[i]->surface_comp->phase_proportion * */
(x[i]->phase_unknown->moles -
delta[x[i]->phase_unknown->number]);
if (x[i]->surface_charge->grams <= MIN_RELATED_SURFACE) {
x[i]->surface_charge->grams = 0.0;
}
}
if (x[i]->surface_charge->grams <= MIN_RELATED_SURFACE) {
x[i]->surface_charge->grams = 0.0;
}
#ifdef SKIP
else if (fabs(delta[i]) > epsilon) {
converge=FALSE;
}
#endif
x[i]->related_moles = x[i]->surface_charge->grams;
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e\n",
x[i]->description, "old f*psi", (double) x[i]->master[0]->s->la, "new f*psi", (double) x[i]->master[0]->s->la + (double) d,"delta", (double) d);
}
x[i]->master[0]->s->la += d;
/* recalculte g's for component */
if (diffuse_layer_x == TRUE) {
if (debug_diffuse_layer == TRUE) {
output_msg(OUTPUT_MESSAGE, "\ncharge, old g, new g, dg*delta,"
" dg, delta\n");
}
for (j = 0; j < x[i]->surface_charge->count_g; j++) {
if (debug_diffuse_layer == TRUE) {
output_msg(OUTPUT_MESSAGE, "%12f\t%12.4e\t%12.4e\t%12.4e\t%12.4e\t%12.4e\n",
(double) x[i]->surface_charge->g[j].charge,
(double) x[i]->surface_charge->g[j].g,
(double) x[i]->surface_charge->g[j].g +
(double) (x[i]->surface_charge->g[j].dg * delta[i]),
(double) (x[i]->surface_charge->g[j].dg * delta[i]),
(double) x[i]->surface_charge->g[j].dg,
(double) delta[i]);
}
/*appt*/ if (use.surface_ptr->donnan != TRUE) {
x[i]->surface_charge->g[j].g += x[i]->surface_charge->g[j].dg * delta[i];
}
}
/*appt*/ if (use.surface_ptr->donnan == TRUE) {
calc_all_donnan();
}
}
/* Solution phase boundary */
} else if ( x[i]->type == SOLUTION_PHASE_BOUNDARY ) {
/*if (fabs(delta[i]) > epsilon) converge=FALSE;*/
d = delta[i] / LOG_10;
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e %-8s%10.2e\n", x[i]->description, "old la", (double) x[i]->master[0]->s->la, "new la", (double) (x[i]->master[0]->s->la + d), "delta", (double) delta[i], "delta/c", (double) d);
}
x[i]->master[0]->s->la += d;
/* Charge balance */
} else if ( x[i]->type == CB ) {
/*if (fabs(delta[i]) > epsilon * mu_x * mass_water_aq_x ) converge=FALSE;*/
d = delta[i] / LOG_10;
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e %-8s%10.2e\n", x[i]->description, "old la", (double) x[i]->master[0]->s->la, "new la", (double) (x[i]->master[0]->s->la + d), "delta", (double) delta[i], "delta/c", (double) d);
}
x[i]->master[0]->s->la += d;
/* Ionic strength */
} else if (x[i]->type == MU) {
/*if (fabs(delta[i]) > epsilon * mu_x ) converge=FALSE;*/
mu_calc = 0.5*mu_unknown->f/mass_water_aq_x;
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"Calculated mu: %e\n", (double) mu_calc);
output_msg(OUTPUT_MESSAGE,"%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e\n",
x[i]->description, "old mu", (double) mu_x, "new mu",
(double) (mu_x + delta[i]),
"delta", (double) delta[i]);
}
d = mu_x + delta[i];
if (d < 1e-7) {
delta[i] = sqrt(mu_calc * mu_x) - mu_x;
mu_x = sqrt(mu_calc * mu_x);
} else {
mu_x += delta[i];
}
if (mu_x <= 1e-8) {
mu_x = 1e-8;
}
/* Activity of water */
} else if (x[i]->type == AH2O) {
/*if (pitzer_model == TRUE && full_pitzer == FALSE) continue;*/
/*if (fabs(delta[i]) > epsilon) converge=FALSE;*/
d = delta[i] / LOG_10;
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e %-8s%10.2e\n", x[i]->description, "old la", (double) x[i]->master[0]->s->la, "new la", (double) (x[i]->master[0]->s->la + d), "delta", (double) delta[i], "delta/c", (double) d);
}
s_h2o->la += d;
if (pitzer_model == FALSE) {
if (s_h2o->la < -1.0) {
d = -1.0 - s_h2o->la;
delta[i] = d * LOG_10;
s_h2o->la = -1.0;
}
}
/* pe */
} else if (x[i]->type == MH) {
/*if (fabs(delta[i]) > epsilon) converge=FALSE;*/
d = delta[i] / LOG_10;
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e %-8s%10.2e\n", x[i]->description, "old pe", (double) x[i]->master[0]->s->la, "new pe", (double) (x[i]->master[0]->s->la + d), "delta", (double) delta[i], "delta/c", (double) d);
}
s_eminus->la += d;
/* Mass of water */
} else if (x[i]->type == MH2O) {
if (mass_water_switch == TRUE) continue;
/*if (fabs(delta[i]) > epsilon * mass_water_aq_x) converge=FALSE;*/
/* ln(gh2o) + delta, log(gh2o) + d, gh2o * 10**d */
d = exp(delta[i]);
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e %-8s%10.2e\n", x[i]->description, "old MH2O", (double) mass_water_aq_x, "new MH2O", (double) (mass_water_aq_x * d), "delta", (double) delta[i], "10**d/c", (double) d);
}
mass_water_aq_x *= d;
mass_water_bulk_x = mass_water_aq_x + mass_water_surfaces_x;
if (debug_model == TRUE && diffuse_layer_x == TRUE) {
output_msg(OUTPUT_MESSAGE,"mass_water bulk: %e\taq: %e\tsurfaces: %e\n",
(double) mass_water_bulk_x, (double) mass_water_aq_x, (double) mass_water_surfaces_x);
}
x[i]->master[0]->s->moles = mass_water_aq_x/gfw_water;
if (mass_water_aq_x < 1e-10) {
sprintf(error_string, "Mass of water is less than 1e-10 kilogram.\n"
"The aqueous phase may not be stable relative to given masses of minerals.");
warning_msg(error_string);
stop_program = TRUE;
return(TRUE);
}
/* Pure phases */
} else if (x[i]->type == PP) {
/*if (fabs(delta[i]) > epsilon) converge=FALSE;*/
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e\n",
x[i]->description, "old mass", (double) x[i]->moles,
"new mass", (double) (x[i]->moles - delta[i]), "delta", (double) delta[i]);
}
x[i]->moles -= delta[i];
/* if (fabs(x[i]->moles) < MIN_RELATED_SURFACE) x[i]->moles = 0.0; */
} else if (x[i]->type == GAS_MOLES) {
/*if (fabs(delta[i]) > epsilon) converge=FALSE;*/
/*if (gas_in == TRUE && fabs(residual[i]) > epsilon) converge=FALSE;*/
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e\n",
x[i]->description, "old mol", (double) x[i]->moles,
"new mol", (double) (x[i]->moles + delta[i]),
"delta", (double) delta[i]);
}
x[i]->moles += delta[i];
if (x[i]->moles < MIN_TOTAL) x[i]->moles = MIN_TOTAL;
} else if (x[i]->type == S_S_MOLES) {
/*if (fabs(delta[i]) > epsilon) converge=FALSE;*/
/*if (x[i]->s_s_in == TRUE && fabs(residual[i]) > epsilon) converge=FALSE;*/
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e\n",
x[i]->description, "old mol", (double) x[i]->moles,
"new mol", (double) (x[i]->moles - delta[i]),
"delta", (double) delta[i]);
}
x[i]->moles -= delta[i];
if (x[i]->moles < MIN_TOTAL) x[i]->moles = MIN_TOTAL;
x[i]->s_s_comp->moles = x[i]->moles;
/* Pitzer gamma */
} else if ( x[i]->type == PITZER_GAMMA ) {
if (full_pitzer == FALSE) continue;
d = delta[i];
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e %-8s%10.2e\n", x[i]->description, "old lg", (double) x[i]->s->lg, "new lg", (double) (x[i]->s->lg + d), "delta", (double) delta[i], "delta", (double) d);
}
x[i]->s->lg += d;
}
}
/*
* Reset total molalities in mass balance equations
*/
if (pure_phase_unknown != NULL || gas_unknown != NULL || s_s_unknown != NULL) {
for (i=0; i<count_unknowns; i++) {
if (x[i]->type == MB || x[i]->type == MH ||
x[i]->type == MH2O ||
x[i]->type == CB || x[i]->type == EXCH ||
x[i]->type == SURFACE ) {
/*if (fabs(x[i]->delta) > epsilon*x[i]->moles) converge = FALSE;*/
if (x[i]->type == SURFACE) x[i]->delta = 0.0;
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e\n",
x[i]->description, "old mole", (double) x[i]->moles,
"new mole", (double) (x[i]->moles + x[i]->delta),
"delta", (double) x[i]->delta);
}
x[i]->moles += x[i]->delta;
}
}
}
converge = FALSE;
return(converge);
}
/* ---------------------------------------------------------------------- */
int residuals(void)
/* ---------------------------------------------------------------------- */
{
/*
* Calculates residuals for all equations
*/
int i;
int converge;
LDBLE toler;
LDBLE sum_residual;
LDBLE sinh_constant;
sum_residual = 0.0;
/*
* Calculate residuals
*/
converge = TRUE;
#ifdef SKIP
if (punch.high_precision == FALSE)
toler = 1e-8;
else
toler = 1.e-12;
#endif
toler = convergence_tolerance;
for (i=0; i < count_unknowns; i++) {
if (x[i]->type == MB ) {
residual[i] = x[i]->moles - x[i]->f;
if (fabs(residual[i]) > toler * x[i]->moles && x[i]->moles > MIN_TOTAL ) {
/*
fprintf(stderr,"Residuals %d: %s %d %e\n", iterations, x[i]->description, i, residual[i]);
*/
converge = FALSE;
}
} else if (x[i]->type == ALK) {
residual[i] = x[i]->moles - x[i]->f;
if (fabs(residual[i]) > toler * x[i]->moles ) converge = FALSE;
} else if (x[i]->type == SOLUTION_PHASE_BOUNDARY) {
residual[i] = x[i]->f * LOG_10;
if (fabs(residual[i]) > toler ) converge = FALSE;
} else if (x[i]->type == CB) {
residual[i] = - x[i]->f;
if (ph_unknown == charge_balance_unknown) {
residual[i] += x[i]->moles;
}
if (fabs(residual[i]) >= toler * mu_x * mass_water_aq_x ) converge = FALSE;
} else if (x[i]->type == MU && pitzer_model == FALSE) {
residual[i] = mass_water_aq_x * mu_x - 0.5*x[i]->f;
if (fabs(residual[i]) > toler*mu_x * mass_water_aq_x ) converge = FALSE;
} else if (x[i]->type == AH2O /*&& pitzer_model == FALSE*/) {
/*if (pitzer_model == TRUE && full_pitzer == FALSE) continue;*/
residual[i] = mass_water_aq_x*exp(s_h2o->la * LOG_10) - mass_water_aq_x + 0.017 * x[i]->f;
if (pitzer_model) {
residual[i] = pow(10.0,s_h2o->la) - AW;
if (full_pitzer == FALSE) {
residual[i] = 0.0;
}
}
if (fabs(residual[i]) > toler ) converge = FALSE;
} else if (x[i]->type == MH && pitzer_model == FALSE) {
#ifdef COMBINE
residual[i] = x[i]->moles - x[i]->f;
#else
residual[i] = (x[i]->moles - 2*s_h2o->moles) - x[i]->f;
x[i]->f += 2 * s_h2o->moles;
#endif
if (mass_water_switch == TRUE) {
residual[i] -= 2 * (mass_oxygen_unknown->moles - mass_oxygen_unknown->f);
}
#ifdef COMBINE
#ifndef COMBINE_CHARGE
if (fabs(residual[i]) > toler*(x[i]->moles + 2 * mass_oxygen_unknown->moles)) converge = FALSE;
#else
if (fabs(residual[i]) > toler*(x[i]->moles + 2 * mass_oxygen_unknown->moles + charge_balance_unknown->moles)) converge = FALSE;
#endif
#else
if (fabs(residual[i]) > toler*x[i]->moles ) converge = FALSE;
#endif
} else if (x[i]->type == MH2O) {
if (mass_water_switch == TRUE) continue;
#ifdef COMBINE
residual[i] = x[i]->moles - x[i]->f;
#else
residual[i] = (x[i]->moles - s_h2o->moles) - x[i]->f;
x[i]->f += s_h2o->moles;
#endif
if (fabs(residual[i]) > 0.01 * toler*x[i]->moles ) converge = FALSE;
} else if (x[i]->type == PP) {
residual[i] = x[i]->f * LOG_10;
if (x[i]->pure_phase->add_formula == NULL) {
if (x[i]->dissolve_only == TRUE) {
if ((residual[i] > toler && x[i]->moles > 0.0) || (residual[i] < -toler && (x[i]->pure_phase->initial_moles - x[i]->moles) > 0)) {
converge = FALSE;
}
} else {
if (residual[i] < -toler || iterations < 1) converge = FALSE;
}
} else {
/* if (x[i]->moles > 0.0 && fabs(residual[i]) > toler) converge = FALSE;*/
if (residual[i] < -toler || iterations < 1) {
converge = FALSE;
}
}
} else if (x[i]->type == GAS_MOLES) {
residual[i] = x[i]->gas_phase->total_p - x[i]->f;
if (fabs(residual[i]) > toler && gas_in == TRUE) converge = FALSE;
} else if (x[i]->type == S_S_MOLES) {
residual[i] = x[i]->f * LOG_10;
if (fabs(residual[i]) > toler && x[i]->s_s_in == TRUE) converge = FALSE;
} else if (x[i]->type == EXCH) {
residual[i] = x[i]->moles - x[i]->f;
if (x[i]->moles <= MIN_RELATED_SURFACE) {
if (fabs(residual[i]) > toler) converge = FALSE;
} else if (fabs(residual[i]) > toler * x[i]->moles ) {
converge = FALSE;
}
} else if (x[i]->type == SURFACE) {
residual[i] = x[i]->moles - x[i]->f;
if (x[i]->moles <= MIN_RELATED_SURFACE) {
if ( fabs(residual[i]) > toler ) converge = FALSE;
} else if (fabs(residual[i]) > toler * x[i]->moles ) {
converge = FALSE;
}
} else if (x[i]->type == PITZER_GAMMA) {
if (full_pitzer == FALSE) continue;
residual[i] = x[i]->s->lg - x[i]->s->lg_pitzer;
if (fabs(residual[i]) > toler ) {
/*
fprintf(stderr,"Residuals %d: %s %d %e\n", iterations, x[i]->description, i, residual[i]);
*/
converge = FALSE;
}
} else if (x[i]->type == SURFACE_CB) {
/*sinh_constant = 0.1174;*/
sinh_constant = sqrt(8*EPSILON*EPSILON_ZERO*(R_KJ_DEG_MOL*1000)*tk_x*1000);
/* if (x[i]->surface_charge->grams <= MIN_RELATED_SURFACE) { */
if (x[i]->surface_charge->grams == 0) {
residual[i] = 0.0;
} else if (diffuse_layer_x == TRUE) {
residual[i] = - x[i]->f;
} else {
/*
* sinh_constant is (8 e e0 R T 1000)**1/2
* = sqrt(8*EPSILON*EPSILON_ZERO*(R_KJ_DEG_MOL*1000)*t_x*1000)
* ~ 0.1174 at 25C
*/
residual[i] = sinh_constant * sqrt(mu_x) *
sinh(x[i]->master[0]->s->la * LOG_10) -
x[i]->f * F_C_MOL /
(x[i]->surface_charge->specific_area * x[i]->surface_charge->grams);
}
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE,"Charge/Potential\n");
if (x[i]->surface_charge->grams > 0) {
output_msg(OUTPUT_MESSAGE,"\tSum of surface charge %e eq\n",
(double) (x[i]->f /* F_C_MOL / (x[i]->surface_charge->specific_area * x[i]->surface_charge->grams)*/));
} else {
output_msg(OUTPUT_MESSAGE,"\tResidual %e\n", (double) x[i]->f );
}
output_msg(OUTPUT_MESSAGE,"\t grams %g\n", (double) x[i]->surface_charge->grams);
output_msg(OUTPUT_MESSAGE,"\tCharge from potential %e eq\n",
(double) (x[i]->surface_charge->specific_area * x[i]->surface_charge->grams / F_C_MOL *
sinh_constant * sqrt(mu_x) *
sinh(x[i]->master[0]->s->la * LOG_10)));
output_msg(OUTPUT_MESSAGE,"\t FPsi/2RT %e\n",
(double) (x[i]->master[0]->s->la * LOG_10));
output_msg(OUTPUT_MESSAGE,"\t Sinh(FPsi/2RT) %e\n",
sinh(x[i]->master[0]->s->la * LOG_10));
output_msg(OUTPUT_MESSAGE,"\t Cosh(FPsi/2RT) %e\n",
cosh(x[i]->master[0]->s->la * LOG_10));
output_msg(OUTPUT_MESSAGE,"\t Sqrt(mu_x) %e\n",
sqrt(mu_x));
}
if (x[i]->surface_charge->grams > MIN_RELATED_SURFACE && fabs(residual[i]) > toler ) converge = FALSE;
}
/*
* Store residuals in array
*/
array[(i+1)*(count_unknowns+1)-1] = residual[i];
sum_residual += fabs(residual[i]);
}
/*
* Return
*/
if (pitzer_model == TRUE && iterations < 1) return(OK);
if (converge == TRUE ) {
return (CONVERGED);
}
return(OK);
}
/* ---------------------------------------------------------------------- */
int set(int initial)
/* ---------------------------------------------------------------------- */
{
/*
* Sets initial guesses for unknowns if initial == TRUE
* Revises guesses whether initial is true or not
*/
int i;
struct solution *solution_ptr;
/*
* Set initial log concentrations to zero
*/
if (pitzer_model == TRUE) return(set_pz(initial));
iterations = -1;
solution_ptr = use.solution_ptr;
for (i=0; i < count_s_x; i++) {
s_x[i]->lm = LOG_ZERO_MOLALITY;
s_x[i]->lg = 0.0;
}
/*
* Set master species activities
*/
tc_x=solution_ptr->tc;
tk_x=tc_x+273.15;
/*
* H+, e-, H2O
*/
mass_water_aq_x = solution_ptr->mass_water;
mu_x = solution_ptr->mu;
s_h2o->moles = mass_water_aq_x/gfw_water;
s_h2o->la = log10(solution_ptr->ah2o);
s_hplus->la = - solution_ptr->ph;
s_hplus->lm = s_hplus->la;
s_hplus->moles = exp(s_hplus->lm * LOG_10)*mass_water_aq_x;
s_eminus->la= - solution_ptr->solution_pe;
if (initial == TRUE) initial_guesses();
if (diffuse_layer_x == TRUE) initial_surface_water();
revise_guesses();
return(OK);
}
/* ---------------------------------------------------------------------- */
int initial_guesses(void)
/* ---------------------------------------------------------------------- */
{
/*
* Make initial guesses for activities of master species and
* ionic strength
*/
int i;
struct solution *solution_ptr;
solution_ptr = use.solution_ptr;
mu_x = s_hplus->moles + exp((solution_ptr->ph - 14.) * LOG_10) * mass_water_aq_x;
mu_x /= mass_water_aq_x;
s_h2o->la=0.0;
for ( i=0; i < count_unknowns; i++ ) {
if (x[i] == ph_unknown || x[i] == pe_unknown ) continue;
if (x[i]->type < CB) {
mu_x += x[i]->moles / mass_water_aq_x * 0.5 * x[i]->master[0]->s->z *
x[i]->master[0]->s->z;
x[i]->master[0]->s->la = log10(x[i]->moles/mass_water_aq_x);
} else if (x[i]->type == CB) {
x[i]->master[0]->s->la = log10(0.001 * x[i]->moles/mass_water_aq_x);
} else if (x[i]->type == SOLUTION_PHASE_BOUNDARY) {
x[i]->master[0]->s->la = log10(0.001 * x[i]->moles/mass_water_aq_x);
} else if (x[i]->type == EXCH) {
if (x[i]->moles <= 0) {
x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY;
} else {
x[i]->master[0]->s->la = log10(x[i]->moles);
}
} else if (x[i]->type == SURFACE) {
if (x[i]->moles <= 0) {
x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY;
} else {
x[i]->master[0]->s->la = log10(0.1 * x[i]->moles);
}
} else if (x[i]->type == SURFACE_CB) {
x[i]->master[0]->s->la = 0.0;
}
}
return(OK);
}
/* ---------------------------------------------------------------------- */
int revise_guesses(void)
/* ---------------------------------------------------------------------- */
{
/*
* Revise la's of master species
*/
int i;
int iter, max_iter, repeat, fail;
LDBLE weight, f;
/*appt char name[MAX_LENGTH]; LDBLE surf_chrg_eq;
*/
max_iter = 10;
gammas(mu_x);
iter = 0;
repeat = TRUE;
fail = FALSE;;
while ( repeat == TRUE ) {
iter++;
if (debug_set == TRUE) {
output_msg(OUTPUT_MESSAGE,"\nBeginning set iteration %d.\n", iter);
}
if (iter == max_iter + 1) {
output_msg(OUTPUT_LOG, "Did not converge in set, iteration %d.\n", iterations);
fail = TRUE;
}
if (iter > 2*max_iter) {
output_msg(OUTPUT_LOG, "Did not converge with relaxed criteria in set.\n");
return(OK);
}
molalities(TRUE);
mb_sums();
if (state < REACTION) {
sum_species();
} else {
for (i = 0; i < count_unknowns; i++) {
x[i]->sum = x[i]->f;
}
}
/* debug
if (debug_set == TRUE) {
pr.species = TRUE;
pr.all = TRUE;
print_species();
}
*/
repeat=FALSE;
for ( i=0; i < count_unknowns; i++ ) {
if (x[i] == ph_unknown || x[i] == pe_unknown) continue;
if (x[i]->type == MB ||
/* x[i]->type == ALK || */
x[i]->type == CB ||
x[i]->type == SOLUTION_PHASE_BOUNDARY ||
x[i]->type == EXCH ||
x[i]->type == SURFACE ) {
if ( debug_set == TRUE ) {
output_msg(OUTPUT_MESSAGE,"\n\t%5s at beginning of set %d: %e\t%e\t%e\n", x[i]->description, iter, (double) x[i]->sum, (double) x[i]->moles, (double) x[i]->master[0]->s->la);
}
if (fabs(x[i]->moles) < 1e-30) x[i]->moles = 0;
f = fabs(x[i]->sum);
if (f == 0 && x[i]->moles == 0) {
x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY;
continue;
} else if (f == 0) {
repeat = TRUE;
x[i]->master[0]->s->la += 5;
/*!!!!*/ if (x[i]->master[0]->s->la < -999.) x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY;
} else if (fail == TRUE && f < 1.5 * fabs(x[i]->moles)) {
continue;
} else if (f > 1.5 * fabs(x[i]->moles) || f < 1e-5 * fabs(x[i]->moles) ) {
weight = (f < 1e-5 * fabs(x[i]->moles)) ? 0.3 : 1.0;
if (x[i]->moles <= 0) {
x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY;
} else {
repeat = TRUE;
x[i]->master[0]->s->la += weight * log10(fabs(x[i]->moles / x[i]->sum));
}
if ( debug_set == TRUE ) {
output_msg(OUTPUT_MESSAGE,"\t%5s not converged in set %d: %e\t%e\t%e\n", x[i]->description, iter, (double) x[i]->sum, (double) x[i]->moles, (double) x[i]->master[0]->s->la);
}
}
} else if (x[i]->type == ALK) {
f = total_co2;
if (fail == TRUE && f < 1.5 * fabs(x[i]->moles)) {
continue;
}
if (f > 1.5 * fabs(x[i]->moles) || f < 1e-5 * fabs(x[i]->moles) ) {
repeat = TRUE;
weight = (f < 1e-5 * fabs(x[i]->moles)) ? 0.3 : 1.0;
x[i]->master[0]->s->la += weight *
log10(fabs(x[i]->moles / x[i]->sum));
if ( debug_set == TRUE ) {
output_msg(OUTPUT_MESSAGE,"%s not converged in set. %e\t%e\t%e\n", x[i]->description, (double) x[i]->sum, (double) x[i]->moles, (double) x[i]->master[0]->s->la);
}
}
}
}
}
output_msg(OUTPUT_LOG,"Iterations in revise_guesses: %d\n", iter);
mu_x = mu_unknown->f * 0.5 / mass_water_aq_x;
if (mu_x <= 1e-8) {
mu_x = 1e-8;
}
gammas(mu_x);
return(OK);
}
/* ---------------------------------------------------------------------- */
int sum_species(void)
/* ---------------------------------------------------------------------- */
{
/*
* Calculates total alk, total carbon, total co2, electrical balance,
* total hydrogen, and total oxygen.
*
* Sorts species for summing and printing based on valence state and
* concentrations.
*
* Sums total valence states and stores in master[i]->total.
*/
int i, j;
struct master *master_ptr;
/*
* Set global variables
*/
ph_x = -s_hplus->la;
solution_pe_x = -s_eminus->la;
ah2o_x = exp(s_h2o->la * LOG_10);
density_x = 1.0;
if (s_o2 != NULL) s_o2->moles = under(s_o2->lm) * mass_water_aq_x;
if (s_h2 != NULL) s_h2->moles = under(s_h2->lm) * mass_water_aq_x;
/*
* Calculate sums
*/
total_alkalinity = 0.0;
total_carbon = 0.0;
total_co2 = 0.0;
cb_x = 0.0;
total_ions_x = 0.0;
total_o_x = 0.0;
total_h_x = 0.0;
for (i=0; i < count_s_x; i++) {
if (s_x[i]->type == EX ) continue;
if (s_x[i]->type == SURF ) continue;
cb_x += s_x[i]->z * s_x[i]->moles;
total_ions_x += fabs(s_x[i]->z * s_x[i]->moles);
total_alkalinity += s_x[i]->alk * s_x[i]->moles;
total_carbon += s_x[i]->carbon * s_x[i]->moles;
total_co2 += s_x[i]->co2 * s_x[i]->moles;
total_h_x += s_x[i]->h * s_x[i]->moles;
total_o_x += s_x[i]->o * s_x[i]->moles;
}
/*
* Sum valence states, put in master->total
*/
for (i=0; i < count_master; i++) {
master[i]->total=0.0;
master[i]->total_primary = 0.0;
}
for (i=0; i < count_species_list; i++) {
if (species_list[i].master_s->secondary != NULL) {
master_ptr = species_list[i].master_s->secondary;
} else {
master_ptr = species_list[i].master_s->primary;
}
master_ptr->total += species_list[i].s->moles * species_list[i].coef;
}
/*
* Calculate mass-balance sums
*/
for (i = 0; i < count_unknowns; i++) {
if (x[i]->type == MB ||
x[i]->type == SOLUTION_PHASE_BOUNDARY ||
x[i]->type == EXCH ||
x[i]->type == SURFACE ||
( x[i]->type == CB && x[i] != ph_unknown && x[i] != pe_unknown ) ) {
x[i]->sum = 0.0;
for (j = 0; x[i]->master[j] != NULL; j++) {
x[i]->sum += x[i]->master[j]->total;
}
} else if (x[i]->type == ALK) {
x[i]->sum = total_co2;
}
}
/*
* Calculate total element concentrations
*/
for (i = 0; i < count_master; i++) {
master[i]->elt->primary->total_primary +=
master[i]->total;
}
/*
* Calculate isotope ratios
*/
calculate_values();
return(OK);
}
/* ---------------------------------------------------------------------- */
int surface_model(void)
/* ---------------------------------------------------------------------- */
{
/*
* Use extra iterative loop to converge on g_factors
*/
int i, g_iterations, debug_diffuse_layer_save, debug_model_save;
/*
* Allocate space for g factors for diffuse layer in surface complexation
*/
debug_diffuse_layer_save = debug_diffuse_layer;
debug_model_save = debug_model;
if (last_model.force_prep == TRUE) {
same_model = FALSE;
} else {
same_model = check_same_model();
}
if (diffuse_layer_x == TRUE && same_model == FALSE) {
for (i=0; i < count_s; i++) {
s[i]->diff_layer = (struct species_diff_layer *) free_check_null(s[i]->diff_layer);
s[i]->diff_layer = (struct species_diff_layer *) PHRQ_malloc((size_t) use.surface_ptr->count_charge * sizeof (struct species_diff_layer));
if (s[i]->diff_layer == NULL) malloc_error();
}
}
prep();
k_temp(tc_x);
if (use.surface_ptr->donnan == TRUE) {
initial_surface_water();
calc_init_donnan();
} else {
calc_init_g();
}
if (state >= REACTION && use.surface_ptr->new_def == FALSE) {
set(FALSE);
} else {
set(TRUE);
}
if (model() == ERROR) return(ERROR);
g_iterations = 0;
if (use.surface_ptr->donnan == TRUE) {
do {
g_iterations++;
k_temp(tc_x);
gammas(mu_x);
molalities(TRUE);
mb_sums();
if (model() == ERROR) return(ERROR);
if (use.surface_ptr->related_phases != TRUE && use.surface_ptr->related_rate != TRUE) initial_surface_water();
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE, "Surface_model (Donnan approximation): %d g_iterations, %d model iterations\n",
g_iterations, iterations);
}
} while (calc_all_donnan() == FALSE && g_iterations < itmax);
} else {
do {
g_iterations++;
if (g_iterations > itmax - 10) {
debug_model = TRUE;
debug_diffuse_layer = TRUE;
}
k_temp(tc_x);
gammas(mu_x);
molalities(TRUE);
mb_sums();
if (model() == ERROR) return(ERROR);
if (use.surface_ptr->related_phases != TRUE && use.surface_ptr->related_rate != TRUE) initial_surface_water();
if (debug_model == TRUE) {
output_msg(OUTPUT_MESSAGE, "Surface_model (full integration): %d g_iterations, %d iterations\n",
g_iterations, iterations);
}
} while (calc_all_g() == FALSE && g_iterations < itmax);
}
if (g_iterations >= itmax) {
error_msg("Did not converge on g (diffuse layer excess)", STOP);
}
/*
print_all();
*/
debug_diffuse_layer = debug_diffuse_layer_save;
debug_model = debug_model_save;
return(OK);
}
/* ---------------------------------------------------------------------- */
int free_model_allocs(void)
/* ---------------------------------------------------------------------- */
{
/*
* free space allocated in model
*/
int i;
if (x != NULL) {
for (i = 0; i < max_unknowns; i++) {
unknown_free(x[i]);
}
}
x = (struct unknown **) free_check_null(x);
max_unknowns = 0;
array = (LDBLE *) free_check_null(array);
delta = (LDBLE *) free_check_null(delta);
residual = (LDBLE *) free_check_null(residual);
s_x = (struct species **) free_check_null(s_x);
sum_mb1 = (struct list1*) free_check_null(sum_mb1);
sum_mb2 = (struct list2 *) free_check_null(sum_mb2);
sum_jacob0 = (struct list0 *) free_check_null(sum_jacob0);
sum_jacob1 = (struct list1 *) free_check_null(sum_jacob1);
sum_jacob2 = (struct list2 *) free_check_null(sum_jacob2);
sum_delta = (struct list2 *) free_check_null(sum_delta);
charge_group = (struct charge_group *) free_check_null(charge_group);
return(OK);
}
#ifdef SLNQ
/* ---------------------------------------------------------------------- */
int add_trivial_eqns(int rows, int cols, LDBLE *matrix)
/* ---------------------------------------------------------------------- */
{
int r, i, j;
r = rows;
if (rows == cols) return(OK);
if (rows > cols) return(ERROR);
for (i = 0; i < cols; i++) {
for (j = 0; j < rows; j++) {
if (matrix[j*(cols+1) + i] != 0.0) break;
}
if (j < rows) continue;
for (j = 0; j < cols+1; j++) matrix[r*(cols+1) + j] = 0.0;
matrix[r*(cols+1) + i] = 1.0;
r++;
}
if (r == cols) return(OK);
return(ERROR);
}
#endif
#define ZERO_TOL 1.0e-30
/* ---------------------------------------------------------------------- */
LDBLE s_s_root(LDBLE a0, LDBLE a1, LDBLE kc, LDBLE kb, LDBLE xcaq, LDBLE xbaq)
/* ---------------------------------------------------------------------- */
{
int i;
LDBLE x0, y0, x1, y1, xb, miny;
/*
* Bracket answer
*/
x0 = 0.0;
x1 = 0.0;
y0 = s_s_f( x0, a0, a1, kc, kb, xcaq, xbaq);
miny = fabs(y0);
for (i = 1; i <= 10; i++) {
x1 = (LDBLE) i / 10;
y1 = s_s_f( x1, a0, a1, kc, kb, xcaq, xbaq);
if (fabs(y1) < miny) {
miny = fabs(y1);
}
if (y0*y1 < 0) {
break;
} else {
x0 = x1;
y0 = y1;
}
}
/*
* Interval halve
*/
if (i > 10) {
xb = 0.0;
} else {
xb = s_s_halve(a0, a1, x0, x1, kc, kb, xcaq, xbaq);
}
return(xb);
}
/* ---------------------------------------------------------------------- */
LDBLE s_s_halve(LDBLE a0, LDBLE a1, LDBLE x0, LDBLE x1, LDBLE kc, LDBLE kb, LDBLE xcaq, LDBLE xbaq)
/* ---------------------------------------------------------------------- */
{
int i;
LDBLE x, y0, dx, y;
y0 = s_s_f( x0, a0, a1, kc, kb, xcaq, xbaq);
dx = (x1 - x0);
/*
* Loop for interval halving
*/
for (i = 0; i < 100; i++) {
dx *= 0.5;
x = x0 + dx;
y = s_s_f( x, a0, a1, kc, kb, xcaq, xbaq);
if (dx < 1e-8 || y == 0) {
break;
}
#ifdef SKIP
if (y0*y < 0) {
x1 = x;
} else {
x0 = x;
y0 = y;
}
#endif
if (y0*y >= 0) {
x0 = x;
y0 = y;
}
}
return(x0 + dx);
}
/* ---------------------------------------------------------------------- */
LDBLE s_s_f(LDBLE xb, LDBLE a0, LDBLE a1, LDBLE kc, LDBLE kb, LDBLE xcaq, LDBLE xbaq)
/* ---------------------------------------------------------------------- */
{
/*
* Need root of this function to determine xb
*/
LDBLE lb, lc, f, xc, r;
xc = 1 - xb;
if (xb == 0) xb = 1e-20;
if (xc == 0) xc = 1e-20;
lc = exp((a0-a1*(-4*xb + 3))*xb*xb);
lb = exp((a0+a1*(4*xb - 3))*xc*xc);
r = lc*kc/(lb*kb);
f = xcaq*(xb/r + xc) + xbaq*(xb + r*xc) - 1;
return(f);
}