iphreeqc/phreeqcpp/model.cpp
Darth Vader 622bc5fb4c Squashed 'src/' changes from ac393756..d6a74675
d6a74675 Merge commit '4e80a54467a084df3b666c7d6fc56a4798fd3301'
4e80a544 Squashed 'phreeqcpp/' changes from 7c7fafd..c876219
0209fdf9 Merge commit 'b537589773f4819fe97ff8e5322bcd38c54b63f7'
b5375897 Squashed 'phreeqcpp/' changes from e317dd0..7c7fafd

git-subtree-dir: src
git-subtree-split: d6a74675d73985977ceac1601b57463c1ee8c331
2024-10-08 20:05:43 +00:00

5844 lines
152 KiB
C++

#include "Utils.h"
#include "Phreeqc.h"
#include "phqalloc.h"
#include "cxxMix.h"
#include "Exchange.h"
#include "GasPhase.h"
#include "PPassemblage.h"
#include "SSassemblage.h"
#include "Solution.h"
#if defined(PHREEQCI_GUI)
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#endif
/* ---------------------------------------------------------------------- */
int Phreeqc::
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_ss--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 l_kode, return_kode;
int r;
int count_infeasible, count_basis_change;
int debug_model_save;
int mass_water_switch_save;
set_inert_moles();
/* debug_model = TRUE; */
/* debug_prep = TRUE; */
/* debug_set = TRUE; */
/* mass_water_switch == TRUE, mass of water is constant */
if (pitzer_model == TRUE && sit_model == TRUE)
{
input_error++;
error_msg("Cannot use PITZER and SIT data blocks in same run (database + input file).", STOP);
}
if ((pitzer_model == TRUE || sit_model == TRUE) && llnl_temp.size() > 0)
{
input_error++;
error_msg("Cannot use LLNL_AQUEOUS_MODEL_PARAMETERS with PITZER or SIT data blocks in same run (database + input file).", STOP);
}
if (pitzer_model == TRUE)
{
l_kode = model_pz();
unset_inert_moles();
return l_kode;
}
if (sit_model == TRUE)
{
l_kode = model_sit();
unset_inert_moles();
return l_kode;
}
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;
#ifdef NPP
if (!use.Get_kinetics_in()) status(0, NULL);
#else
status(0, NULL);
#endif
iterations = 0;
count_basis_change = count_infeasible = 0;
stop_program = FALSE;
remove_unstable_phases = FALSE;
for (;;)
{
mb_gases();
mb_ss();
l_kode = 1;
while ((r = residuals()) != CONVERGED
|| remove_unstable_phases == TRUE)
{
#if defined(PHREEQCI_GUI)
PhreeqcIWait(this);
#endif
iterations++;
overall_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(sformatf(
"\nIteration %d\tStep_size = %f\n", iterations,
(double) step_size_now));
output_msg(sformatf( "\t\tPe_step_size = %f\n\n",
(double) pe_step_size_now));
}
/*
* Iterations exceeded
*/
if (iterations > itmax)
{
error_string = sformatf( "Maximum iterations exceeded, %d\n",
itmax);
warning_msg(error_string);
stop_program = TRUE;
break;
}
/*
* Calculate jacobian
*/
if (state >= REACTION && numerical_deriv)
{
//jacobian_sums();
numerical_jacobian();
}
else /* hmm */
{
jacobian_sums();
numerical_jacobian();
}
/*
* Full matrix with pure phases
*/
if (r == OK || remove_unstable_phases == TRUE)
{
return_kode = ineq(l_kode);
if (return_kode != OK)
{
if (debug_model == TRUE)
{
output_msg(sformatf(
"Ineq had infeasible solution, "
"kode %d, iteration %d\n", return_kode,
iterations));
}
log_msg(sformatf("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.Get_surface_ptr() != NULL &&
use.Get_surface_ptr()->Get_dl_type() != cxxSurface::NO_DL &&
use.Get_surface_ptr()->Get_related_phases())
initial_surface_water();
mb_sums();
mb_gases();
mb_ss();
/*
* Switch bases if necessary
*/
if (switch_bases() == TRUE)
{
count_basis_change++;
reprep();
gammas(mu_x);
molalities(TRUE);
if (use.Get_surface_ptr() != NULL &&
use.Get_surface_ptr()->Get_dl_type() != cxxSurface::NO_DL &&
use.Get_surface_ptr()->Get_related_phases())
initial_surface_water();
revise_guesses();
mb_sums();
mb_gases();
mb_ss();
}
/* 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)
{
log_msg(sformatf(
"\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(sformatf(
"\nRemoving unstable phases. Iteration %d.\n",
iterations));
}
log_msg(sformatf( "\nRemoving unstable phases. Iteration %d.\n",
iterations));
}
log_msg(sformatf( "\nNumber of infeasible solutions: %d\n",
count_infeasible));
log_msg(sformatf( "Number of basis changes: %d\n\n",
count_basis_change));
log_msg(sformatf( "Number of iterations: %d\n\n", iterations));
debug_model = debug_model_save;
set_forward_output_to_log(FALSE);
unset_inert_moles();
if (stop_program == TRUE)
{
return (ERROR);
}
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
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;
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 && fabs(residual[i]) > sqrt(fabs(x[i]->moles) * MIN_TOTAL)
&& x[i]->moles > MIN_TOTAL /* || stop_program == TRUE */ )
{
error_string = sformatf(
"%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 */ )
{
error_string = sformatf(
"%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 */ )
{
error_string = sformatf(
"%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 && sit_model == FALSE*/)
{
if (fabs(residual[i]) >=
epsilon * mu_x *
mass_water_aq_x /* || stop_program == TRUE */ )
{
error_string = sformatf(
"%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 && sit_model == FALSE)
{
if (fabs(residual[i]) >= epsilon /* || stop_program == TRUE */ )
{
error_string = sformatf(
"%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 || pitzer_pe == TRUE)))
{
#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
{
error_string = sformatf(
"%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 */ )
{
error_string = sformatf(
"%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)
{
//cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name);
cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr;
if (comp_ptr->Get_add_formula().size() == 0)
{
if (x[i]->dissolve_only == TRUE)
{
if ((residual[i] > epsilon && x[i]->moles > 0.0)
||
((residual[i] < -epsilon
&& (comp_ptr->Get_initial_moles() - x[i]->moles) >
0)))
{
log_msg(sformatf(
"%20s Dissolve_only pure phase has not converged. \tResidual: %e\n",
x[i]->description, (double) residual[i]));
}
}
else
{
if ((residual[i] >= epsilon * 100
&& x[i]->moles > 0.0) /* || stop_program == TRUE */ )
{
remove_unstable_phases = TRUE;
log_msg(sformatf(
"%20s Pure phase has not converged. \tResidual: %e\n",
x[i]->description, (double) residual[i]));
}
else if (residual[i] <= -epsilon)
{
error_string = sformatf(
"%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 */ )
{
log_msg(sformatf(
"%s, Pure phase has not converged. \tResidual: %e\n",
x[i]->description, (double) residual[i]));
error_string = sformatf(
"%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)))
{
error_string = sformatf(
"%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 (fabs(residual[i]) < ineq_tol && fabs(residual[i]) < 1e-2*x[i]->moles) continue;
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)))
{
error_string = sformatf(
"%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 || x[i]->type == SURFACE_CB1
|| x[i]->type == SURFACE_CB2)
{
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge);
if ((charge_ptr->Get_grams() > MIN_RELATED_SURFACE
&& fabs(residual[i]) >
epsilon) /* || stop_program == TRUE */ )
{
error_string = sformatf(
"%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 */ )
{
error_string = sformatf(
"%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)
{
error_string = sformatf(
"%20s log gamma not converged.\tResidual: %e\n",
x[i]->description, (double) residual[i]);
}
}
else if (x[i]->type == SS_MOLES)
{
if (x[i]->ss_in == FALSE)
continue;
//if (x[i]->moles <= 1e2*MIN_TOTAL)
// continue;
if (residual[i] >= epsilon
|| residual[i] <= -epsilon /* || stop_program == TRUE */ )
{
error_string = sformatf(
"%20s Total moles in solid solution has not converged. "
"\tResidual: %e %e\n", x[i]->description,
(double) residual[i], x[i]->moles);
error_msg(error_string, CONTINUE);
}
}
}
if (remove_unstable_phases == TRUE)
{
log_msg(sformatf( "%20sRemoving unstable phases, iteration %d.",
" ", iterations));
}
return (return_value);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
gammas(LDBLE mu)
/* ---------------------------------------------------------------------- */
{
/*
* Calculates gammas and [moles * d(ln gamma)/d mu] for all aqueous
* species.
*/
int i, j;
int ifirst, ilast;
LDBLE f, log_g_co2, dln_g_co2, c2_llnl;
LDBLE c1, c2, a, b;
LDBLE muhalf, equiv;
/* Initialize */
if (mu <= 0) mu = 1e-10;
if (pitzer_model == TRUE)
return gammas_pz(true);
if (sit_model == TRUE)
return gammas_sit();
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
*/
// a and b are calc'd in calc_dielectrics(tc_x, patm_x);
k_temp(tc_x, patm_x);
a = DH_A;
b = DH_B;
/*
* LLNL temperature dependence
*/
if (llnl_temp.size() > 0)
{
ifirst = 0;
ilast = (int)llnl_temp.size();
if (tc_x < llnl_temp[0] || tc_x > llnl_temp[llnl_temp.size() - 1])
{
error_msg
("Temperature out of range of LLNL_AQUEOUS_MODEL parameters",
STOP);
}
for (i = 0; i < (int)llnl_temp.size(); 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_temp.size() > 0)
{
c2_llnl = -a_llnl / (2 * muhalf);
}
/*
* Calculate activity coefficients
*/
for (i = 0; i < (int)this->s_x.size(); 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 charge of cation for exchange species, alk contains cec
* correct activity for Gapon-type exchange eqns: Ca0.5X uses (gamma_Ca)^0.5
*/
if (calculating_deriv)
continue;
{
LDBLE coef = 0, z = 0;
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;
}
else if (s_x[i]->rxn_x.token[j].s->type <= HPLUS)
{
coef = s_x[i]->rxn_x.token[j].coef;
z = s_x[i]->rxn_x.token[j].s->z;
}
}
if (!use.Get_exchange_ptr()->Get_pitzer_exchange_gammas())
{
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;
}
}
else if (s_x[i]->exch_gflag == 1 && s_x[i]->alk > 0)
{
/* Davies */
s_x[i]->lg = -coef * z * z * a *
(muhalf / (1.0 + muhalf) - 0.3 * mu) +
log10(fabs(s_x[i]->equiv) / s_x[i]->alk);
s_x[i]->dg =
c1 * coef * z * z * 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 = coef * (-a * muhalf * z * z /
(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 = coef * (c2 * z * 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;
}
else if (s_x[i]->exch_gflag == 7 && s_x[i]->alk > 0)
{
if (llnl_temp.size() > 0)
{
s_x[i]->lg =
coef * (-a_llnl * muhalf * z * z /
(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 =
coef * (c2_llnl * z * 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;
}
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;
}
}
if (s_x[i]->a_f && s_x[i]->primary == NULL && s_x[i]->moles)
gammas_a_f(i); // appt
}
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 (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC)
{
/* mole fraction */
equiv = 1.0;
}
else
{
equiv = s_x[i]->equiv;
}
if (s_x[i]->alk > 0)
{
s_x[i]->lg = log10(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_temp.size() > 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_temp.size() > 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 Phreeqc::gammas_a_f(int i1)
/* ------------------------------------------------------------------------------- */
{
int i, j;
//LDBLE d2, d3, coef = 0, sum = 0;
LDBLE d2, d3, sum = 0;
//char name[MAX_LENGTH];
std::string name;
//class master *m_ptr;
i = i1;
for (j = 1; s_x[i]->rxn_x.token[j].s != NULL; j++)
{
if (s_x[i]->rxn_x.token[j].s->type == EX)
{
name = s_x[i]->rxn_x.token[j].s->name;
//m_ptr = s_x[i]->rxn_x.token[j].s->primary->elt->master; // appt debug
break;
}
}
for (i = 0; i < (int)this->s_x.size(); i++)
{
if (s_x[i]->gflag != 4 || s_x[i]->primary)
continue;
for (j = 1; s_x[i]->rxn_x.token[j].s != NULL; j++)
{
if (s_x[i]->rxn_x.token[j].s->type == EX)
{
if (!strcmp(name.c_str(), s_x[i]->rxn_x.token[j].s->name))
sum += s_x[i]->moles * s_x[i]->equiv;
break;
}
}
}
i = i1;
d2 = s_x[i]->moles * s_x[i]->equiv / sum;
if (d2 > 1) d2 = 1;
//if (iterations > 19)
// i += 0; // appt debug
d3 = 0.5;
if (s_x[i]->a_f > 2)
{
d3 += (s_x[i]->a_f - 2) / 10; if (d3 > 0.8) d3 = 0.8;
}
d2 = s_x[i]->dw_a * d3 + (1 - d3) * d2;
s_x[i]->lg -= s_x[i]->a_f * (1 - d2);
s_x[i]->dw_a = d2;
return 0;
}
/* ------------------------------------------------------------------------------- */
int Phreeqc::
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 l_count_rows;
int l_count_optimize, count_equal;
int k, l, m, n;
int l_klmd, l_nklmd, l_n2d;
int l_iter;
LDBLE l_error;
LDBLE max;
int l_kode;
LDBLE min;
#ifdef SLNQ
LDBLE *slnq_array;
LDBLE *slnq_delta1;
#endif
/* Debug
if (debug_model == TRUE) {
output_msg(sformatf( "\narray:\n\n");
array_print(array, count_unknowns, count_unknowns + 1, count_unknowns + 1));
}
*/
/*
* Special case for removing unstable phases
*/
cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr();
cxxPPassemblageComp * comp_ptr;
if (remove_unstable_phases == TRUE)
{
if (debug_model == TRUE)
{
output_msg(sformatf(
"\nSolution vector for removing unstable phases:\n"));
}
for (i = 0; i < count_unknowns; i++)
{
if (x[i]->type == PP)
{
//std::map<std::string, cxxPPassemblageComp>::iterator it;
//it = pp_assemblage_ptr->Get_pp_assemblage_comps().find(x[i]->pp_assemblage_comp_name);
//assert(it != pp_assemblage_ptr->Get_pp_assemblage_comps().end());
cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr;
if (residual[i] > 0e-8 && x[i]->moles > 0 &&
//it->second.Get_add_formula().size() == 0
comp_ptr->Get_add_formula().size() == 0
&& 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(sformatf( "%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 || sit_model == TRUE)
{
for (i = 0; i < count_unknowns; i++)
{
if ((x[i]->type == AH2O && full_pitzer == FALSE) ||
(x[i]->type == MH && pitzer_model == TRUE && pitzer_pe == FALSE) ||
/*x[i]->type == MU ||*/
(x[i]->type == PITZER_GAMMA && full_pitzer == FALSE))
{
for (j = 0; j < count_unknowns; j++)
{
my_array[(size_t)j * (count_unknowns + 1) + i] = 0.0;
}
for (j = 0; j < count_unknowns + 1; j++)
{
my_array[(size_t)i * (count_unknowns + 1) + (size_t)j] = 0.0;
}
}
}
}
/*
* Normalize column
*/
normal.resize(count_unknowns);
std::fill(normal.begin(), normal.end(), 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
|| x[i]->type == SURFACE_CB1 || x[i]->type == SURFACE_CB2)
{
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 (x[i]->type == SURFACE_CB1 && x[j]->type == SURFACE_CB2)
continue;
if (fabs(my_array[(size_t)j * (count_unknowns + 1) + (size_t)i]) > max)
{
max = fabs(my_array[(size_t)j * (count_unknowns + 1) + (size_t)i]);
if (max > min_value)
break;
}
}
if (diagonal_scale == TRUE)
{
if (fabs(my_array[(size_t)i * (count_unknowns + 1) + (size_t)i]) < min_value)
{
max = fabs(my_array[(size_t)i * (count_unknowns + 1) + (size_t)i]);
}
}
if (max == 0)
{
my_array[(size_t)i * (count_unknowns + 1) + (size_t)i] = 1e-5 * x[i]->moles;
max = fabs(1e-5 * x[i]->moles);
}
}
if (x[i]->type == MH && (pitzer_model == FALSE || pitzer_pe == TRUE))
{
/* make absolute value of diagonal at least 1e-12 */
min = 1e-12;
min = MIN_TOTAL;
my_array[(size_t)x[i]->number * (count_unknowns + 1) + (size_t)x[i]->number] += min;
if (fabs(my_array[(size_t)x[i]->number * (count_unknowns + 1) + (size_t)x[i]->number]) < min)
my_array[(size_t)x[i]->number * (count_unknowns + 1) + (size_t)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 != SURFACE_CB1 &&
x[j]->type != SURFACE_CB2 &&
x[j]->type != EXCH && x[j]->type != MH
&& x[j]->type != MH2O)
continue;
if (fabs(my_array[(size_t)j * (count_unknowns + 1) + (size_t)i]) > max)
{
max = fabs(my_array[(size_t)j * (count_unknowns + 1) + (size_t)i]);
if (max > min_value)
break;
}
}
}
if (max > 0.0 && max < min_value)
{
if (debug_model == TRUE)
{
output_msg(sformatf(
"Scaling column for %s, max= %e\n",
x[i]->description, (double) max));
}
for (j = 0; j < count_unknowns; j++)
{
my_array[(size_t)j * (count_unknowns + 1) + (size_t)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.resize(max_row_count * max_column_count);
back_eq.resize(max_row_count);
zero.resize(max_row_count);
memset(&zero[0], 0, max_row_count * sizeof(double));
res.resize(max_row_count);
memset(&res[0], 0, max_row_count * sizeof(double));
delta1.resize(max_column_count);
memset(&delta1[0], 0,max_column_count * sizeof(double));
/*
* Copy equations to optimize into ineq_array
*/
l_count_rows = 0;
for (i = 0; i < count_unknowns; i++)
{
if (iterations < aqueous_only)
continue;
/*
* Pure phases
*/
if (x[i]->type == PP)
{
//std::map<std::string, cxxPPassemblageComp>::iterator it;
//it = pp_assemblage_ptr->Get_pp_assemblage_comps().find(x[i]->pp_assemblage_comp_name);
/* not in model, ignore */
if (x[i]->phase->in == FALSE)
continue;
// delay removing phase
if (x[i]->moles > 0.0 || x[i]->f <= 0.0 || iterations == 0 || equi_delay == 0)
{
x[i]->iteration = iterations;
}
cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr;
//if (it->second.Get_force_equality())
if (comp_ptr->Get_force_equality())
continue;
/* Undersaturated and no mass, ignore */
if (x[i]->f > 0e-8 && x[i]->moles <= 0
&& iterations >= x[i]->iteration + equi_delay
&& comp_ptr->Get_add_formula().size() == 0)
{
continue;
}
else if (x[i]->f < 0e-8 && x[i]->dissolve_only == TRUE
//&& (x[i]->moles - it->second.Get_initial_moles() >= 0))
&& (x[i]->moles - comp_ptr->Get_initial_moles() >= 0))
{
continue;
}
else
{
/* Copy in saturation index equation (has mass or supersaturated) */
memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]),
(void *) &(my_array[(size_t)i * (count_unknowns + 1)]),
(count_unknowns + 1) * sizeof(LDBLE));
back_eq[l_count_rows] = i;
//if (it->second.Get_add_formula().size() == 0
if (comp_ptr->Get_add_formula().size() == 0
&& x[i]->dissolve_only == FALSE)
{
res[l_count_rows] = 1.0;
}
/*
* If infeasible solution on first attempt, remove constraints on IAP
*/
if (pp_scale != 1)
{
for (j = 0; j < count_unknowns + 1; j++)
{
ineq_array[(size_t)l_count_rows * max_column_count + (size_t)j] *= pp_scale;
}
}
if (in_kode != 1)
{
res[l_count_rows] = 0.0;
}
l_count_rows++;
}
}
else if (x[i]->type == ALK || x[i]->type == SOLUTION_PHASE_BOUNDARY)
{
/*
* Alkalinity and solution phase boundary
*/
memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]),
(void *) &(my_array[(size_t)i * (count_unknowns + 1)]),
(count_unknowns + 1) * sizeof(LDBLE));
back_eq[l_count_rows] = i;
l_count_rows++;
/*
* Gas phase
*/
}
else if (x[i]->type == GAS_MOLES && gas_in == TRUE)
{
memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]),
(void *) &(my_array[(size_t)i * (count_unknowns + 1)]),
(count_unknowns + 1) * sizeof(LDBLE));
back_eq[l_count_rows] = i;
res[l_count_rows] = 1.0;
if (in_kode != 1)
{
res[l_count_rows] = 0.0;
}
l_count_rows++;
/*
* Solid solution
*/
}
else if (x[i]->type == SS_MOLES && x[i]->ss_in == TRUE)
{
memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]),
(void *) &(my_array[(size_t)i * (count_unknowns + 1)]),
(count_unknowns + 1) * sizeof(LDBLE));
back_eq[l_count_rows] = i;
res[l_count_rows] = 1.0;
if (in_kode != 1)
{
res[l_count_rows] = 0.0;
}
l_count_rows++;
}
}
l_count_optimize = l_count_rows;
/*
* Copy equality equations into ineq_array
*/
for (i = 0; i < count_unknowns; i++)
{
comp_ptr = NULL;
cxxPPassemblageComp *comp_ptr1 = NULL;
if (x[i]->type == PP)
{
//comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name);
comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr;
}
if (x[i]->type == SURFACE && x[i]->phase_unknown != NULL)
{
comp_ptr = pp_assemblage_ptr->Find(x[i]->phase_unknown->phase->name);
}
if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1
|| x[i]->type == SURFACE_CB2)
&& x[(size_t)i - 1]->phase_unknown != NULL)
{
comp_ptr1 = pp_assemblage_ptr->Find(x[(size_t)i-1]->phase_unknown->phase->name);
}
if (x[i]->type != SOLUTION_PHASE_BOUNDARY &&
x[i]->type != ALK &&
x[i]->type != GAS_MOLES && x[i]->type != SS_MOLES &&
x[i]->type != PITZER_GAMMA
/* && x[i]->type != PP */
)
{
if (x[i]->type == PP && !comp_ptr->Get_force_equality())
continue;
if (x[i]->type == MH && pitzer_model == TRUE && pitzer_pe == FALSE)
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]->type == SURFACE_CB1
|| x[i]->type == SURFACE_CB2) )
{
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge);
if (charge_ptr->Get_grams() <= MIN_RELATED_SURFACE)
{
continue;
}
}
if (x[i]->type == SURFACE && x[i]->phase_unknown != NULL &&
x[i]->phase_unknown->moles <= MIN_RELATED_SURFACE &&
comp_ptr->Get_add_formula().size() == 0)
continue;
if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1
|| x[i]->type == SURFACE_CB2)
&& x[(size_t)i - 1]->phase_unknown != NULL)
{
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge);
if (charge_ptr->Get_grams() <= MIN_RELATED_SURFACE &&
comp_ptr1->Get_add_formula().size() == 0)
{
continue;
}
}
memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]),
(void *) &(my_array[(size_t)i * (count_unknowns + 1)]),
(count_unknowns + 1) * sizeof(LDBLE));
back_eq[l_count_rows] = i;
if (mass_water_switch == TRUE && x[i] == mass_hydrogen_unknown)
{
k = (int)mass_oxygen_unknown->number;
for (j = 0; j < count_unknowns; j++)
{
ineq_array[(size_t)l_count_rows * max_column_count + (size_t)j] -=
2 * my_array[(size_t)k * (count_unknowns + 1) + (size_t)j];
}
}
l_count_rows++;
}
else if (x[i]->type == PITZER_GAMMA && full_pitzer == TRUE)
{
memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]),
(void *) &(my_array[(size_t)i * (count_unknowns + 1)]),
(count_unknowns + 1) * sizeof(LDBLE));
back_eq[l_count_rows] = i;
l_count_rows++;
}
}
count_equal = l_count_rows - l_count_optimize;
/*
* Copy inequality constraints into ineq
*/
if (pure_phase_unknown != NULL)
{
for (i = 0; i < count_unknowns; i++)
{
if (x[i]->type == PP)
{
//comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name);
comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr;
/* not in model, ignore */
if (x[i]->phase->in == FALSE)
continue;
/* No moles and undersaturated, ignore */
if (x[i]->moles <= 0.0 && x[i]->f > 0e-8 &&
comp_ptr->Get_add_formula().size() == 0)
{
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 - comp_ptr->Get_initial_moles() >=
0))
{
continue;
}
else
{
/* Pure phase is present, force Mass transfer to be <= amount of mineral remaining */
memset(&ineq_array[(size_t)l_count_rows * max_column_count], 0, ((size_t) count_unknowns + 1) * sizeof(LDBLE));
ineq_array[(size_t)l_count_rows * max_column_count + i] = 1.0;
ineq_array[(size_t)l_count_rows * max_column_count + count_unknowns] = x[i]->moles;
back_eq[l_count_rows] = i;
l_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[(size_t)l_count_rows * max_column_count]),
// (void *) &(zero[0]),
// ((size_t) count_unknowns + 1) * sizeof(LDBLE));
memset(&(ineq_array[(size_t)l_count_rows * max_column_count]), 0, (count_unknowns + 1) * sizeof(LDBLE));
ineq_array[(size_t)l_count_rows * max_column_count + i] = -1.0;
ineq_array[(size_t)l_count_rows * max_column_count + count_unknowns] =
comp_ptr->Get_initial_moles() - x[i]->moles;
back_eq[l_count_rows] = i;
l_count_rows++;
}
}
}
}
/*
* Add inequality for mass of oxygen greater than zero
*/
if (pitzer_model || sit_model)
{
for (i = 0; i < count_unknowns; i++)
{
if (x[i]->type == MH2O)
{
memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]),
(void *) &(my_array[(size_t)i * (count_unknowns + 1)]),
(count_unknowns + 1) * sizeof(LDBLE));
back_eq[l_count_rows] = i;
for (j = 0; j < count_unknowns; j++)
{
if (x[j]->type < PP)
{
ineq_array[(size_t)l_count_rows * max_column_count + j] = 0.0;
}
else
{
/*ineq_array[l_count_rows*max_column_count + j] = -ineq_array[l_count_rows*max_column_count + j]; */
}
}
ineq_array[(size_t)l_count_rows * max_column_count + count_unknowns] =
0.5 * x[i]->moles;
l_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)
{
//comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name);
comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr;
if ((x[i]->moles <= 0.0 && x[i]->f > 0e-8 &&
comp_ptr->Get_add_formula().size() == 0)
|| x[i]->phase->in == FALSE)
{
for (j = 0; j < l_count_rows; j++)
{
ineq_array[(size_t)j * max_column_count + i] = 0.0;
}
}
if (x[i]->dissolve_only == TRUE)
{
if (x[i]->f < 0e-8
&& (x[i]->moles - comp_ptr->Get_initial_moles() >=
0))
{
for (j = 0; j < l_count_rows; j++)
{
ineq_array[(size_t)j * max_column_count + (size_t)i] = 0.0;
}
}
}
}
}
}
/*
* No moles of exchanger
*/
if (use.Get_exchange_ptr() != NULL
&& (use.Get_exchange_ptr()->Get_related_phases() ||
use.Get_exchange_ptr()->Get_related_rate()))
{
for (i = 0; i < count_unknowns; i++)
{
if (x[i]->type == EXCH && x[i]->moles <= 0)
{
for (j = 0; j < l_count_rows; j++)
{
ineq_array[(size_t)j * max_column_count + i] = 0.0;
}
}
}
}
/*
* No moles of surface
*/
if (use.Get_surface_ptr() != NULL
&& (use.Get_surface_ptr()->Get_related_phases()
|| use.Get_surface_ptr()->Get_related_rate()))
{
for (i = 0; i < count_unknowns; i++)
{
comp_ptr = NULL;
cxxPPassemblageComp * comp_ptr1 = NULL;
if (x[i]->type == SURFACE && x[i]->phase_unknown != NULL)
{
comp_ptr = pp_assemblage_ptr->Find(x[i]->phase_unknown->phase->name);
}
if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1
|| x[i]->type == SURFACE_CB2)
&& (x[(size_t)i - 1]->phase_unknown != NULL))
{
comp_ptr1 = pp_assemblage_ptr->Find(x[(size_t)i-1]->phase_unknown->phase->name);
}
LDBLE grams = 0;
if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1
|| x[i]->type == SURFACE_CB2)
&& x[(size_t)i - 1]->phase_unknown != NULL)
{
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge);
grams = charge_ptr->Get_grams();
}
if ((x[i]->type == SURFACE && x[i]->phase_unknown != NULL &&
x[i]->phase_unknown->moles <= MIN_RELATED_SURFACE &&
comp_ptr->Get_add_formula().size() == 0) ||
((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1
|| x[i]->type == SURFACE_CB2)
&& x[(size_t)i - 1]->phase_unknown != NULL &&
grams <= MIN_RELATED_SURFACE &&
comp_ptr1->Get_add_formula().size() == 0))
{
for (j = 0; j < l_count_rows; j++)
{
ineq_array[(size_t)j * max_column_count + (size_t)i] = 0.0;
}
}
}
}
/*
* No moles of surface
*/
if (use.Get_surface_ptr() != NULL)
{
LDBLE grams = 0;;
for (i = 0; i < count_unknowns; i++)
{
if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1
|| x[i]->type == SURFACE_CB2) && x[(size_t)i - 1]->phase_unknown == NULL)
{
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge);
grams = charge_ptr->Get_grams();
}
if ((x[i]->type == SURFACE &&
x[i]->phase_unknown == NULL &&
x[i]->moles <= MIN_RELATED_SURFACE) ||
((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1
|| x[i]->type == SURFACE_CB2)
&& x[(size_t)i - 1]->phase_unknown == NULL
&& grams <= MIN_RELATED_SURFACE))
{
for (j = 0; j < l_count_rows; j++)
{
ineq_array[(size_t)j * max_column_count + (size_t)i] = 0.0;
}
}
}
}
/*
* Moles of gas must be >= zero
*/
if (gas_in == TRUE)
{
for (i = (int)gas_unknown->number; i < (int)count_unknowns; i++)
{
if (x[i]->type == GAS_MOLES)
{
//memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]),
// (void *) &(zero[0]),
// ((size_t) count_unknowns + 1) * sizeof(LDBLE));
//std::fill(&(ineq_array[(size_t)l_count_rows * max_column_count]),
// &(ineq_array[l_count_rows * max_column_count + count_unknowns]),
// 0.0e0);
memset(&(ineq_array[(size_t)l_count_rows * max_column_count]), 0,
(count_unknowns + 1) * sizeof(LDBLE));
ineq_array[(size_t)l_count_rows * max_column_count + (size_t)i] = -1.0;
ineq_array[(size_t)l_count_rows * max_column_count + count_unknowns] =
x[i]->moles;
back_eq[l_count_rows] = i;
l_count_rows++;
}
else
{
break;
}
}
}
else if (use.Get_gas_phase_ptr() != NULL && gas_in == FALSE)
{
/*
* Moles of gas small and sum p < ptotal
*/
i = (int)gas_unknown->number;
for (j = 0; j < l_count_rows; j++)
{
ineq_array[(size_t)j * max_column_count + (size_t)i] = 0.0;
}
}
/*
* Phase must be "in" and moles of solid solution must be >= zero
*/
if (ss_unknown != NULL)
{
for (i = (int)ss_unknown->number; i < (int)count_unknowns; i++)
{
if (x[i]->type != SS_MOLES)
break;
if (x[i]->phase->in == TRUE && x[i]->ss_in == TRUE)
{
//memcpy((void *) &(ineq_array[(size_t)l_count_rows * max_column_count]),
// (void *) &(zero[0]),
// ((size_t) count_unknowns + 1) * sizeof(LDBLE));
memset(&(ineq_array[(size_t)l_count_rows * max_column_count]), 0,
(count_unknowns + 1) * sizeof(LDBLE));
ineq_array[(size_t)l_count_rows * max_column_count + (size_t)i] = 1.0;
ineq_array[(size_t)l_count_rows * max_column_count + count_unknowns] =
0.99 * x[i]->moles - MIN_TOTAL_SS;
back_eq[l_count_rows] = i;
l_count_rows++;
}
else
{
for (j = 0; j < l_count_rows; j++)
{
ineq_array[(size_t)j * max_column_count + (size_t)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[(size_t)l_count_rows * max_column_count]),
(void *) &(my_array[(size_t)i * (count_unknowns + 1)]),
((size_t) count_unknowns + 1) * sizeof(LDBLE));
back_eq[l_count_rows] = i;
for (j = 0; j < count_unknowns; j++)
{
if (x[j]->type < PP)
{
ineq_array[(size_t)l_count_rows * max_column_count + (size_t)j] = 0.0;
}
}
l_count_rows++;
}
}
}
/*
* Zero column for mass of water
*/
if (mass_oxygen_unknown != NULL && mass_water_switch == TRUE)
{
k = (int)mass_oxygen_unknown->number;
for (j = 0; j < l_count_rows + 1; j++)
{
ineq_array[(size_t)j * max_column_count + (size_t)k] = 0;
}
}
/*
* Scale column for pure phases
*/
for (i = 0; i < count_unknowns; i++)
{
if ((x[i]->type == PP || x[i]->type == SS_MOLES) && x[i]->phase->in == TRUE
&& pp_column_scale != 1.0)
{
for (j = 0; j < l_count_rows; j++)
{
ineq_array[(size_t)j * max_column_count + (size_t)i] *= pp_column_scale;
}
normal[i] = pp_column_scale;
}
}
if (debug_model == TRUE)
{
output_msg(sformatf( "\nA and B arrays:\n\n"));
array_print(&ineq_array[0], (int)l_count_rows, (int)count_unknowns + 1,
(int)max_column_count);
}
/*
* Calculate dimensions
*/
k = l_count_optimize; /* rows in A */
l = count_equal; /* rows in C */
m = l_count_rows - l - k; /* rows in E */
if (m < 0)
m = 0;
if (debug_model == TRUE)
{
output_msg(sformatf( "k, l, m\t%d\t%d\t%d\n", k, l, m));
}
#define SHRINK_ARRAY
#ifdef SHRINK_ARRAY
if ((sit_model || pitzer_model) && full_pitzer == FALSE)
{
n = (int)count_unknowns - (int)s_list.size();
for (int i = 0; i < l_count_rows; i++)
{
for (int j = 0; j < n; j++)
{
ineq_array[(size_t)i*((size_t)n+2) + (size_t)j] = ineq_array[(size_t)i*(count_unknowns+2) + (size_t)j];
}
//if (i > 0)
//{
// memcpy((void *) &ineq_array[i*(n+2)], (void *) &ineq_array[i*(count_unknowns+2)], (size_t) (n) * sizeof(LDBLE));
//}
ineq_array[(size_t)i*((size_t)n+2) + (size_t)n] = ineq_array[(size_t)i*(count_unknowns+2) + count_unknowns];
}
}
else
{
n = (int)count_unknowns; /* columns in A, C, E */
}
#else
n = count_unknowns; /* columns in A, C, E */
#endif
l_klmd = (int)max_row_count - 2;
l_nklmd = n + l_klmd;
l_n2d = n + 2;
/*
* Retain constraints on mineral mass transfers, even if infeasible on
* first attempt.
*/
l_kode = 1;
if (in_kode == 2)
{
l_kode = 1;
}
l_iter = 2*(n + l_count_rows);
/*
* Allocate space for arrays
*/
cu.resize(2 * (size_t)l_nklmd);
iu.resize(2 * (size_t)l_nklmd);
is.resize(l_klmd);
#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, l_nklmd, l_n2d, &ineq_array[0],
&l_kode, ineq_tol, &l_iter, &delta1[0], &res[0],
&l_error, &cu[0], &iu[0], &is[0], FALSE);
/* Set return_kode */
if (l_kode == 1)
{
return_code = ERROR;
}
else if (l_kode == 2)
{
return_code = 2;
}
else if (l_kode == 3)
{
return_code = ERROR;
return_code = 2;
warning_msg("Too many iterations in Cl1. Should not have done this.");
}
else
{
return_code = OK;
}
#ifdef SLNQ
/* if (l_kode > 0 && ((k + l) == count_unknowns)) { */
if (l_kode > 0 && ((k + l) <= count_unknowns))
{
if (add_trivial_eqns(k + l, count_unknowns, slnq_array) == TRUE)
{
if (debug_model == TRUE)
output_msg(sformatf( "Calling SLNQ, iteration %d\n",
iterations));
log_msg(sformatf( "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(sformatf( "Using SLNQ results.\n"));
log_msg(sformatf( "Using SLNQ results.\n"));
return_code = OK;
}
else
{
if (debug_model == TRUE)
output_msg(sformatf(
"Could not use SLNQ results.\n"));
log_msg(sformatf( "Could not use SLNQ results.\n"));
}
}
else
{
log_msg(sformatf(
"Could not call SLNQ, row %d, unknowns %d, iteration %d\n",
k + l, count_unknowns, iterations));
}
}
else if (l_kode > 0)
{
log_msg(sformatf( "Could not call SLNQ, row %d, unknowns %d\n",
k + l, count_unknowns));
}
#endif
/* Copy delta1 into delta and scale */
#ifdef SHRINK_ARRAY
//memcpy((void *) &(delta[0]), (void *) &(zero[0]),
// (size_t) count_unknowns * sizeof(LDBLE));
memset(&(delta[0]), 0,
count_unknowns * sizeof(LDBLE));
#endif
memcpy((void *) &(delta[0]), (void *) &(delta1[0]),
(size_t) n * sizeof(LDBLE));
for (i = 0; i < n; 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++)
{
my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] /= normal[i];
}
}
}
/* adjust unknowns where cl1 failed */
if (l_kode != 0)
{
for (i = 0; i < l_count_rows; i++)
{
/*
output_msg(sformatf( "%6d %-12.12s %10.2e\n", i,
x[back_eq[i]]->description, (double) res[i);
*/
j = back_eq[i];
if (x[j]->type == MB && delta[j] == 0.0 && fabs(res[i]) > ineq_tol)
{
delta[j] = res[i]/fabs(res[i]) * 1;
}
}
}
/*
* Debug, write results of ineq
*/
if (debug_model == TRUE)
{
output_msg(sformatf( "kode: %d\titer: %d\terror: %e\n", l_kode,
l_iter, (double) l_error));
output_msg(sformatf( "\nsolution vector:\n"));
for (i = 0; i < count_unknowns; i++)
{
output_msg(sformatf( "%6d %-12.12s %10.2e", i,
x[i]->description, (double) delta[i]));
if (x[i]->type == PP)
{
output_msg(sformatf( " -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(sformatf( " **"));
}
}
output_msg(sformatf( "\n"));
}
output_msg(sformatf( "\nresidual vector:\n"));
for (i = 0; i < l_count_rows; i++)
{
output_msg(sformatf( "%6d %-12.12s %10.2e\n", i,
x[back_eq[i]]->description, (double) res[i]));
}
}
#ifdef SLNQ
slnq_array = (LDBL *) free_check_null(slnq_array);
slnq_delta1 = (LDBL *) free_check_null(slnq_delta1);
#endif
return (return_code);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
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++)
{
my_array[i] = 0.0;
}
for (i = 1; i < count_unknowns; i++)
{
memcpy((void *) &(my_array[(size_t)i * (count_unknowns + 1)]),
(void *) &(my_array[0]), (size_t) count_unknowns * sizeof(LDBLE));
}
/*
* Add constant terms
*/
for (k = 0; k < (int)sum_jacob0.size(); k++)
{
*sum_jacob0[k].target += sum_jacob0[k].coef;
}
/*
* Add terms with coefficients of 1.0
*/
for (k = 0; k < (int)sum_jacob1.size(); k++)
{
*sum_jacob1[k].target += *sum_jacob1[k].source;
}
/*
* Add terms with coefficients != 1.0
*/
for (k = 0; k < (int)sum_jacob2.size(); k++)
{
*sum_jacob2[k].target += *sum_jacob2[k].source * sum_jacob2[k].coef;
}
/*
* Make final adjustments to jacobian array
*/
/*
* Ionic strength
*/
if (mu_unknown != NULL)
{
for (i = 0; i < count_unknowns; i++)
{
// using straight mu equation
my_array[(size_t)mu_unknown->number * (count_unknowns + 1) + i] *= 0.5;
}
my_array[(size_t)mu_unknown->number * (count_unknowns + 1) +
(size_t)mu_unknown->number] -= mass_water_aq_x;
}
/*
* Mass of oxygen
*/
if (mass_oxygen_unknown != NULL && mu_unknown != NULL)
{
my_array[(size_t)mu_unknown->number * (count_unknowns + 1) +
(size_t)mass_oxygen_unknown->number] -= mu_x * mass_water_aq_x;
}
/*
* Activity of water
*/
if (ah2o_unknown != NULL)
{
if (dampen_ah2o)
{
// factors for tanh attenuation to make ah2o positive
LDBLE y_sum = ah2o_unknown->f;
LDBLE x_h2o = mass_water_aq_x;
LDBLE a = AH2O_FACTOR;
LDBLE lim = 95.0 - 100.0*a*y_sum/x_h2o;
LDBLE factor = -a*(x_h2o*(-0.5*tanh(lim) - 0.5) + (50.0*a*y_sum - 47.5 * x_h2o) / (cosh(lim)*cosh(lim))) /
(x_h2o*x_h2o);
for (i = 0; i < count_unknowns; i++)
{
my_array[(size_t)ah2o_unknown->number * (count_unknowns + 1) + (size_t)i] *= factor;
}
// activity of water term
my_array[(size_t)ah2o_unknown->number * (count_unknowns + 1) +
(size_t)ah2o_unknown->number] -= exp(s_h2o->la * LOG_10);
// mass of water term
if (mass_oxygen_unknown != NULL)
{
my_array[(size_t)ah2o_unknown->number * (count_unknowns + 1) + (size_t)mass_oxygen_unknown->number] -=
a*y_sum*(x_h2o*(0.5*tanh(lim) + 0.5) + (47.5*x_h2o - 50.0*a*y_sum)/(cosh(lim)*cosh(lim))) /
(x_h2o*x_h2o*x_h2o);
}
}
else
{
for (i = 0; i < count_unknowns; i++)
{
my_array[(size_t)ah2o_unknown->number * (count_unknowns + 1) + i] *= -AH2O_FACTOR;
}
my_array[(size_t)ah2o_unknown->number * (count_unknowns + 1) + (size_t)ah2o_unknown->number] -=
mass_water_aq_x * exp(s_h2o->la * LOG_10);
if (mass_oxygen_unknown != NULL)
{
my_array[(size_t)ah2o_unknown->number * (count_unknowns + 1) + (size_t)mass_oxygen_unknown->number] -=
(exp(s_h2o->la * LOG_10) - 1) * mass_water_aq_x;
}
}
}
/*
* Surface charge balance
*/
if (surface_unknown != NULL && dl_type_x == cxxSurface::NO_DL)
{
if (use.Get_surface_ptr()->Get_type() != cxxSurface::CCM)
{
sinh_constant =
//sqrt(8 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) * tk_x *
// 1000);
sqrt(8 * eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) * tk_x *
1000);
for (i = 0; i < count_unknowns; i++)
{
cxxSurfaceCharge *charge_ptr = NULL;
if (x[i]->type == SURFACE_CB)
{
charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge);
}
if (x[i]->type == SURFACE_CB && charge_ptr->Get_grams() > 0)
{
for (j = 0; j < count_unknowns; j++)
{
my_array[(size_t)x[i]->number * (count_unknowns + 1) + (size_t)j] *=
F_C_MOL / (charge_ptr->Get_specific_area() * charge_ptr->Get_grams());
}
my_array[(size_t)x[i]->number * (count_unknowns + 1) + (size_t)x[i]->number] -=
sinh_constant * sqrt(mu_x) * cosh(x[i]->master[0]->s->la * LOG_10);
if (mu_unknown != NULL)
{
my_array[(size_t)x[i]->number * (count_unknowns + 1) +
(size_t)mu_unknown->number] -= 0.5 * sinh_constant / sqrt(mu_x) *
sinh(x[i]->master[0]->s->la * LOG_10);
}
}
}
}
else
{
for (i = 0; i < count_unknowns; i++)
{
cxxSurfaceCharge *charge_ptr = NULL;
if (x[i]->type == SURFACE_CB)
{
charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge);
}
if (x[i]->type == SURFACE_CB && charge_ptr->Get_grams() > 0)
{
for (j = 0; j < count_unknowns; j++)
{
my_array[(size_t)x[i]->number * (count_unknowns + 1) + (size_t)j] *=
F_C_MOL / (charge_ptr->Get_specific_area() * charge_ptr->Get_grams());
}
my_array[(size_t)x[i]->number * (count_unknowns + 1) + (size_t)x[i]->number] -=
charge_ptr->Get_capacitance0() * 2 * R_KJ_DEG_MOL * tk_x * LOG_10 / F_KJ_V_EQ;
}
}
}
}
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
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 < (int)sum_mb1.size(); k++)
{
*sum_mb1[k].target += *sum_mb1[k].source;
}
/*
* Add terms with coefficients != 1.0
*/
for (k = 0; k < (int)sum_mb2.size(); k++)
{
*sum_mb2[k].target += *sum_mb2[k].source * sum_mb2[k].coef;
}
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
mb_gases(void)
/* ---------------------------------------------------------------------- */
{
/*
* Determines whether gas_phase equation is needed
*/
gas_in = FALSE;
if (gas_unknown == NULL || use.Get_gas_phase_ptr() == NULL)
return (OK);
cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr();
if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE)
{
if (gas_unknown->f > gas_phase_ptr->Get_total_p() + 1e-7 ||
gas_unknown->moles > MIN_TOTAL)
{
gas_in = TRUE;
//patm_x = gas_phase_ptr->Get_total_p();
}
}
else
{
if (numerical_fixed_volume && (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume))
{
gas_in = TRUE;
}
else
{
gas_in = FALSE;
}
}
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
mb_ss(void)
/* ---------------------------------------------------------------------- */
{
LDBLE lp, log10_iap, total_moles;
LDBLE iapc, iapb, l_kc, l_kb, lc, lb, xcaq, xbaq, xb, xc;
LDBLE sigmapi_aq, sigmapi_solid;
LDBLE total_p;
class rxn_token *rxn_ptr;
/*
* Determines whether solid solution equation is needed
*/
if (ss_unknown == NULL || use.Get_ss_assemblage_ptr() == NULL)
return (OK);
std::vector<cxxSS *> ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize();
for (size_t i = 0; i < ss_ptrs.size(); i++)
{
cxxSS *ss_ptr = ss_ptrs[i];
total_moles = 0;
//bool ss_in = true;
for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++)
{
int l;
class phase *phase_ptr = phase_bsearch(ss_ptr->Get_ss_comps()[j].Get_name().c_str(), &l, FALSE);
if (phase_ptr->in == FALSE)
{
continue;
}
cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]);
total_moles += comp_ptr->Get_moles();
}
if (total_moles > 1.e10*MIN_TOTAL)
{
ss_ptr->Set_ss_in(true);
}
else if (ss_ptr->Get_a0() != 0.0 || ss_ptr->Get_a1() != 0.0)
{
int l;
class phase *phase0_ptr = phase_bsearch(ss_ptr->Get_ss_comps()[0].Get_name().c_str(), &l, FALSE);
class phase *phase1_ptr = phase_bsearch(ss_ptr->Get_ss_comps()[1].Get_name().c_str(), &l, FALSE);
/*
* Calculate IAPc and IAPb
*/
if (phase0_ptr->in == TRUE && phase0_ptr->rxn_x.token.size() != 0)
{
log10_iap = 0;
for (rxn_ptr = &phase0_ptr->rxn_x.token[0] + 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 (phase1_ptr->in == TRUE && phase1_ptr->rxn_x.token.size() != 0)
{
log10_iap = 0;
for (rxn_ptr = &phase1_ptr->rxn_x.token[0] + 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
*/
l_kc = exp(phase0_ptr->lk * LOG_10);
l_kb = exp(phase1_ptr->lk * LOG_10);
/*
* Solve for xb
*/
xb = ss_root(ss_ptr->Get_a0(), ss_ptr->Get_a1(), l_kc, l_kb, xcaq, xbaq);
/*
* Calculate lambdac and lambdab
*/
xc = 1 - xb;
lc = exp((ss_ptr->Get_a0() - ss_ptr->Get_a1() * (-4 * xb + 3)) * xb * xb);
lb = exp((ss_ptr->Get_a0() + ss_ptr->Get_a1() * (4 * xb - 1)) * xc * xc);
/*
* Calculate sigma pi, solid
*/
sigmapi_solid = xb * lb * l_kb + xc * lc * l_kc;
/*
* If Sigma pi, solid < sigma pi, aq, then use eqns
*/
if (sigmapi_solid < sigmapi_aq)
{
ss_ptr->Set_ss_in(true);
}
else
{
ss_ptr->Set_ss_in(false);
}
}
else
{
/*
* Calculate total mole fraction from solution activities
*/
total_p = 0;
for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++)
{
cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]);
int l;
class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE);
if (phase_ptr->in == TRUE)
{
lp = -phase_ptr->lk;
for (rxn_ptr = &phase_ptr->rxn_x.token[0] + 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)
{
ss_ptr->Set_ss_in(true);
}
else
{
ss_ptr->Set_ss_in(false);
}
}
}
for (int i = (int)ss_unknown->number; i < (int)count_unknowns; i++)
{
if (x[i]->type != SS_MOLES)
break;
cxxSS *ss_ptr = (cxxSS *) x[i]->ss_ptr;
x[i]->ss_in = FALSE;
if (x[i]->phase->in == TRUE && ss_ptr->Get_ss_in())
{
x[i]->ss_in = TRUE;
}
}
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
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;
LDBLE total_g;
class rxn_token *rxn_ptr;
/*
* la for master species
*/
for (i = 0; i < (int)master.size(); i++)
{
if (master[i]->in == REWRITE)
{
master[i]->s->la = master[i]->s->lm + master[i]->s->lg;
}
}
if (dl_type_x != cxxSurface::NO_DL)
{
//s_h2o->tot_g_moles = s_h2o->moles;
//s_h2o->tot_dh2o_moles = 0.0;
if (calculating_deriv && use.Get_surface_ptr() != NULL) // DL_pitz
if (use.Get_surface_ptr()->Get_debye_lengths() > 0)
s_h2o->moles = mass_water_bulk_x / gfw_water;
s_h2o->tot_g_moles = s_h2o->moles;
s_h2o->tot_dh2o_moles = 0.0;
}
for (i = 0; i < (int)this->s_x.size(); 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[0] + 1; rxn_ptr->s != NULL;
rxn_ptr++)
{
s_x[i]->lm += rxn_ptr->s->la * rxn_ptr->coef;
/*
if (isnan(rxn_ptr->s->la))
{
fprintf(stderr,"molalities la %s %e\n", rxn_ptr->s->name, rxn_ptr->s->la);
}
*/
}
if (s_x[i]->type == EX)
{
s_x[i]->moles = Utilities::safe_exp(s_x[i]->lm * LOG_10);
}
else if (s_x[i]->type == SURF)
{
s_x[i]->moles = Utilities::safe_exp(s_x[i]->lm * LOG_10);
}
else
{
s_x[i]->moles = under(s_x[i]->lm) * mass_water_aq_x;
if (s_x[i]->moles / mass_water_aq_x > 100)
{
log_msg(sformatf( "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.Get_surface_ptr() != NULL
&& dl_type_x != cxxSurface::NO_DL
&& (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC
|| pitzer_model || calculating_deriv)) // DL_pitz
{
calc_all_donnan();
}
class species *s_ptr = NULL;
for (i = 0; i < (int)this->s_x.size(); i++)
{
s_ptr = s_x[i];
if (s_ptr->type > HPLUS && s_ptr->type != EX && s_ptr->type != SURF)
continue;
if (use.Get_surface_ptr() != NULL && dl_type_x != cxxSurface::NO_DL && s_ptr->type <= HPLUS)
{
total_g = 0.0;
s_ptr->tot_dh2o_moles = 0.0;
for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++)
{
cxxSurfaceCharge & charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j];
cxxSpeciesDL & dl_ref = s_diff_layer[s_ptr->number][charge_ref.Get_name()];
cxxSurfDL & surf_dl_ref = charge_ref.Get_g_map()[s_ptr->z];
//s_diff_layer[is][charge_ref.Get_name()] = dl_ref
//charge_ref.Get_g_map()[s_ptr->z] = surf_dl
/*
* 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 */
dl_ref.Set_g_moles(s_ptr->moles * s_ptr->erm_ddl *
(surf_dl_ref.Get_g() + charge_ref.Get_mass_water() / mass_water_aq_x));
if (s_ptr->moles > 1e-30)
{
dl_ref.Set_dg_g_moles(s_ptr->dg * dl_ref.Get_g_moles() / s_ptr->moles);
}
/*
* first term of 63 is summed for all surfaces in
* s_ptr->tot_g_moles. This sum is then used in
* the jacobian for species i
*/
total_g += surf_dl_ref.Get_g() + charge_ref.Get_mass_water() / mass_water_aq_x;
/* revised eq. 63, second term */
/* g.dg is dg/dx(-2y**2) or dg/d(ln y) */
dl_ref.Set_dx_moles(s_ptr->moles * s_ptr->erm_ddl * surf_dl_ref.Get_dg());
/* revised eq. 63, third term */
dl_ref.Set_dh2o_moles(-s_ptr->moles * s_ptr->erm_ddl *
charge_ref.Get_mass_water() / mass_water_aq_x);
s_ptr->tot_dh2o_moles += dl_ref.Get_dh2o_moles();
/* surface related to phase */
dl_ref.Set_drelated_moles(s_ptr->moles * s_ptr->erm_ddl * charge_ref.Get_specific_area() *
use.Get_surface_ptr()->Get_thickness() / mass_water_aq_x);
}
s_ptr->tot_g_moles = s_ptr->moles * (1 + total_g * s_ptr->erm_ddl);
/* 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_ptr->moles > 1e-30)
{
s_ptr->dg_total_g = s_ptr->dg * s_ptr->tot_g_moles / s_ptr->moles;
}
else
{
s_ptr->dg_total_g = 0.0;
}
if (debug_diffuse_layer == TRUE)
{
output_msg(sformatf( "%s\t%e\t%e\n", s_ptr->name,
(double) s_ptr->moles,
(double) s_ptr->tot_g_moles));
output_msg(sformatf( "\tg\n"));
for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++)
{
cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j];
output_msg(sformatf( "\t%e",
(double) charge_ref.Get_g_map()[s_ptr->z].Get_g()));
}
output_msg(sformatf( "\n\tg_moles\n"));
for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++)
{
cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j];
int is = s_ptr->number;
output_msg(sformatf( "\t%e",
(double) s_diff_layer[is][charge_ref.Get_name()].Get_g_moles()));
}
output_msg(sformatf( "\n\tdg\n"));
for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++)
{
cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j];
output_msg(sformatf( "\t%e",
(double) charge_ref.Get_g_map()[s_ptr->z].Get_dg()));
}
output_msg(sformatf( "\n\tdx_moles\n"));
for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++)
{
int is = s_ptr->number;
cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j];
output_msg(sformatf( "\t%e",
(double) s_diff_layer[is][charge_ref.Get_name()].Get_dx_moles()));
}
output_msg(sformatf( "\n\tdh2o_moles\t%e\n",
(double) s_ptr->tot_dh2o_moles));
for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++)
{
cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j];
int is = s_ptr->number;
output_msg(sformatf( "\t%e",
s_diff_layer[is][charge_ref.Get_name()].Get_dh2o_moles()));
}
output_msg(sformatf( "\n"));
}
}
}
calc_gas_pressures();
calc_ss_fractions();
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
calc_gas_pressures(void)
/* ---------------------------------------------------------------------- */
{
//int n_g = 0;
LDBLE lp, V_m = 0;
class rxn_token *rxn_ptr;
std::vector<class phase *> phase_ptrs;
bool PR = false, pr_done = false;
/*
* moles and partial pressures for gases
*/
if (use.Get_gas_phase_ptr() == NULL)
return (OK);
cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr();
if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume)
{
if (iterations > 2)
return calc_fixed_volume_gas_pressures();
else
return OK;
}
if (iterations > 2 && gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME)
{
gas_phase_ptr->Set_total_moles(0);
}
for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++)
{
const cxxGasComp * gas_comp_ptr = &(gas_phase_ptr->Get_gas_comps()[i]);
int j;
class phase *phase_ptr = phase_bsearch(gas_comp_ptr->Get_phase_name().c_str(), &j, FALSE);
if (phase_ptr->in == TRUE)
{
phase_ptrs.push_back(phase_ptr);
if (!PR && phase_ptr->t_c > 0 && phase_ptr->p_c > 0)
PR = true;
//n_g++;
}
if (iterations > 2 && gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME)
{
gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + phase_ptr->moles_x);
}
}
if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE)
{
if (PR /*&& gas_unknown->gas_phase->total_p > 1 */ && iterations > 0)
{
calc_PR(phase_ptrs, gas_phase_ptr->Get_total_p(), tk_x, 0);
}
} else
{
if (PR)
{
if (gas_phase_ptr->Get_total_moles() > 0)
{
V_m = gas_phase_ptr->Get_volume() / gas_phase_ptr->Get_total_moles();
if (V_m < 0.016)
{
V_m = 0.016;
} else if (V_m > 1e4)
{
V_m = 1e4;
}
if (V_m < 0.02)
V_m = (8. * gas_phase_ptr->Get_v_m() + V_m) / 9;
else if (V_m < 0.03)
V_m = (6. * gas_phase_ptr->Get_v_m() + V_m) / 7;
else if (V_m < 0.05)
V_m = (4. * gas_phase_ptr->Get_v_m() + V_m) / 5;
else if (V_m < 0.07)
V_m = (2. * gas_phase_ptr->Get_v_m() + V_m) / 3;
else
V_m = (1. * gas_phase_ptr->Get_v_m() + V_m) / 2;
if ((pitzer_model || iterations > 99) && numerical_fixed_volume == false)
{
//V_m *= 1; /* debug */
numerical_fixed_volume = true;
//switch_numerical = true;
if (!pitzer_model) warning_msg("Numerical method failed, switching to numerical derivatives.");
prep();
//switch_numerical = false;
}
} else
V_m = 1.0;
calc_PR(phase_ptrs, 0, tk_x, V_m);
pr_done = true;
} else
{
gas_phase_ptr->Set_total_p(0);
}
}
gas_phase_ptr->Set_total_moles(0);
std::vector<cxxGasComp> gas_comps;
for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++)
{
const cxxGasComp *gas_comp = &(gas_phase_ptr->Get_gas_comps()[i]);
int j;
class phase *phase_ptr = phase_bsearch(gas_comp->Get_phase_name().c_str(), &j, FALSE);
if (phase_ptr->in == TRUE)
{
lp = -phase_ptr->lk;
for (rxn_ptr = &phase_ptr->rxn_x.token[0] + 1; rxn_ptr->s != NULL;
rxn_ptr++)
{
lp += rxn_ptr->s->la * rxn_ptr->coef;
}
phase_ptr->p_soln_x = exp(LOG_10 * (lp - phase_ptr->pr_si_f));
//if (!strcmp(phase_ptr->name, "H2O(g)") && phase_ptr->p_soln_x > 90)
// phase_ptr->p_soln_x = 90;
if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE)
{
phase_ptr->moles_x = phase_ptr->p_soln_x *
gas_unknown->moles / gas_phase_ptr->Get_total_p();
phase_ptr->fraction_x =
phase_ptr->moles_x / gas_unknown->moles;
}
else
{
if (pr_done)
{
lp = phase_ptr->p_soln_x / gas_phase_ptr->Get_total_p() *
gas_phase_ptr->Get_volume() / V_m;
if (lp > 0)
phase_ptr->moles_x = lp;
if (iterations > 50)
{
lp *= 1.0; /* debug */
}
} else
{
phase_ptr->moles_x = phase_ptr->p_soln_x *
gas_phase_ptr->Get_volume() / (R_LITER_ATM * tk_x);
gas_phase_ptr->Set_total_p(gas_phase_ptr->Get_total_p() + phase_ptr->p_soln_x);
}
gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() +
phase_ptr->moles_x);
}
}
else
{
phase_ptr->moles_x = 0;
phase_ptr->fraction_x = 0;
}
}
if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && !PR)
{
/*
* Fixed-volume gas phase reacting with a solution
* Change pressure used in logK to pressure of gas phase
*/
if (gas_phase_ptr->Get_total_p() > MAX_P_NONLLNL && llnl_temp.size() == 0)
{
gas_phase_ptr->Set_total_moles(0);
for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++)
{
const cxxGasComp *gas_comp = &(gas_phase_ptr->Get_gas_comps()[i]);
int j;
class phase *phase_ptr = phase_bsearch(gas_comp->Get_phase_name().c_str(), &j, FALSE);
if (phase_ptr->in == TRUE)
{
phase_ptr->moles_x *= MAX_P_NONLLNL / gas_phase_ptr->Get_total_p();
gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() +
phase_ptr->moles_x);
}
}
gas_phase_ptr->Set_total_p(MAX_P_NONLLNL);
}
}
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
calc_ss_fractions(void)
/* ---------------------------------------------------------------------- */
{
LDBLE moles, n_tot;
/*
* moles and lambdas for solid solutions
*/
if (ss_unknown == NULL)
return (OK);
/*
* Calculate mole fractions and log lambda and derivative factors
*/
if (use.Get_ss_assemblage_ptr() == NULL)
return (OK);
std::vector<cxxSS *> ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize();
for (size_t i = 0; i < ss_ptrs.size(); i++)
{
cxxSS *ss_ptr = ss_ptrs[i];
n_tot = 0;
for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++)
{
cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[k]);
moles = comp_ptr->Get_moles();
if (moles < 0)
{
moles = MIN_TOTAL_SS;
comp_ptr->Set_initial_moles(moles);
}
n_tot += moles;
}
ss_ptr->Set_total_moles(n_tot);
for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++)
{
cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[k]);
int l;
class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE);
moles = comp_ptr->Get_moles();
if (moles < 0)
{
moles = MIN_TOTAL_SS;
}
comp_ptr->Set_fraction_x(moles / n_tot);
comp_ptr->Set_log10_fraction_x(log10(moles / n_tot));
/* all mb and jacobian items must be in x or phase to be static between models */
phase_ptr->log10_fraction_x = comp_ptr->Get_log10_fraction_x();
}
if (ss_ptr->Get_a0() != 0.0 || ss_ptr->Get_a1() != 0)
{
ss_binary(ss_ptr);
}
else
{
ss_ideal(ss_ptr);
}
}
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
ss_binary(cxxSS *ss_ptr)
/* ---------------------------------------------------------------------- */
{
LDBLE nb, nc, n_tot, xb, xc, dnb, dnc, l_a0, l_a1;
//LDBLE xb2, xb3, xb4, xc2, xc3;
LDBLE xb2, xc2;
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 = ss_ptr->Get_total_moles();
cxxSScomp *comp0_ptr = &(ss_ptr->Get_ss_comps()[0]);
cxxSScomp *comp1_ptr = &(ss_ptr->Get_ss_comps()[1]);
int l;
class phase *phase0_ptr = phase_bsearch(comp0_ptr->Get_name().c_str(), &l, FALSE);
class phase *phase1_ptr = phase_bsearch(comp1_ptr->Get_name().c_str(), &l, FALSE);
nc = comp0_ptr->Get_moles();
xc = nc / n_tot;
nb = comp1_ptr->Get_moles();
xb = nb / n_tot;
/*
* In miscibility gap
*/
l_a0 = ss_ptr->Get_a0();
l_a1 = ss_ptr->Get_a1();
if (ss_ptr->Get_miscibility() && xb > ss_ptr->Get_xb1()
&& xb < ss_ptr->Get_xb2())
{
xb1 = ss_ptr->Get_xb1();
xc1 = 1.0 - xb1;
comp0_ptr->Set_fraction_x(xc1);
comp0_ptr->Set_log10_fraction_x(log10(xc1));
phase0_ptr->log10_fraction_x =
comp0_ptr->Get_log10_fraction_x();
comp1_ptr->Set_fraction_x(xb1);
comp1_ptr->Set_log10_fraction_x(log10(xb1));
phase1_ptr->log10_fraction_x =
comp1_ptr->Get_log10_fraction_x();
comp0_ptr->Set_log10_lambda(
xb1 * xb1 * (l_a0 - l_a1 * (3 - 4 * xb1)) / LOG_10);
phase0_ptr->log10_lambda =
comp0_ptr->Get_log10_lambda();
comp1_ptr->Set_log10_lambda(
xc1 * xc1 * (l_a0 + l_a1 * (4 * xb1 - 1)) / LOG_10);
phase1_ptr->log10_lambda =
comp1_ptr->Get_log10_lambda();
comp0_ptr->Set_dnb(0);
comp0_ptr->Set_dnc(0);
comp1_ptr->Set_dnb(0);
comp1_ptr->Set_dnc(0);
phase0_ptr->dnb = 0;
phase0_ptr->dnc = 0;
phase1_ptr->dnb = 0;
phase1_ptr->dnc = 0;
}
else
{
/*
* Not in miscibility gap
*/
comp0_ptr->Set_fraction_x(xc);
comp0_ptr->Set_log10_fraction_x(log10(xc));
phase0_ptr->log10_fraction_x =
comp0_ptr->Get_log10_fraction_x();
comp1_ptr->Set_fraction_x(xb);
comp1_ptr->Set_log10_fraction_x(log10(xb));
phase1_ptr->log10_fraction_x =
comp1_ptr->Get_log10_fraction_x();
comp0_ptr->Set_log10_lambda(
xb * xb * (l_a0 - l_a1 * (3 - 4 * xb)) / LOG_10);
phase0_ptr->log10_lambda =
comp0_ptr->Get_log10_lambda();
comp1_ptr->Set_log10_lambda(
xc * xc * (l_a0 + l_a1 * (4 * xb - 1)) / LOG_10);
phase1_ptr->log10_lambda =
comp1_ptr->Get_log10_lambda();
xc2 = xc * xc;
//xc3 = xc2 * xc;
xb2 = xb * xb;
//xb3 = xb2 * xb;
//xb4 = xb3 * xb;
/* xb4 = xb4; */
/* xc3 = xc3; */
/* used derivation that did not substitute x2 = 1-x1 */
/* first component, df1/dn1 */
dnc = 2 * l_a0 * xb2 + 12 * l_a1 * xc * xb2 + 6 * l_a1 * xb2;
phase0_ptr->dnc = -xb / nc + dnc / n_tot;
/* first component, df1/dn2 */
dnb =
1 - 2 * l_a0 * xb + 2 * l_a0 * xb2 + 8 * l_a1 * xc * xb -
12 * l_a1 * xc * xb2 - 2 * l_a1 * xb + 2 * l_a1 * xb2;
phase0_ptr->dnb = dnb / n_tot;
/* second component, df2/dn1 */
dnc =
1 - 2 * l_a0 * xc + 2 * l_a0 * xc2 - 8 * l_a1 * xb * xc +
12 * l_a1 * xb * xc2 + 2 * l_a1 * xc - 2 * l_a1 * xc2;
phase1_ptr->dnc = dnc / n_tot;
/* second component, df2/dn2 */
dnb = 2 * l_a0 * xc2 + 12 * l_a1 * xb * xc2 - 6 * l_a1 * xc2;
phase1_ptr->dnb = -xc / nb + dnb / n_tot;
}
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
ss_ideal(cxxSS *ss_ptr)
/* ---------------------------------------------------------------------- */
{
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 = ss_ptr->Get_total_moles();
/*
* Ideal solid solution
*/
ss_ptr->Set_dn(1.0 / n_tot);
for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++)
{
cxxSScomp *compk_ptr = &(ss_ptr->Get_ss_comps()[k]);
int l;
class phase *phasek_ptr = phase_bsearch(compk_ptr->Get_name().c_str(), &l, FALSE);
n_tot1 = 0;
for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++)
{
cxxSScomp *compj_ptr = &(ss_ptr->Get_ss_comps()[j]);
if (j != k)
{
n_tot1 += compj_ptr->Get_moles();
}
}
compk_ptr->Set_log10_lambda(0);
compk_ptr->Set_log10_lambda(0);
compk_ptr->Set_dnb(-(n_tot1) / (compk_ptr->Get_moles() * n_tot));
phasek_ptr->dnb = compk_ptr->Get_dnb();
compk_ptr->Set_dn(ss_ptr->Get_dn());
phasek_ptr->dn = ss_ptr->Get_dn();
}
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
reset(void)
/* ---------------------------------------------------------------------- */
{
/*
* Checks deltas (changes to unknowns) to make sure they are reasonable
* Scales deltas if necessary
* Updates unknowns with deltas
*/
int i;
int converge;
LDBLE up, down;
LDBLE d;
LDBLE factor, f0;
LDBLE sum_deltas;
LDBLE step_up;
LDBLE mu_calc;
LDBLE old_moles;
int warning;
/*
* Calculate interphase mass transfers
*/
cxxPPassemblageComp * comp_ptr;
step_up = log(step_size_now);
factor = 1.;
if ((pure_phase_unknown != NULL || ss_unknown != NULL)
&& calculating_deriv == FALSE)
{
/*
* Don`t take out more mineral than is present
*/
for (i = 0; i < count_unknowns; i++)
{
if ((x[i]->type == PP || x[i]->type == SS_MOLES) && x[i]->phase->in == TRUE)
{
if (delta[i] < -1e8)
{
delta[i] = -10.;
}
else if (delta[i] > 1e8)
{
delta[i] = 10;
}
if (x[i]->dissolve_only == TRUE)
{
assert (x[i]->type == PP);
//comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name);
comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr;
assert(comp_ptr);
if ((delta[i] < 0.0)
&& (-delta[i] >
(comp_ptr->Get_initial_moles() - x[i]->moles)))
{
if ((comp_ptr->Get_initial_moles() - x[i]->moles) !=
0.0)
{
f0 = fabs(delta[i] /
(comp_ptr->Get_initial_moles() -
x[i]->moles));
if (f0 > factor)
{
if (debug_model == TRUE)
{
output_msg(sformatf(
"%-10.10s, Precipitating too much dissolve_only mineral.\tDelta %e\tCurrent %e\tInitial %e\n",
x[i]->description,
(double) delta[i],
(double) x[i]->moles,
(double) comp_ptr->Get_initial_moles()));
}
factor = f0;
}
}
else
{
if (debug_model == TRUE)
{
output_msg(sformatf(
"%-10.10s, Precipitating dissolve_only mineral.\tDelta %e\n",
x[i]->description,
(double) delta[i]));
}
delta[i] = 0;
}
}
}
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(sformatf(
"%-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(sformatf(
"%-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 (x[i]->ss_comp_name != NULL && delta[i] < -x[i]->phase->delta_max)
// Uses delta_max computed in step
// delta_max is the maximum amount of the mineral that could form based
// on the limiting element in the system
{
f0 = -delta[i] / x[i]->phase->delta_max;
if (f0 > factor)
{
if (debug_model == TRUE)
{
output_msg(sformatf(
"%-10.10s, Precipitating too much mineral.\t%f\n",
x[i]->description, (double) f0));
}
factor = f0;
}
}
else if (x[i]->ss_comp_name != NULL && delta[i] < -pe_step_size*x[i]->moles)
{
f0 = (-delta[i]) / (pe_step_size*x[i]->moles);
if (f0 > factor)
{
if (debug_model == TRUE)
{
output_msg(sformatf(
"%-10.10s, Precipitating too much mineral.\t%f\n",
x[i]->description, (double)f0));
}
factor = f0;
}
}
else if (x[i]->ss_comp_name != NULL && delta[i] > x[i]->moles/ pe_step_size)
{
f0 = (delta[i]) / (x[i]->moles/ pe_step_size);
if (f0 > factor)
{
if (debug_model == TRUE)
{
output_msg(sformatf(
"%-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
*/
warning = 0;
for (i = 0; i < count_unknowns; i++)
{
/*if (isnan(delta[i]))*/
if (!PHR_ISFINITE((double) delta[i]))
{
warning ++;
delta[i] = 0;
}
}
if (warning > 0)
{
error_string = sformatf( "%d delta equal NaN\n", warning);
warning_msg(error_string);
}
if (pure_phase_unknown != NULL || gas_unknown != NULL
|| ss_unknown != NULL)
{
for (i = 0; i < count_unknowns; i++)
{
x[i]->delta = 0.0;
}
for (i = 0; i < (int)sum_delta.size(); 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 == SS_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++)
{
/* fixes underflow problem on Windows */
if (delta[i] > 0)
{
sum_deltas += delta[i];
}
else
{
sum_deltas -= delta[i];
}
/*sum_deltas += fabs(delta[i]); */
if (calculating_deriv == FALSE)
{
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;
down = mu_x;
}
else if (x[i]->type == AH2O)
{
down = up;
if (pitzer_model || sit_model)
{
up = 0.05;
down = -0.03;
}
}
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 == SS_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 == PITZER_GAMMA)
{
up = step_up;
if (up > 1)
{
up = 0.7;
}
down = 1.3 * up;
}
else if (x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1
|| x[i]->type == SURFACE_CB2)
{
up = step_up;
down = 1.3 * up;
/*
up = 1.3;
down = 1.2;
*/
}
if (delta[i] > 0.0)
{
f0 = delta[i] / up;
if (f0 > factor)
{
if (debug_model == TRUE)
{
output_msg(sformatf( "%-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(sformatf( "%-10.10s\t%f\n",
x[i]->description, (double) f0));
}
factor = f0;
}
}
}
}
/*converge=TRUE; */
if (debug_model == TRUE)
{
output_msg(sformatf( "\nSum of deltas: %12.6f\n",
(double) sum_deltas));
output_msg(sformatf( "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 != SS_MOLES)
delta[i] *= factor;
}
warning = 0;
for (i = 0; i < count_unknowns; i++)
{
/*if (isnan(delta[i]))*/
if (!PHR_ISFINITE((double) delta[i]))
{
warning ++;
delta[i] = 0;
}
}
if (warning > 0)
{
error_string = sformatf( "%d delta equal NaN after scaling\n", warning);
warning_msg(error_string);
}
/*
* 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)
{
cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp);
old_moles = x[i]->moles;
if (x[i]->phase_unknown != NULL)
{
x[i]->moles = comp_ptr->Get_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) - 15.;
}
}
else if (comp_ptr->Get_phase_name().size() > 0)
{
/* probably initial surface calculation */
if (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.;
}
}
}
}
/* 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(sformatf(
"%-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 || x[i]->type == SURFACE_CB1
|| x[i]->type == SURFACE_CB2)
{
d = delta[i] / LOG_10;
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge);
if (x[i]->phase_unknown != NULL)
{
charge_ptr->Set_grams(
(x[i]->phase_unknown->moles -
delta[x[i]->phase_unknown->number]));
if (charge_ptr->Get_grams() <= MIN_RELATED_SURFACE)
{
charge_ptr->Set_grams(0.0);
}
}
if (charge_ptr->Get_grams() <= MIN_RELATED_SURFACE)
{
charge_ptr->Set_grams(0.0);
}
x[i]->related_moles = charge_ptr->Get_grams();
if (debug_model == TRUE)
{
output_msg(sformatf(
"%-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;
/* recalculate g's for component */
if (dl_type_x != cxxSurface::NO_DL
&& (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM
|| (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC
&& x[i]->type == SURFACE_CB2)))
{
if (debug_diffuse_layer == TRUE)
{
output_msg(sformatf(
"\ncharge, old g, new g, dg*delta,"
" dg, delta\n"));
}
std::map<LDBLE, cxxSurfDL>::iterator jit;
for (jit = charge_ptr->Get_g_map().begin(); jit != charge_ptr->Get_g_map().end(); jit++)
{
if (debug_diffuse_layer == TRUE)
{
output_msg(sformatf(
"\t%12.4e\t%12.4e\t%12.4e\t%12.4e\t%12.4e\n",
(double) jit->second.Get_g(),
(double) jit->second.Get_g() +
(double) (jit->second.Get_dg() *
delta[i]),
(double) (jit->second.Get_dg() *
delta[i]),
(double) jit->second.Get_dg(),
(double) delta[i]));
}
if (use.Get_surface_ptr()->Get_dl_type() != cxxSurface::DONNAN_DL)
{
jit->second.Set_g(jit->second.Get_g() +
jit->second.Get_dg() * delta[i]);
}
}
if (use.Get_surface_ptr()->Get_dl_type() == cxxSurface::DONNAN_DL)
{
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(sformatf(
"%-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(sformatf(
"%-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)
{
// using straight ionic strength equation
{
mu_calc = 0.5 * mu_unknown->f / mass_water_aq_x;
}
if (debug_model == TRUE)
{
output_msg(sformatf( "Calculated mu: %e\n",
(double) mu_calc));
output_msg(sformatf(
"%-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-11)
{
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(sformatf(
"%-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;
ah2o_x = exp(s_h2o->la * LOG_10);
/* 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(sformatf(
"%-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(sformatf(
"%-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 && dl_type_x != cxxSurface::NO_DL)
{
output_msg(sformatf(
"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 (use.Get_surface_ptr() != NULL)
{
if (use.Get_surface_ptr()->Get_debye_lengths() > 0)
x[i]->master[0]->s->moles = mass_water_bulk_x / gfw_water;
}
if (mass_water_aq_x < 1e-10)
{
error_string = sformatf(
"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)
{
//comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name);
comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr;
/*if (fabs(delta[i]) > epsilon) converge=FALSE; */
if (debug_model == TRUE)
{
output_msg(sformatf(
"%-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]));
}
if (equal(x[i]->moles, delta[i], ineq_tol))
{
x[i]->moles = 0.0;
}
else
{
x[i]->moles -= delta[i];
}
if (x[i]->dissolve_only == TRUE)
{
if (equal
(x[i]->moles, comp_ptr->Get_initial_moles(), ineq_tol))
x[i]->moles = comp_ptr->Get_initial_moles();
}
/*if (fabs(x[i]->moles) < MIN_RELATED_SURFACE) x[i]->moles = 0.0; */
}
else if (x[i]->type == GAS_MOLES)
{
cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr();
/*if (fabs(delta[i]) > epsilon) converge=FALSE; */
/*if (gas_in == TRUE && fabs(residual[i]) > epsilon) converge=FALSE; */
if (debug_model == TRUE)
{
output_msg(sformatf(
"%-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;
if (x[i] == gas_unknown && gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME &&
!calculating_deriv)
{
if (debug_model == TRUE)
{
output_msg(sformatf(
"%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e\n",
"Pressure", "old P",
(double) last_patm_x, "new P",
(double) gas_phase_ptr->Get_total_p(), "iter P",
(double) patm_x));
}
patm_x = gas_phase_ptr->Get_total_p();
//if (patm_x < 1e-10 && patm_x < p_sat)
//{
// patm_x = ( 1 * patm_x + p_sat) / 2.0;
//}
if (llnl_temp.size() == 0)
{
if (patm_x > MAX_P_NONLLNL)
patm_x = MAX_P_NONLLNL;
}
}
last_patm_x = patm_x;
}
else if (x[i]->type == SS_MOLES && x[i]->ss_in == TRUE)
{
/*if (fabs(delta[i]) > epsilon) converge=FALSE; */
/*if (x[i]->ss_in == TRUE && fabs(residual[i]) > epsilon) converge=FALSE; */
if (debug_model == TRUE)
{
output_msg(sformatf(
"%-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_SS && calculating_deriv == FALSE)
x[i]->moles = MIN_TOTAL_SS;
cxxSScomp *comp_ptr = (cxxSScomp *) x[i]->ss_comp_ptr;
comp_ptr->Set_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(sformatf(
"%-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
|| ss_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(sformatf(
"%-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 Phreeqc::
residuals(void)
/* ---------------------------------------------------------------------- */
{
/*
* Calculates residuals for all equations
*/
int i, j;
int converge;
LDBLE l_toler;
//LDBLE sum_residual;
LDBLE sinh_constant;
LDBLE sum, sum1;
class master *master_ptr, *master_ptr1, *master_ptr2;
LDBLE sigmaddl, negfpsirt;
int print_fail;
std::vector<LDBLE> cd_psi;
print_fail = FALSE;
//sum_residual = 0.0;
sigmaddl = 0;
sum = 0;
/*
* Calculate residuals
*/
converge = TRUE;
l_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]) > l_toler * x[i]->moles &&
fabs(residual[i]) > sqrt(fabs(x[i]->moles) * MIN_TOTAL) &&
x[i]->moles > MIN_TOTAL)
||
x[i]->moles < 0)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %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]) > l_toler * x[i]->moles)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n", iterations,
x[i]->description, i, residual[i]));
converge = FALSE;
}
}
else if (x[i]->type == SOLUTION_PHASE_BOUNDARY)
{
residual[i] = x[i]->f * LOG_10;
if (fabs(residual[i]) > l_toler)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n", iterations,
x[i]->description, i, residual[i]));
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]) >= l_toler * mu_x * mass_water_aq_x)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n", iterations,
x[i]->description, i, residual[i]));
converge = FALSE;
}
}
else if (x[i]->type == MU /*&& pitzer_model == FALSE && sit_model == FALSE*/)
{
// Using straight ionic strength equation
{
residual[i] = mass_water_aq_x * mu_x - 0.5 * x[i]->f;
}
if (fabs(residual[i]) > l_toler * mu_x * mass_water_aq_x)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n", iterations,
x[i]->description, i, residual[i]));
converge = FALSE;
}
}
else if (x[i]->type == AH2O)
{
if (dampen_ah2o)
{
// a = 0.017; Y = sum(m(i)); X = Mw (mass of water)
// Aw = (1 - a y/x) 0.5 (tanh(100 (0.95 - ay/x)) + 1) + 0.05 (0.5 (1 - tanh(100(0.95 - ay/x))))
//residual[i] = exp(s_h2o->la * LOG_10) - ((1.0 - 0.017 * x[i]->f/mass_water_aq_x) *
// 0.5*(tanh(100.0*(0.95 - 0.017*x[i]->f/mass_water_aq_x)) + 1) +
// 0.05*0.5*(1.0 - tanh(100.0*(0.95 - 0.017*x[i]->f/mass_water_aq_x)))) ;
residual[i] = exp(s_h2o->la * LOG_10) - ((1.0 - AH2O_FACTOR * x[i]->f/mass_water_aq_x) *
0.5*(tanh(100.0*(0.95 - AH2O_FACTOR*x[i]->f/mass_water_aq_x)) + 1) +
0.05*0.5*(1.0 - tanh(100.0*(0.95 - AH2O_FACTOR*x[i]->f/mass_water_aq_x)))) ;
}
else
{
//residual[i] = mass_water_aq_x * exp(s_h2o->la * LOG_10) - mass_water_aq_x +
// 0.017 * x[i]->f;
residual[i] = mass_water_aq_x * exp(s_h2o->la * LOG_10) - mass_water_aq_x +
AH2O_FACTOR * x[i]->f;
}
if (pitzer_model || sit_model)
{
residual[i] = pow((LDBLE) 10.0, s_h2o->la) - AW;
if (full_pitzer == FALSE)
{
residual[i] = 0.0;
}
}
if (fabs(residual[i]) > l_toler)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n", iterations,
x[i]->description, i, residual[i]));
converge = FALSE;
}
}
else if (x[i]->type == MH
&& (pitzer_model == FALSE || pitzer_pe == TRUE))
{
#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]) >
l_toler * (x[i]->moles + 2 * mass_oxygen_unknown->moles))
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n", iterations,
x[i]->description, i, residual[i]));
converge = FALSE;
}
#else
if (fabs(residual[i]) >
l_toler * (x[i]->moles + 2 * mass_oxygen_unknown->moles +
charge_balance_unknown->moles))
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n", iterations,
x[i]->description, i, residual[i]));
converge = FALSE;
}
#endif
#else
if (fabs(residual[i]) > l_toler * x[i]->moles)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n", iterations,
x[i]->description, i, residual[i]));
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 * l_toler * x[i]->moles)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n", iterations,
x[i]->description, i, residual[i]));
converge = FALSE;
}
}
else if (x[i]->type == PP)
{
//cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name);
cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr;
residual[i] = x[i]->f * LOG_10;
if (comp_ptr->Get_add_formula().size() == 0)
{
if (x[i]->dissolve_only == TRUE)
{
if ((residual[i] > l_toler && x[i]->moles > 0.0)
|| (residual[i] < -l_toler
&& (comp_ptr->Get_initial_moles() -
x[i]->moles) > 0))
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n",
iterations, x[i]->description, i,
residual[i]));
converge = FALSE;
}
}
else
{
if (residual[i] < -l_toler || iterations < 1)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n",
iterations, x[i]->description, i,
residual[i]));
converge = FALSE;
}
}
}
else
{
/* if (x[i]->moles > 0.0 && fabs(residual[i]) > l_toler) converge = FALSE; */
if (residual[i] < -l_toler || iterations < 1)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n",
iterations, x[i]->description, i,
residual[i]));
converge = FALSE;
}
}
}
else if (x[i]->type == GAS_MOLES)
{
cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr();
if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME &&
(gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume)
{
residual[i] = x[i]->moles - x[i]->phase->moles_x;
}
else
{
residual[i] = gas_phase_ptr->Get_total_p() - x[i]->f;
}
if (fabs(residual[i]) > l_toler && gas_in == TRUE)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n", iterations,
x[i]->description, i, residual[i]));
converge = FALSE;
}
if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME &&
(fabs(last_patm_x - patm_x) > 0.001 || fabs(last_patm_x - gas_phase_ptr->Get_total_p()) > 0.001)
&& !calculating_deriv)
{
if (print_fail)
output_msg(sformatf("Failed pressure test %d: %e %e %e\n", iterations, last_patm_x, patm_x,
gas_phase_ptr->Get_total_p()));
converge = FALSE;
}
}
else if (x[i]->type == SS_MOLES)
{
if (x[i]->ss_in == FALSE)
continue;
residual[i] = x[i]->f * LOG_10;
if (fabs(residual[i]) > l_toler)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n", iterations,
x[i]->description, i, residual[i]));
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]) > l_toler)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n",
iterations, x[i]->description, i,
residual[i]));
converge = FALSE;
}
}
else if (fabs(residual[i]) > l_toler * x[i]->moles)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n", iterations,
x[i]->description, i, residual[i]));
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]) > l_toler)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n",
iterations, x[i]->description, i,
residual[i]));
converge = FALSE;
}
}
else if (fabs(residual[i]) < ineq_tol && fabs(residual[i]) < 1e-2*x[i]->moles)
{
}
else if (fabs(residual[i]) > l_toler * x[i]->moles)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n", iterations,
x[i]->description, i, residual[i]));
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]) > l_toler)
{
/*
fprintf(stderr,"Residuals %d: %s %d %e\n", iterations, x[i]->description, i, residual[i]);
*/
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n", iterations,
x[i]->description, i, residual[i]));
converge = FALSE;
}
}
else if (x[i]->type == SURFACE_CB && use.Get_surface_ptr()->Get_type() == cxxSurface::DDL)
{
/*sinh_constant = 0.1174; */
sinh_constant =
//sqrt(8 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) *
// tk_x * 1000);
sqrt(8 * eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) *
tk_x * 1000);
/* if (x[i]->surface_charge->grams <= MIN_RELATED_SURFACE) { */
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge);
if (charge_ptr->Get_grams() == 0)
{
residual[i] = 0.0;
}
else if (dl_type_x != cxxSurface::NO_DL)
{
residual[i] = -x[i]->f;
}
else
{
residual[i] = sinh_constant * sqrt(mu_x) * sinh(x[i]->master[0]->s->la * LOG_10) -
x[i]->f * F_C_MOL / (charge_ptr->Get_specific_area() * charge_ptr->Get_grams());
}
if (debug_model == TRUE)
{
output_msg(sformatf( "Charge/Potential\n"));
if (charge_ptr->Get_grams() > 0)
{
if (dl_type_x != cxxSurface::NO_DL)
output_msg(sformatf("\tSum of surface + diffuse layer charge %e eq\n", (double)(x[i]->f)));
else
output_msg(sformatf(
"\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(sformatf( "\tResidual %e\n",
(double) x[i]->f));
}
output_msg(sformatf( "\t grams %g\n",
(double) charge_ptr->Get_grams()));
output_msg(sformatf( "\tCharge from potential %e eq\n",
(double) (charge_ptr->Get_specific_area() *
charge_ptr->Get_grams() / F_C_MOL *
sinh_constant * sqrt(mu_x) *
sinh(x[i]->master[0]->s->la * LOG_10))));
output_msg(sformatf( "\t FPsi/2RT %e\n",
(double) (x[i]->master[0]->s->la * LOG_10)));
output_msg(sformatf( "\t Sinh(FPsi/2RT) %e\n",
sinh(x[i]->master[0]->s->la * LOG_10)));
output_msg(sformatf( "\t Cosh(FPsi/2RT) %e\n",
cosh(x[i]->master[0]->s->la * LOG_10)));
output_msg(sformatf( "\t Sqrt(mu_x) %e\n",
sqrt(mu_x)));
}
if (charge_ptr->Get_grams() > MIN_RELATED_SURFACE
&& fabs(residual[i]) > l_toler)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n", iterations,
x[i]->description, i, residual[i]));
converge = FALSE;
}
}
else if (x[i]->type == SURFACE_CB
&& use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC)
{
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge);
if (charge_ptr->Get_grams() == 0)
{
residual[i] = 0.0;
cd_psi.clear();
cd_psi.push_back(0.0);
cd_psi.push_back(0.0);
cd_psi.push_back(0.0);
}
else
{
/* sum is in moles of charge */
/*psi = pow(10, x[i]->surface_charge->psi_master->s->la); */ /* = exp(-Fpsi/RT) */
master_ptr =
surface_get_psi_master(charge_ptr->Get_name().c_str(),
SURF_PSI);
master_ptr1 =
surface_get_psi_master(charge_ptr->Get_name().c_str(),
SURF_PSI1);
master_ptr2 =
surface_get_psi_master(charge_ptr->Get_name().c_str(),
SURF_PSI2);
cd_psi.clear();
cd_psi.push_back(-(master_ptr->s->la * LOG_10) * R_KJ_DEG_MOL * tk_x /
F_KJ_V_EQ);
cd_psi.push_back(-(master_ptr1->s->la * LOG_10) * R_KJ_DEG_MOL * tk_x /
F_KJ_V_EQ);
cd_psi.push_back(-(master_ptr2->s->la * LOG_10) * R_KJ_DEG_MOL * tk_x /
F_KJ_V_EQ);
sum = 0;
for (size_t j = 0; j < x[i]->comp_unknowns.size(); j++)
{
sum +=
x[i]->comp_unknowns[j]->moles *
x[i]->comp_unknowns[j]->master[0]->s->z;
}
charge_ptr->Set_sigma0(
(x[i]->f +
sum) * F_C_MOL / (charge_ptr->Get_specific_area() *
charge_ptr->Get_grams()));
/* f is in moles */
/* eqns A-3 */
residual[i] =
charge_ptr->Get_sigma0() -
charge_ptr->Get_capacitance0() *
(cd_psi[0] - cd_psi[1]);
}
if (charge_ptr->Get_grams() > MIN_RELATED_SURFACE
&& fabs(residual[i]) > l_toler)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual A %d: %s %d %e\n",
iterations, x[i]->description, i, residual[i]));
converge = FALSE;
}
}
else if (x[i]->type == SURFACE_CB && use.Get_surface_ptr()->Get_type() == cxxSurface::CCM)
{
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge);
if (charge_ptr->Get_grams() == 0)
{
residual[i] = 0.0;
}
else if (dl_type_x != cxxSurface::NO_DL)
{
residual[i] = -x[i]->f;
}
else
{
residual[i] =
charge_ptr->Get_capacitance0() * x[i]->master[0]->s->la * 2 * R_KJ_DEG_MOL *
tk_x * LOG_10 / F_KJ_V_EQ -
x[i]->f * F_C_MOL / (charge_ptr->Get_specific_area() *
charge_ptr->Get_grams());
}
if (debug_model == TRUE)
{
output_msg(sformatf( "Charge/Potential\n"));
if (charge_ptr->Get_grams() > 0)
{
if (dl_type_x != cxxSurface::NO_DL)
output_msg(sformatf("\tSum of surface + diffuse layer charge %e eq\n", (double)(x[i]->f)));
else
output_msg(sformatf(
"\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(sformatf( "\tResidual %e\n",
(double) x[i]->f));
}
output_msg(sformatf( "\t grams %g\n",
(double) charge_ptr->Get_grams()));
output_msg(sformatf( "\tCharge from potential %e eq\n",
(double) (charge_ptr->Get_capacitance0() * x[i]->master[0]->s->la * 2 * R_KJ_DEG_MOL *
tk_x * LOG_10 / F_KJ_V_EQ)));
output_msg(sformatf( "\t Psi %e\n",
(double) (x[i]->master[0]->s->la * 2 * R_KJ_DEG_MOL *
tk_x * LOG_10 / F_KJ_V_EQ)));
}
if (charge_ptr->Get_grams() > MIN_RELATED_SURFACE
&& fabs(residual[i]) > l_toler)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual %d: %s %d %e\n", iterations,
x[i]->description, i, residual[i]));
converge = FALSE;
}
}
else if (x[i]->type == SURFACE_CB1)
{
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge);
if (charge_ptr->Get_grams() == 0)
{
residual[i] = 0.0;
}
else
{
/* eqns A-4 */
/*psi = pow(10, x[i]->surface_charge->psi_master1->s->la); */ /* = exp(-Fpsi/RT) */
charge_ptr->Set_sigma1(
x[i]->f * F_C_MOL / (charge_ptr->Get_specific_area() *
charge_ptr->Get_grams()));
residual[i] =
(charge_ptr->Get_sigma0() +
charge_ptr->Get_sigma1()) -
charge_ptr->Get_capacitance1() *
(cd_psi[1] - cd_psi[2]);
}
if (charge_ptr->Get_grams() > MIN_RELATED_SURFACE
&& fabs(residual[i]) > l_toler)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual B %d: %s %d %e\n",
iterations, x[i]->description, i, residual[i]));
converge = FALSE;
}
}
else if (x[i]->type == SURFACE_CB2)
{
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge);
if (charge_ptr->Get_grams() == 0)
{
residual[i] = 0.0;
}
else if (dl_type_x != cxxSurface::NO_DL)
{
sum = 0;
sum1 = 0;
for (j = 0; j < (int)this->s_x.size(); j++)
{
if (s_x[j]->type == SURF)
{
sum += under(s_x[j]->lm) * s_x[j]->dz[2];
}
if (s_x[j]->type < H2O)
{
int is = s_x[j]->number;
sum1 += s_x[j]->z * s_x[j]->z * s_diff_layer[is][charge_ptr->Get_name()].Get_g_moles();
}
}
charge_ptr->Set_sigma2(
sum * F_C_MOL / (charge_ptr->Get_specific_area() *
charge_ptr->Get_grams()));
charge_ptr->Set_sigmaddl(
(x[i]->f -
sum) * F_C_MOL / (charge_ptr->Get_specific_area() *
charge_ptr->Get_grams()));
residual[i] =
x[i]->f + (charge_ptr->Get_sigma0() +
charge_ptr->Get_sigma1()) *
(charge_ptr->Get_specific_area() *
charge_ptr->Get_grams()) / F_C_MOL;
/* residual[i] = sum + (x[i]->surface_charge->sigma0 + x[i]->surface_charge->sigma1) * (x[i]->surface_charge->specific_area * x[i]->surface_charge->grams) / F_C_MOL */
}
else
{
/* eqns A-6 and A-7 */
sinh_constant =
//sqrt(8 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) *
// tk_x * 1000);
sqrt(8 * eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) *
tk_x * 1000);
/*
* 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
*/
master_ptr2 =
surface_get_psi_master(charge_ptr->Get_name().c_str(),
SURF_PSI2);
negfpsirt = master_ptr2->s->la * LOG_10;
sum = 0;
sum1 = 0;
for (j = 0; j < (int)this->s_x.size(); j++)
{
if (s_x[j]->type < H2O)
{
sum +=
under(s_x[j]->lm) *
(exp(s_x[j]->z * negfpsirt) - 1);
sum1 += under(s_x[j]->lm) * s_x[j]->z;
}
}
/* add fictitious monovalent ion that balances charge */
//sum += fabs(sum1) * (exp(-sum1 / fabs(sum1) * negfpsirt) - 1);
if (sum1 >= 0)
{
sum += fabs(sum1) * (exp(-negfpsirt) - 1);
}
else
{
sum += fabs(sum1) * (exp(negfpsirt) - 1);
}
if (sum < 0)
{
sum = -sum;
{
if (print_fail)
output_msg(sformatf(
"Failed Residual C %d: %s %d %e %e\n",
iterations, x[i]->description, i, sum,
l_toler));
converge = FALSE;
}
/*output_msg(sformatf( "Negative sum, iteration %d\n", iterations)); */
}
charge_ptr->Set_sigma2(
x[i]->f * F_C_MOL / (charge_ptr->Get_specific_area() *
charge_ptr->Get_grams()));
if ((negfpsirt) < 0)
{
sigmaddl = -0.5 * sinh_constant * sqrt(sum);
}
else
{
sigmaddl = 0.5 * sinh_constant * sqrt(sum);
}
charge_ptr->Set_sigmaddl(sigmaddl);
residual[i] =
(charge_ptr->Get_sigma0() +
charge_ptr->Get_sigma1() +
charge_ptr->Get_sigma2()) + sigmaddl;
}
if (debug_model == TRUE)
{
master_ptr =
surface_get_psi_master(charge_ptr->Get_name().c_str(),
SURF_PSI);
master_ptr1 =
surface_get_psi_master(charge_ptr->Get_name().c_str(),
SURF_PSI1);
master_ptr2 =
surface_get_psi_master(charge_ptr->Get_name().c_str(),
SURF_PSI2);
output_msg(sformatf( "CD_music Charge/Potential 2\n"));
output_msg(sformatf( "\tgrams %g\n",
(double) charge_ptr->Get_grams()));
output_msg(sformatf( "\tCapacitances %g\t%g\n",
(double) charge_ptr->Get_capacitance0(),
charge_ptr->Get_capacitance1()));
output_msg(sformatf( "\t-F/(RT) %g\n",
(double) -F_KJ_V_EQ / (R_KJ_DEG_MOL * tk_x)));
output_msg(sformatf( "\tResidual 0 %14e\n",
(double) residual[master_ptr->unknown->number]));
output_msg(sformatf( "\tResidual 1 %14e\n",
(double) residual[master_ptr1->unknown->number]));
output_msg(sformatf( "\tResidual 2 %14e\n",
(double) residual[master_ptr2->unknown->number]));
output_msg(sformatf( "\texp(-FPsi0/RT) %14e",
(double) pow((LDBLE) 10., master_ptr->s->la)));
output_msg(sformatf( "\tPsi0 %14e\n",
(double) cd_psi[0]));
output_msg(sformatf( "\texp(-FPsi1/RT) %14e",
(double) pow((LDBLE) 10., master_ptr1->s->la)));
output_msg(sformatf( "\tPsi1 %14e\n",
(double) cd_psi[1]));
output_msg(sformatf( "\texp(-FPsi2/RT) %14e",
(double) pow((LDBLE) 10., master_ptr2->s->la)));
output_msg(sformatf( "\tPsi2 %14e\n",
(double) cd_psi[2]));
output_msg(sformatf( "\tf 0 %14e",
(double) master_ptr->unknown->f));
output_msg(sformatf( "\tsigma 0 %14e\n",
(double) charge_ptr->Get_sigma0()));
output_msg(sformatf( "\tf 1 %14e",
(double) master_ptr1->unknown->f));
output_msg(sformatf( "\tsigma 1 %14e\n",
(double) charge_ptr->Get_sigma1()));
output_msg(sformatf( "\tf 2 %14e",
(double) master_ptr2->unknown->f));
output_msg(sformatf( "\tsigma 2 %14e\n",
(double) charge_ptr->Get_sigma2()));
output_msg(sformatf( "\tsigma ddl %14e\n",
(double) sigmaddl));
output_msg(sformatf( "\texp sum %14e\n",
(double) sum));
}
if (charge_ptr->Get_grams() > MIN_RELATED_SURFACE
&& fabs(residual[i]) > l_toler)
{
if (print_fail)
output_msg(sformatf(
"Failed Residual D %d: %s %d %e\n",
iterations, x[i]->description, i, residual[i]));
converge = FALSE;
}
}
/*
* Store residuals in array
*/
my_array[((size_t)i + 1) * (count_unknowns + 1) - 1] = residual[i];
//sum_residual += fabs(residual[i]);
}
/*
* Return
*/
if ((pitzer_model == TRUE || sit_model == TRUE) && iterations < 1)
return (OK);
if (converge == TRUE)
{
return (CONVERGED);
}
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
set(int initial)
/* ---------------------------------------------------------------------- */
{
/*
* Sets initial guesses for unknowns if initial == TRUE
* Revises guesses whether initial is true or not
*/
int i;
cxxSolution *solution_ptr;
/*
* Set initial log concentrations to zero
*/
if (pitzer_model == TRUE)
return (set_pz(initial));
if (sit_model == TRUE)
return (set_sit(initial));
iterations = -1;
solution_ptr = use.Get_solution_ptr();
for (i = 0; i < (int)this->s_x.size(); i++)
{
s_x[i]->lm = LOG_ZERO_MOLALITY;
s_x[i]->lg = 0.0;
}
/*
* Set master species activities
*/
tc_x = solution_ptr->Get_tc();
tk_x = tc_x + 273.15;
patm_x = solution_ptr->Get_patm(); // done in calc_rho_0(tc, pa)
potV_x = solution_ptr->Get_potV();
/*
* H+, e-, H2O
*/
mass_water_aq_x = solution_ptr->Get_mass_water();
mu_x = solution_ptr->Get_mu();
s_h2o->moles = mass_water_aq_x / gfw_water;
s_h2o->la = log10(solution_ptr->Get_ah2o());
s_hplus->la = -solution_ptr->Get_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->Get_pe();
if (initial == TRUE)
initial_guesses();
if (dl_type_x != cxxSurface::NO_DL)
initial_surface_water();
revise_guesses();
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
initial_guesses(void)
/* ---------------------------------------------------------------------- */
{
/*
* Make initial guesses for activities of master species and
* ionic strength
*/
int i;
cxxSolution *solution_ptr;
// mu_x is reset here, but the real, already calculated mu_x must be used for INITIAL_EXCHANGE & _SURFACE appt
solution_ptr = use.Get_solution_ptr();
mu_x =
s_hplus->moles +
exp((solution_ptr->Get_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 Phreeqc::
revise_guesses(void)
/* ---------------------------------------------------------------------- */
{
/*
* Revise la's of master species
*/
int i;
int l_iter, max_iter, repeat, fail;
LDBLE weight, f;
LDBLE d;
max_iter = 10;
gammas(mu_x);
l_iter = 0;
repeat = TRUE;
fail = FALSE;;
while (repeat == TRUE)
{
l_iter++;
if (debug_set == TRUE)
{
output_msg(sformatf( "\nBeginning set iteration %d.\n",
l_iter));
}
if (l_iter == max_iter + 1)
{
log_msg(sformatf(
"Did not converge in set, iteration %d.\n",
iterations));
fail = TRUE;
}
if (l_iter > 2 * max_iter)
{
log_msg(sformatf(
"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(sformatf(
"\n\t%5s at beginning of set %d: %e\t%e\t%e\n",
x[i]->description, l_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 (isnan(f) || !_finite(f))*/
if (!PHR_ISFINITE((double) f))
{
f = 0;
}
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;
d = 0;
// avoid underflows and overflows
if (x[i]->moles > 1e101 || x[i]->moles < 1e-101 ||
x[i]->sum > 1e101 || x[i]->sum < 1e-101)
{
LDBLE d1 = log10(x[i]->moles);
LDBLE d2 = log10(x[i]->sum);
LDBLE d3 = d1 - d2;
if (d3 > DBL_MAX_10_EXP/2)
{
d = pow(10.0, DBL_MAX_10_EXP/2.);
}
else if (d3 < DBL_MIN_10_EXP/2.)
{
d = pow(10.0, DBL_MIN_10_EXP/2.);
}
}
else
{
d = fabs(x[i]->moles / x[i]->sum);
}
LDBLE d1;
if (d > 0)
{
d1 = weight * log10(d);
if (PHR_ISFINITE((double) d1) /*&& d1 < 5.0*/)
{
x[i]->master[0]->s->la += d1;
}
else
{
warning_msg("Adjustment to la in revise_guesses was NaN\n");
x[i]->master[0]->s->la += 5.0;
}
}
else
{
x[i]->master[0]->s->la += 5.0;
}
}
if (debug_set == TRUE)
{
output_msg(sformatf(
"\t%5s not converged in set %d: %e\t%e\t%e\n",
x[i]->description, l_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(sformatf(
"%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));
}
}
}
}
}
log_msg(sformatf( "Iterations in revise_guesses: %d\n", l_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 Phreeqc::
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;
class 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);
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 < (int)this->s_x.size(); 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;
if (use.Get_surface_ptr() != NULL)
{
if (use.Get_surface_ptr()->Get_debye_lengths() > 0 && state >= REACTION
&& s_x[i]->type == H2O)
{
total_h_x -= 2 * mass_water_surfaces_x / gfw_water;
total_o_x -= mass_water_surfaces_x / gfw_water;
}
}
}
/*
* Sum valence states, put in master->total
*/
for (i = 0; i < (int)master.size(); i++)
{
master[i]->total = 0.0;
master[i]->total_primary = 0.0;
}
for (i = 0; i < (int)species_list.size(); 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 (size_t j = 0; j < x[i]->master.size(); 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 < (int)master.size(); i++)
{
master[i]->elt->primary->total_primary += master[i]->total;
}
/*
* Calculate isotope ratios
*/
calculate_values();
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
surface_model(void)
/* ---------------------------------------------------------------------- */
{
/*
* Use extra iterative loop to converge on g_factors
*/
int i, debug_diffuse_layer_save, debug_model_save;
cxxSolution *solution_ptr;
LDBLE prev_aq_x;
/*
* 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)
{
same_model = FALSE;
}
else
{
same_model = check_same_model();
}
if (dl_type_x != cxxSurface::NO_DL && same_model == FALSE)
{
s_diff_layer.clear();
for (i = 0; i < (int)s.size(); i++)
{
std::map < std::string, cxxSpeciesDL > dl;
s_diff_layer.push_back(dl);
for (size_t j = 0; j < use.Get_surface_ptr()->Get_surface_charges().size(); j++)
{
cxxSpeciesDL species_dl;
std::string name = use.Get_surface_ptr()->Get_surface_charges()[j].Get_name();
s_diff_layer.back()[name] = species_dl;
}
}
}
if (state >= REACTION && dl_type_x != cxxSurface::NO_DL)
{
if (use.Get_mix_ptr() != NULL)
{
mass_water_bulk_x = 0.0;
std::map<int, LDBLE>::const_iterator cit;
for (cit = use.Get_mix_ptr()->Get_mixComps().begin(); cit != use.Get_mix_ptr()->Get_mixComps().end(); cit++)
{
solution_ptr = Utilities::Rxn_find(Rxn_solution_map, cit->first);
mass_water_bulk_x += solution_ptr->Get_mass_water() * cit->second;
}
}
else
{
mass_water_bulk_x = use.Get_solution_ptr()->Get_mass_water();
}
for (i = 0; i < (int) use.Get_surface_ptr()->Get_surface_charges().size(); i++)
{
cxxSurfaceCharge *charge_ptr = &(use.Get_surface_ptr()->Get_surface_charges()[i]);
mass_water_bulk_x += charge_ptr->Get_mass_water();
if (use.Get_surface_ptr()->Get_debye_lengths() > 0)
charge_ptr->Set_mass_water(0.0);
}
}
prep();
k_temp(tc_x, patm_x);
if (use.Get_surface_ptr()->Get_dl_type() == cxxSurface::DONNAN_DL)
{
initial_surface_water();
calc_init_donnan();
}
else
{
calc_init_g();
}
if (state >= REACTION && !use.Get_surface_ptr()->Get_new_def())
{
set(FALSE);
}
else
{
set(TRUE);
}
if (model() == ERROR)
return (ERROR);
g_iterations = 0;
if (use.Get_surface_ptr()->Get_dl_type() == cxxSurface::DONNAN_DL)
{
do
{
g_iterations++;
prev_aq_x = mass_water_aq_x;
k_temp(tc_x, patm_x);
gammas(mu_x);
molalities(TRUE);
mb_sums();
if (model() == ERROR)
return (ERROR);
if (!use.Get_surface_ptr()->Get_related_phases()
&& !use.Get_surface_ptr()->Get_related_rate())
initial_surface_water();
if (debug_model == TRUE)
{
output_msg(sformatf(
"Surface_model (Donnan approximation): %d g_iterations, %d model iterations\n",
g_iterations, iterations));
}
}
while ((calc_all_donnan() == FALSE
|| fabs(1 - prev_aq_x / mass_water_aq_x) > 1e-6)
&& g_iterations < itmax);
}
else
{
do
{
g_iterations++;
if (g_iterations > itmax - 10)
{
debug_model = TRUE;
debug_diffuse_layer = TRUE;
}
gammas(mu_x);
molalities(TRUE);
mb_sums();
if (model() == ERROR)
return (ERROR);
if (!use.Get_surface_ptr()->Get_related_phases()
&& !use.Get_surface_ptr()->Get_related_rate())
initial_surface_water();
if (debug_model == TRUE)
{
output_msg(sformatf(
"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)
{
pr.all = TRUE;
pr.surface = TRUE;
pr.species = TRUE;
pr.use = TRUE;
print_all();
error_msg("Did not converge on g (diffuse layer excess)", STOP);
}
debug_diffuse_layer = debug_diffuse_layer_save;
debug_model = debug_model_save;
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
free_model_allocs(void)
/* ---------------------------------------------------------------------- */
{
/*
* free space allocated in model
*/
int i;
for (i = 0; i < (int)x.size(); i++)
{
unknown_free(x[i]);
}
x.clear();
count_unknowns = 0;
max_unknowns = 0;
my_array.clear();
delta.clear();
residual.clear();
s_x.clear();
sum_mb1.clear();
sum_mb2.clear();
sum_jacob0.clear();
sum_jacob1.clear();
sum_jacob2.clear();
sum_delta.clear();
return (OK);
}
#ifdef SLNQ
/* ---------------------------------------------------------------------- */
int Phreeqc::
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 Phreeqc::
ss_root(LDBLE l_a0, LDBLE l_a1, LDBLE l_kc, LDBLE l_kb, LDBLE xcaq, LDBLE xbaq)
/* ---------------------------------------------------------------------- */
{
int i;
LDBLE x0, y0, x1, y1, xb, miny;
/*
* Bracket answer
*/
x0 = 0.0;
x1 = 0.0;
y0 = ss_f(x0, l_a0, l_a1, l_kc, l_kb, xcaq, xbaq);
miny = fabs(y0);
for (i = 1; i <= 10; i++)
{
x1 = (LDBLE) i / 10;
y1 = ss_f(x1, l_a0, l_a1, l_kc, l_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 = ss_halve(l_a0, l_a1, x0, x1, l_kc, l_kb, xcaq, xbaq);
}
return (xb);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
ss_halve(LDBLE l_a0, LDBLE l_a1, LDBLE x0, LDBLE x1, LDBLE l_kc, LDBLE l_kb,
LDBLE xcaq, LDBLE xbaq)
/* ---------------------------------------------------------------------- */
{
int i;
LDBLE l_x, y0, dx, y;
y0 = ss_f(x0, l_a0, l_a1, l_kc, l_kb, xcaq, xbaq);
dx = (x1 - x0);
/*
* Loop for interval halving
*/
for (i = 0; i < 100; i++)
{
dx *= 0.5;
l_x = x0 + dx;
y = ss_f(l_x, l_a0, l_a1, l_kc, l_kb, xcaq, xbaq);
if (dx < 1e-8 || y == 0)
{
break;
}
if (y0 * y >= 0)
{
x0 = l_x;
y0 = y;
}
}
return (x0 + dx);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
ss_f(LDBLE xb, LDBLE l_a0, LDBLE l_a1, LDBLE l_kc, LDBLE l_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((l_a0 - l_a1 * (-4 * xb + 3)) * xb * xb);
lb = exp((l_a0 + l_a1 * (4 * xb - 1)) * xc * xc);
r = lc * l_kc / (lb * l_kb);
f = xcaq * (xb / r + xc) + xbaq * (xb + r * xc) - 1;
return (f);
}
//#define ORIGINAL
#ifdef ORIGINAL
/* ---------------------------------------------------------------------- */
int Phreeqc::
numerical_jacobian(void)
/* ---------------------------------------------------------------------- */
{
std::vector<double> base;
LDBLE d, d1, d2;
int i, j;
cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr();
if (!
(numerical_deriv ||
(use.Get_surface_ptr() != NULL && use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) ||
(gas_phase_ptr != NULL && gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME &&
(gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume)
))
return(OK);
calculating_deriv = TRUE;
gammas(mu_x);
molalities(TRUE);
mb_sums();
residuals();
/*
* Clear array, note residuals are in array[i, count_unknowns+1]
*/
for (i = 0; i < count_unknowns; i++)
{
my_array[i] = 0.0;
}
for (i = 1; i < count_unknowns; i++)
{
memcpy((void *) &(my_array[(size_t)i * (count_unknowns + 1)]),
(void *) &(my_array[0]), count_unknowns * sizeof(LDBLE));
}
base.resize(count_unknowns);
base = residual;
d = 0.0001;
d1 = d * LOG_10;
d2 = 0;
for (i = 0; i < count_unknowns; i++)
{
switch (x[i]->type)
{
case MB:
case ALK:
case CB:
case SOLUTION_PHASE_BOUNDARY:
case EXCH:
case SURFACE:
case SURFACE_CB:
case SURFACE_CB1:
case SURFACE_CB2:
x[i]->master[0]->s->la += d;
d2 = d * LOG_10;
break;
case MH:
s_eminus->la += d;
d2 = d * LOG_10;
break;
case AH2O:
x[i]->master[0]->s->la += d;
d2 = d * LOG_10;
break;
case PITZER_GAMMA:
x[i]->s->lg += d;
d2 = d;
break;
case MH2O:
//mass_water_aq_x *= (1 + d);
//x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water;
//d2 = log(1.0 + d);
//break;
// DL_pitz
d1 = mass_water_aq_x * d;
mass_water_aq_x += d1;
if (use.Get_surface_in() && dl_type_x == cxxSurface::DONNAN_DL)
mass_water_bulk_x += d1;
x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water;
d2 = d1;
break;
case MU:
d2 = d * mu_x;
mu_x += d2;
gammas(mu_x);
break;
case PP:
for (j = 0; j < count_unknowns; j++)
{
delta[j] = 0.0;
}
d2 = -1e-8;
delta[i] = d2;
reset();
d2 = delta[i];
break;
case SS_MOLES:
if (x[i]->ss_in == FALSE)
continue;
for (j = 0; j < count_unknowns; j++)
{
delta[j] = 0.0;
}
/*d2 = -1e-8; */
d2 = d * 10 * x[i]->moles;
//d2 = -.1 * x[i]->moles;
/*
if (d2 > -1e-10) d2 = -1e-10;
calculating_deriv = FALSE;
*/
delta[i] = d2;
/*fprintf (stderr, "delta before reset %e\n", delta[i]); */
reset();
d2 = delta[i];
/*fprintf (stderr, "delta after reset %e\n", delta[i]); */
break;
case GAS_MOLES:
if (gas_in == FALSE)
continue;
d2 = (x[i]->moles > 1 ? 1 : 20);
d2 *= d * x[i]->moles;
if (d2 < 1e-14)
d2 = 1e-14;
x[i]->moles += d2;
break;
}
molalities(TRUE);
mb_sums();
/*
mb_ss();
mb_gases();
*/
residuals();
//output_msg(sformatf( "%d\n", i));
for (j = 0; j < count_unknowns; j++)
{
// avoid overflow
if (residual[j] > 1.0e101)
{
LDBLE t = (LDBLE) pow((LDBLE) 10.0, (LDBLE) (DBL_MAX_10_EXP - 50.0));
if (residual[j] > t)
{
my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] = -pow(10.0, DBL_MAX_10_EXP - 50.0);
}
else
{
my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] = -(residual[j] - base[j]) / d2;
if (x[i]->type == MH2O) // DL_pitz
my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] *= mass_water_aq_x;
}
}
else if (residual[j] < -1.0e101)
{
LDBLE t = pow((LDBLE) 10.0, (LDBLE) (DBL_MIN_10_EXP + 50.0));
if (residual[j] < -t)
{
my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] = pow(10.0, DBL_MIN_10_EXP + 50.0);
}
else
{
my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] = -(residual[j] - base[j]) / d2;
if (x[i]->type == MH2O) // DL_pitz
my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] *= mass_water_aq_x;
}
}
else
{
my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] = -(residual[j] - base[j]) / d2;
if (x[i]->type == MH2O) // DL_pitz
my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] *= mass_water_aq_x;
if (!PHR_ISFINITE(my_array[(size_t)j * (count_unknowns + 1) + (size_t)i]))
{
//fprintf(stderr, "oops, got NaN: %e, %e, %e, %e\n", residual[j], base[j], d2, array[j * (count_unknowns + 1) + i]);
}
}
//output_msg(sformatf( "\t%d %e %e %e %e\n", j, array[j*(count_unknowns + 1) + i] , residual[j], base[j], d2));
}
switch (x[i]->type)
{
case MB:
case ALK:
case CB:
case SOLUTION_PHASE_BOUNDARY:
case EXCH:
case SURFACE:
case SURFACE_CB:
case SURFACE_CB1:
case SURFACE_CB2:
case AH2O:
x[i]->master[0]->s->la -= d;
break;
case MH:
s_eminus->la -= d;
if (my_array[(size_t)i * (count_unknowns + 1) + (size_t)i] == 0)
{
/*output_msg(sformatf( "Zero diagonal for MH\n")); */
my_array[(size_t)i * (count_unknowns + 1) + (size_t)i] =
under(s_h2->lm) * 2;
}
break;
case PITZER_GAMMA:
x[i]->s->lg -= d;
break;
case MH2O:
//mass_water_aq_x /= (1 + d);
//x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water;
//break;
//DL_pitz
mass_water_aq_x -= d1;
if (use.Get_surface_in() && dl_type_x == cxxSurface::DONNAN_DL)
mass_water_bulk_x -= d1;
x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water;
break;
case MU:
mu_x -= d2;
gammas(mu_x);
break;
case PP:
delta[i] = -d2;
reset();
break;
case SS_MOLES:
delta[i] = -d2;
reset();
break;
case GAS_MOLES:
x[i]->moles -= d2;
break;
}
}
molalities(TRUE);
mb_sums();
mb_gases();
mb_ss();
residuals();
base.clear();
calculating_deriv = FALSE;
return OK;
}
#else
/* ---------------------------------------------------------------------- */
int Phreeqc::
numerical_jacobian(void)
/* ---------------------------------------------------------------------- */
{
std::vector<double> base;
LDBLE d, d1, d2;
int i, j;
cxxGasPhase* gas_phase_ptr = use.Get_gas_phase_ptr();
std::vector<class phase*> phase_ptrs;
std::vector<class phase> base_phases;
cxxGasPhase base_gas_phase;
cxxSurface base_surface;
if (!
(numerical_deriv ||
(use.Get_surface_ptr() != NULL && use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) ||
(gas_phase_ptr != NULL && gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME &&
(gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume)
))
return(OK);
//jacobian_sums();
if (use.Get_surface_ptr() != NULL)
{
base_surface = *use.Get_surface_ptr();
}
if (use.Get_gas_phase_ptr() != NULL)
{
//cxxGasPhase* gas_phase_ptr = use.Get_gas_phase_ptr();
base_gas_phase = *gas_phase_ptr;
base_phases.resize(gas_phase_ptr->Get_gas_comps().size());
for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++)
{
const cxxGasComp* gas_comp_ptr = &(gas_phase_ptr->Get_gas_comps()[i]);
class phase* phase_ptr = phase_bsearch(gas_comp_ptr->Get_phase_name().c_str(), &j, FALSE);
phase_ptrs.push_back(phase_ptr);
base_phases[i] = *phase_ptr;
}
}
calculating_deriv = TRUE;
gammas(mu_x);
molalities(TRUE);
mb_sums();
//mb_gases();
//mb_ss();
residuals();
/*
* Clear array, note residuals are in array[i, count_unknowns+1]
*/
//for (i = 0; i < count_unknowns; i++)
//{
// my_array[i] = 0.0;
//}
//for (i = 1; i < count_unknowns; i++)
//{
// memcpy((void*)&(my_array[(size_t)i * (count_unknowns + 1)]),
// (void*)&(my_array[0]), count_unknowns * sizeof(LDBLE));
//}
base.resize(count_unknowns);
base = residual;
d = 0.0001;
d1 = d * LOG_10;
d2 = 0;
for (i = 0; i < count_unknowns; i++)
{
switch (x[i]->type)
{
case MB:
case ALK:
case CB:
case SOLUTION_PHASE_BOUNDARY:
case EXCH:
case SURFACE:
case SURFACE_CB:
case SURFACE_CB1:
case SURFACE_CB2:
x[i]->master[0]->s->la += d;
d2 = d;// *LOG_10;
break;
case MH:
s_eminus->la += d;
d2 = d;// *LOG_10;
break;
case AH2O:
x[i]->master[0]->s->la += d;
d2 = d;// *LOG_10;
break;
case PITZER_GAMMA:
x[i]->s->lg += d;
d2 = d;
break;
case MH2O:
//mass_water_aq_x *= (1 + d);
//x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water;
//d2 = log(1.0 + d);
//break;
// DL_pitz
d1 = mass_water_aq_x * d;
mass_water_aq_x += d1;
if (use.Get_surface_in() && dl_type_x == cxxSurface::DONNAN_DL)
mass_water_bulk_x += d1;
x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water;
d2 = d1;
break;
case MU:
d2 = d * mu_x;
mu_x += d2;
//gammas(mu_x);
break;
case PP:
for (j = 0; j < count_unknowns; j++)
{
delta[j] = 0.0;
}
d2 = -1e-8;
delta[i] = d2;
reset();
d2 = delta[i];
break;
case SS_MOLES:
if (x[i]->ss_in == FALSE)
continue;
for (j = 0; j < count_unknowns; j++)
{
delta[j] = 0.0;
}
/*d2 = -1e-8; */
d2 = d * 10 * x[i]->moles;
//d2 = -.1 * x[i]->moles;
/*
if (d2 > -1e-10) d2 = -1e-10;
calculating_deriv = FALSE;
*/
delta[i] = d2;
/*fprintf (stderr, "delta before reset %e\n", delta[i]); */
reset();
d2 = delta[i];
/*fprintf (stderr, "delta after reset %e\n", delta[i]); */
break;
case GAS_MOLES:
if (gas_in == FALSE)
continue;
d2 = (x[i]->moles > 1 ? 1 : 30);
d2 *= d * x[i]->moles;
d2 = (d2 < ineq_tol ? ineq_tol : d2);
//if (d2 < 1e-14)
// d2 = 1e-14;
x[i]->moles += d2;
break;
}
gammas(mu_x);
molalities(TRUE);
mb_sums();
//mb_gases();
//mb_ss();
residuals();
for (j = 0; j < count_unknowns; j++)
{
my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] = -(residual[j] - base[j]) / d2;
if (x[i]->type == MH2O) // DL_pitz
my_array[(size_t)j * (count_unknowns + 1) + (size_t)i] *= mass_water_aq_x;
}
switch (x[i]->type)
{
case MB:
case ALK:
case CB:
case SOLUTION_PHASE_BOUNDARY:
case EXCH:
case SURFACE:
case SURFACE_CB:
case SURFACE_CB1:
case SURFACE_CB2:
case AH2O:
x[i]->master[0]->s->la -= d2;
break;
case MH:
s_eminus->la -= d2;
if (my_array[(size_t)i * (count_unknowns + 1) + (size_t)i] == 0)
{
/*output_msg(sformatf( "Zero diagonal for MH\n")); */
my_array[(size_t)i * (count_unknowns + 1) + (size_t)i] =
under(s_h2->lm) * 2;
}
break;
case PITZER_GAMMA:
x[i]->s->lg -= d2;
break;
case MH2O:
//mass_water_aq_x /= (1 + d);
//x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water;
//break;
//DL_pitz
mass_water_aq_x -= d2;
if (use.Get_surface_in() && dl_type_x == cxxSurface::DONNAN_DL)
mass_water_bulk_x -= d2;
x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water;
break;
case MU:
mu_x -= d2;
//gammas(mu_x);
break;
case PP:
delta[i] = -d2;
reset();
break;
case SS_MOLES:
delta[i] = -d2;
reset();
break;
case GAS_MOLES:
x[i]->moles -= d2;
break;
}
if (use.Get_surface_ptr() != NULL)
{
*use.Get_surface_ptr() = base_surface;
}
if (use.Get_gas_phase_ptr() != NULL)
{
*use.Get_gas_phase_ptr() = base_gas_phase;
for (size_t g = 0; g < base_phases.size(); g++)
{
*phase_ptrs[g] = base_phases[g];
}
}
//gammas(mu_x);
//molalities(TRUE);
//mb_sums();
////mb_gases();
////mb_ss();
//residuals();
}
gammas(mu_x);
molalities(TRUE);
mb_sums();
//mb_gases();
//mb_ss();
residuals();
//for (i = 0; i < count_unknowns; i++)
//{
// //Debugging
// if (fabs(2.0 * (residual[i] - base[i]) / (residual[i] + base[i])) > 1e-2 &&
// fabs(residual[i]) + fabs(base[i]) > 1e-8)
// {
// std::cerr << iterations << ": " << x[i]->description << " " << residual[i] << " " << base[i] << std::endl;
// }
//}
base.clear();
calculating_deriv = FALSE;
return OK;
}
#endif
/* ---------------------------------------------------------------------- */
void Phreeqc::
set_inert_moles(void)
/* ---------------------------------------------------------------------- */
{
int j;
if (use.Get_pp_assemblage_ptr() == NULL) return;
for (j = 0; j < count_unknowns; j++)
{
if (x[j]->type != PP) continue;
//cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[j]->pp_assemblage_comp_name);
cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[j]->pp_assemblage_comp_ptr;
if (comp_ptr->Get_precipitate_only())
{
x[j]->inert_moles = x[j]->moles;
x[j]->moles = 0;
}
}
}
/* ---------------------------------------------------------------------- */
void Phreeqc::
unset_inert_moles()
/* ---------------------------------------------------------------------- */
{
int j;
if (use.Get_pp_assemblage_ptr() == NULL) return;
for (j = 0; j < count_unknowns; j++)
{
if (x[j]->type != PP) continue;
//cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[j]->pp_assemblage_comp_name);
cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[j]->pp_assemblage_comp_ptr;
if (comp_ptr->Get_precipitate_only())
{
x[j]->moles += x[j]->inert_moles;
x[j]->inert_moles = 0;
}
}
}