mirror of
https://git.gfz-potsdam.de/naaice/iphreeqc.git
synced 2025-12-15 16:18:22 +01:00
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
5844 lines
152 KiB
C++
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;
|
|
}
|
|
}
|
|
}
|