mirror of
https://git.gfz-potsdam.de/naaice/iphreeqc.git
synced 2025-12-15 16:18:22 +01:00
ce34aa5e Merge commit '8184121c90728d5026fa0485302e4584a559ffd8' 8184121c Squashed 'phreeqcpp/' changes from f5587da..b576c75 git-subtree-dir: src git-subtree-split: ce34aa5e05ddf53f5b3b7ae2240437d31a736f4a
3181 lines
90 KiB
C++
3181 lines
90 KiB
C++
#include "Utils.h"
|
|
#include "Phreeqc.h"
|
|
#include "phqalloc.h"
|
|
|
|
#include <time.h>
|
|
|
|
#include "StorageBin.h"
|
|
#include "Reaction.h"
|
|
#include "cxxKinetics.h"
|
|
#include "Solution.h"
|
|
#include "cxxMix.h"
|
|
#include "PPassemblage.h"
|
|
#include "Surface.h"
|
|
#include "Exchange.h"
|
|
#include "GasPhase.h"
|
|
#include "SSassemblage.h"
|
|
#include "Temperature.h"
|
|
#include "cxxKinetics.h"
|
|
#include <map>
|
|
#include <fstream>
|
|
#include <memory>
|
|
#include "nvector_serial.h" /* definitions of type N_Vector and macro */
|
|
/* NV_Ith_S, prototypes for N_VNew, N_VFree */
|
|
/* These macros are defined in order to write code which exactly matches
|
|
the mathematical problem description given above.
|
|
|
|
Ith(v,i) references the ith component of the vector v, where i is in
|
|
the range [1..NEQ] and NEQ is defined below. The Ith macro is defined
|
|
using the N_VIth macro in nvector.h. N_VIth numbers the components of
|
|
a vector starting from 0.
|
|
|
|
IJth(A,i,j) references the (i,j)th element of the dense matrix A, where
|
|
i and j are in the range [1..NEQ]. The IJth macro is defined using the
|
|
DENSE_ELEM macro in dense.h. DENSE_ELEM numbers rows and columns of a
|
|
dense matrix starting from 0. */
|
|
|
|
#define Ith(v,i) NV_Ith_S(v,i-1) /* Ith numbers components 1..NEQ */
|
|
#define IJth(A,i,j) DENSE_ELEM(A,i-1,j-1) /* IJth numbers rows,cols 1..NEQ */
|
|
|
|
#define MAX_DIVIDE 2
|
|
#define KINETICS_TOL 1e-8;
|
|
|
|
#if defined(PHREEQCI_GUI)
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
#endif
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
calc_kinetic_reaction(cxxKinetics *kinetics_ptr, LDBLE time_step)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Go through kinetic components to
|
|
* determine rates and
|
|
* a list of elements and amounts in
|
|
* the reaction.
|
|
*/
|
|
int j, return_value;
|
|
LDBLE coef;
|
|
char l_command[] = "run";
|
|
class rate *rate_ptr;
|
|
/*
|
|
* Go through list and generate list of elements and
|
|
* coefficient of elements in reaction
|
|
*/
|
|
return_value = OK;
|
|
count_elts = 0;
|
|
paren_count = 0;
|
|
rate_time = time_step;
|
|
|
|
/* t1 = clock(); */
|
|
for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]);
|
|
coef = 0.0;
|
|
/*
|
|
* Send command to basic interpreter
|
|
*/
|
|
rate_ptr = rate_search(kinetics_comp_ptr->Get_rate_name().c_str(), &j);
|
|
if (rate_ptr == NULL)
|
|
{
|
|
error_string = sformatf( "Rate not found for %s",
|
|
kinetics_comp_ptr->Get_rate_name().c_str());
|
|
error_msg(error_string, STOP);
|
|
}
|
|
else
|
|
{
|
|
rate_moles = NAN;
|
|
rate_m = kinetics_comp_ptr->Get_m();
|
|
rate_m0 = kinetics_comp_ptr->Get_m0();
|
|
rate_p = kinetics_comp_ptr->Get_d_params();
|
|
count_rate_p = (int) kinetics_comp_ptr->Get_d_params().size();
|
|
if (rate_ptr->new_def == TRUE)
|
|
{
|
|
if (basic_compile
|
|
(rates[j].commands.c_str(), &rates[j].linebase, &rates[j].varbase,
|
|
&rates[j].loopbase) != 0)
|
|
{
|
|
error_string = sformatf( "Fatal Basic error in rate %s.",
|
|
kinetics_comp_ptr->Get_rate_name().c_str());
|
|
error_msg(error_string, STOP);
|
|
}
|
|
|
|
rate_ptr->new_def = FALSE;
|
|
}
|
|
if (basic_run
|
|
(l_command, rates[j].linebase, rates[j].varbase,
|
|
rates[j].loopbase) != 0)
|
|
{
|
|
error_string = sformatf( "Fatal Basic error in rate %s.",
|
|
kinetics_comp_ptr->Get_rate_name().c_str());
|
|
error_msg(error_string, STOP);
|
|
}
|
|
if (std::isnan(rate_moles))
|
|
{
|
|
error_string = sformatf( "Moles of reaction not SAVEed for %s.",
|
|
kinetics_comp_ptr->Get_rate_name().c_str());
|
|
error_msg(error_string, STOP);
|
|
}
|
|
else
|
|
{
|
|
|
|
coef = rate_moles;
|
|
}
|
|
}
|
|
/*
|
|
* Accumulate moles of reaction for component
|
|
*/
|
|
kinetics_comp_ptr->Set_moles(kinetics_comp_ptr->Get_moles() + coef);
|
|
if (coef == 0.0)
|
|
continue;
|
|
}
|
|
/* t2=clock();
|
|
printf("secs in reac %e, t2 %e\n", t2-t1, t1);
|
|
*/
|
|
return (return_value);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
calc_final_kinetic_reaction(cxxKinetics *kinetics_ptr)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Go through kinetic components to
|
|
* using extrapolated values, which were
|
|
* stored in moles in run_kinetics
|
|
*/
|
|
LDBLE coef;
|
|
class phase *phase_ptr;
|
|
class master *master_ptr;
|
|
int count= 0;
|
|
/*
|
|
* Go through list and generate list of elements and
|
|
* coefficient of elements in reaction
|
|
*/
|
|
RESTART: // if limiting rates, jump to here
|
|
count++;
|
|
kinetics_ptr->Get_totals().clear();
|
|
for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++)
|
|
{
|
|
count_elts = 0;
|
|
paren_count = 0;
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]);
|
|
if (kinetics_comp_ptr->Get_moles() > m_temp[i])
|
|
{
|
|
kinetics_comp_ptr->Set_moles(m_temp[i]);
|
|
kinetics_comp_ptr->Set_m(0);
|
|
}
|
|
coef = kinetics_comp_ptr->Get_moles();
|
|
if (coef == 0.0)
|
|
continue;
|
|
/*
|
|
* Reactant is a pure phase, copy formula into token
|
|
*/
|
|
cxxNameDouble::iterator it = kinetics_comp_ptr->Get_namecoef().begin();
|
|
for ( ; it != kinetics_comp_ptr->Get_namecoef().end(); it++)
|
|
{
|
|
std::string name = it->first;
|
|
LDBLE coef1 = it->second;
|
|
phase_ptr = NULL;
|
|
int k;
|
|
phase_ptr = phase_bsearch(name.c_str(), &k, FALSE);
|
|
if (phase_ptr != NULL)
|
|
{
|
|
add_elt_list(phase_ptr->next_elt,
|
|
coef *coef1);
|
|
}
|
|
else
|
|
{
|
|
const char* ptr = name.c_str();
|
|
if (get_elts_in_species(&ptr, coef * coef1) == ERROR)
|
|
{
|
|
error_string = sformatf("Error in -formula: %s", name.c_str());
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
}
|
|
}
|
|
if (use.Get_exchange_ptr() != NULL
|
|
&& use.Get_exchange_ptr()->Get_related_rate())
|
|
{
|
|
cxxExchange * exchange_ptr = use.Get_exchange_ptr();
|
|
for(size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++)
|
|
{
|
|
std::string name(exchange_ptr->Get_exchange_comps()[j].Get_rate_name());
|
|
if (name.size() > 0)
|
|
{
|
|
if (strcmp_nocase
|
|
(kinetics_comp_ptr->Get_rate_name().c_str(),
|
|
name.c_str()) == 0)
|
|
{
|
|
/* found kinetics component */
|
|
std::string formula = exchange_ptr->Get_exchange_comps()[j].Get_formula().c_str();
|
|
const char* ptr = formula.c_str();
|
|
if (get_elts_in_species(&ptr, -coef*exchange_ptr->Get_exchange_comps()[j].Get_phase_proportion()) == ERROR)
|
|
{
|
|
error_string = sformatf("Error in -formula: %s", formula.c_str());
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
if (use.Get_surface_ptr() != NULL && use.Get_surface_ptr()->Get_related_rate())
|
|
{
|
|
for (size_t j = 0; j < use.Get_surface_ptr()->Get_surface_comps().size(); j++)
|
|
{
|
|
cxxSurfaceComp *surface_comp_ptr = &(use.Get_surface_ptr()->Get_surface_comps()[j]);
|
|
if (surface_comp_ptr->Get_rate_name().size() > 0)
|
|
{
|
|
if (strcmp_nocase
|
|
(kinetics_comp_ptr->Get_rate_name().c_str(),
|
|
surface_comp_ptr->Get_rate_name().c_str()) == 0)
|
|
{
|
|
/* found kinetics component */
|
|
std::string temp_formula = surface_comp_ptr->Get_formula().c_str();
|
|
const char* cptr = temp_formula.c_str();
|
|
/* Surface = 0 when m becomes low ...
|
|
*/
|
|
if (0.9 * surface_comp_ptr->Get_phase_proportion() *
|
|
(kinetics_comp_ptr->Get_m()) < MIN_RELATED_SURFACE)
|
|
{
|
|
master_ptr = master_bsearch(surface_comp_ptr->Get_master_element().c_str());
|
|
if (master_ptr != NULL)
|
|
{
|
|
master_ptr->total = 0.0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (get_elts_in_species(&cptr, -coef * surface_comp_ptr->Get_phase_proportion()) == ERROR)
|
|
{
|
|
error_string = sformatf("Error in -formula: %s", temp_formula.c_str());
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
kinetics_comp_ptr->Set_moles_of_reaction(elt_list_NameDouble());
|
|
kinetics_ptr->Get_totals().add_extensive(kinetics_comp_ptr->Get_moles_of_reaction(), 1.0);
|
|
}
|
|
if (count > 2)
|
|
{
|
|
#if !defined(R_SO)
|
|
fprintf(stderr, "Too many limit_rates-.\n");
|
|
#else
|
|
error_msg("Too many limit_rates-.\n");
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (limit_rates(kinetics_ptr))
|
|
goto RESTART;
|
|
}
|
|
if (count > 2)
|
|
{
|
|
#if !defined(R_SO)
|
|
fprintf(stderr, "Too many limit_rates+.\n");
|
|
#else
|
|
error_msg("Too many limit_rates+.\n");
|
|
#endif
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
rk_kinetics(int i, LDBLE kin_time, int use_mix, int nsaver,
|
|
LDBLE step_fraction)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Runge-Kutta-Fehlberg method; 6 evaluations of the derivative
|
|
* give O(h^5) global error and error estimate
|
|
* calc_kinetic_reaction(.., ..) calculates moles of intermediate derivatives;
|
|
* these are calc'd for the whole step h.
|
|
* calc_final_kinetic reaction(..) translates moles to PHREEQC reaction.
|
|
*/
|
|
int k, save_old;
|
|
int l_bad, step_bad, step_ok;
|
|
int n_reactions;
|
|
LDBLE h, h_old, h_sum;
|
|
LDBLE l_error, error_max, safety, moles_max, moles_reduction;
|
|
cxxKinetics *kinetics_ptr;
|
|
int equal_rate, zero_rate;
|
|
|
|
cxxPPassemblage *pp_assemblage_save = NULL;
|
|
cxxSSassemblage *ss_assemblage_save = NULL;
|
|
|
|
LDBLE b31 = 3. / 40., b32 = 9. / 40.,
|
|
b51 = -11. / 54., b53 = -70. / 27., b54 = 35. / 27.,
|
|
b61 = 1631. / 55296., b62 = 175. / 512., b63 = 575. / 13824., b64 =
|
|
44275. / 110592., b65 = 253. / 4096., c1 = 37. / 378., c3 =
|
|
250. / 621., c4 = 125. / 594., c6 = 512. / 1771., dc5 =
|
|
-277. / 14336.;
|
|
LDBLE dc1 = c1 - 2825. / 27648., dc3 = c3 - 18575. / 48384., dc4 =
|
|
c4 - 13525. / 55296., dc6 = c6 - 0.25;
|
|
/*
|
|
* Save kinetics i and solution i, if necessary
|
|
*/
|
|
save_old = -2 - (count_cells * (1 + stag_data.count_stag) + 2);
|
|
Utilities::Rxn_copy(Rxn_kinetics_map, i, save_old);
|
|
if (nsaver != i)
|
|
{
|
|
Utilities::Rxn_copy(Rxn_solution_map, i, save_old);
|
|
}
|
|
|
|
/*
|
|
* Malloc some space
|
|
*/
|
|
kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i);
|
|
if (kinetics_ptr == NULL)
|
|
return (OK);
|
|
n_reactions = (int) kinetics_ptr->Get_kinetics_comps().size();
|
|
rk_moles.resize(6 * (size_t)n_reactions);
|
|
|
|
/*if (use_mix != NOMIX) last_model.force_prep = TRUE; */
|
|
set_and_run_wrapper(i, use_mix, FALSE, i, step_fraction);
|
|
run_reactions_iterations += iterations;
|
|
|
|
saver();
|
|
if (state == TRANSPORT || state == PHAST)
|
|
{
|
|
set_transport(i, NOMIX, TRUE, i);
|
|
}
|
|
else if (state == ADVECTION)
|
|
{
|
|
set_advection(i, NOMIX, TRUE, i);
|
|
}
|
|
else if (state == REACTION)
|
|
{
|
|
set_reaction(i, NOMIX, TRUE);
|
|
}
|
|
kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i);
|
|
|
|
step_bad = step_ok = 0;
|
|
l_bad = FALSE;
|
|
h_sum = 0.;
|
|
h = h_old = kin_time;
|
|
moles_max = 0.1;
|
|
moles_reduction = 1.0;
|
|
safety = 0.7;
|
|
if (kinetics_ptr->Get_rk() < 1)
|
|
kinetics_ptr->Set_rk(1);
|
|
else if (kinetics_ptr->Get_rk() > 3)
|
|
kinetics_ptr->Set_rk(6);
|
|
|
|
if (kinetics_ptr->Get_rk() == 6)
|
|
equal_rate = FALSE;
|
|
else
|
|
equal_rate = TRUE;
|
|
/*
|
|
* if step_divide > 1, initial timestep is divided
|
|
* if < 1, step_divide indicates maximal reaction...
|
|
*/
|
|
if (kinetics_ptr->Get_step_divide() > 1.0)
|
|
{
|
|
h = h_old = kin_time / kinetics_ptr->Get_step_divide();
|
|
equal_rate = FALSE;
|
|
}
|
|
else if (kinetics_ptr->Get_step_divide() < 1.0)
|
|
moles_max = kinetics_ptr->Get_step_divide();
|
|
|
|
rate_sim_time = rate_sim_time_start + h_sum;
|
|
|
|
status(0, NULL);
|
|
while (h_sum < kin_time)
|
|
{
|
|
|
|
if (step_bad > kinetics_ptr->Get_bad_step_max())
|
|
{
|
|
error_string = sformatf(
|
|
"Bad RK steps > %d in cell %d. Please decrease (time)step or increase -bad_step_max.",
|
|
kinetics_ptr->Get_bad_step_max(), cell_no);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
|
|
MOLES_TOO_LARGE:
|
|
if (moles_reduction > 1.0)
|
|
{
|
|
h_old = h;
|
|
h = safety * h / (1.0 + moles_reduction);
|
|
moles_reduction = 1.0;
|
|
equal_rate = FALSE;
|
|
l_bad = TRUE;
|
|
}
|
|
/*
|
|
* find k1
|
|
*/
|
|
if (l_bad == TRUE)
|
|
{
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
rk_moles[j] *= (h / h_old);
|
|
kinetics_comp_ptr->Set_moles(rk_moles[j] * 0.2);
|
|
kinetics_comp_ptr->Set_m(m_temp[j]);
|
|
}
|
|
l_bad = FALSE;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* define pointers for calc_kinetic_, they are lost after saver()...
|
|
*/
|
|
if (state == TRANSPORT || state == PHAST)
|
|
{
|
|
set_transport(i, NOMIX, TRUE, i);
|
|
}
|
|
else if (state == ADVECTION)
|
|
{
|
|
set_advection(i, NOMIX, TRUE, i);
|
|
}
|
|
else if (state == REACTION)
|
|
{
|
|
set_reaction(i, NOMIX, TRUE);
|
|
}
|
|
/*
|
|
* Moles of minerals and solid solutions may change to make positive
|
|
* concentrations. Reactions may take out more than is present in
|
|
* solution.
|
|
*/
|
|
if (use.Get_pp_assemblage_ptr() != NULL)
|
|
{
|
|
cxxPPassemblage * pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, use.Get_pp_assemblage_ptr()->Get_n_user());
|
|
assert(pp_assemblage_ptr);
|
|
pp_assemblage_save = new cxxPPassemblage(*pp_assemblage_ptr);
|
|
}
|
|
if (use.Get_ss_assemblage_ptr() != NULL)
|
|
{
|
|
cxxSSassemblage * ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, use.Get_ss_assemblage_ptr()->Get_n_user());
|
|
assert(ss_assemblage_ptr);
|
|
ss_assemblage_save = new cxxSSassemblage(*ss_assemblage_ptr);
|
|
}
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_moles(0.);
|
|
m_temp[j] = kinetics_comp_ptr->Get_m();
|
|
}
|
|
|
|
rate_sim_time = rate_sim_time_start + h_sum;
|
|
calc_kinetic_reaction(kinetics_ptr, h);
|
|
|
|
/* store k1 in rk_moles ... */
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
if (moles_reduction * moles_max < fabs(kinetics_comp_ptr->Get_moles()))
|
|
{
|
|
moles_reduction = fabs(kinetics_comp_ptr->Get_moles()) / moles_max;
|
|
}
|
|
/* define reaction for calculating k2 ... */
|
|
rk_moles[j] = kinetics_comp_ptr->Get_moles();
|
|
kinetics_comp_ptr->Set_moles(kinetics_comp_ptr->Get_moles() * 0.2);
|
|
}
|
|
if (moles_reduction > 1.0)
|
|
goto MOLES_TOO_LARGE;
|
|
}
|
|
/*
|
|
* Quit rk with rk = 1 and equal rates ...
|
|
*/
|
|
if (kinetics_ptr->Get_rk() == 1 && equal_rate)
|
|
{
|
|
zero_rate = TRUE;
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_moles(rk_moles[j]);
|
|
if (fabs(kinetics_comp_ptr->Get_moles()) > MIN_TOTAL)
|
|
zero_rate = FALSE;
|
|
}
|
|
|
|
if (zero_rate == FALSE)
|
|
{
|
|
calc_final_kinetic_reaction(kinetics_ptr);
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles());
|
|
if (kinetics_comp_ptr->Get_m() < 1.e-30)
|
|
kinetics_comp_ptr->Set_m(0);
|
|
kinetics_comp_ptr->Set_moles(0.);
|
|
}
|
|
if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) ==
|
|
MASS_BALANCE)
|
|
{
|
|
run_reactions_iterations += iterations;
|
|
moles_reduction = 9;
|
|
goto MOLES_TOO_LARGE;
|
|
}
|
|
run_reactions_iterations += iterations;
|
|
calc_kinetic_reaction(kinetics_ptr, h);
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
if (fabs(rk_moles[j] - kinetics_comp_ptr->Get_moles()) >
|
|
kinetics_comp_ptr->Get_tol())
|
|
{
|
|
equal_rate = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (zero_rate || equal_rate)
|
|
{
|
|
/* removing the following line causes different results for
|
|
example 6 distributed with the program */
|
|
saver();
|
|
|
|
/* Free space */
|
|
|
|
if (pp_assemblage_save != NULL)
|
|
{
|
|
delete pp_assemblage_save;
|
|
pp_assemblage_save = NULL;
|
|
}
|
|
if (ss_assemblage_save != NULL)
|
|
{
|
|
delete ss_assemblage_save;
|
|
ss_assemblage_save = NULL;
|
|
}
|
|
goto EQUAL_RATE_OUT;
|
|
}
|
|
else
|
|
{
|
|
kinetics_ptr->Set_rk(3);
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_moles(0.2 * rk_moles[j]);
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Continue with rk ...
|
|
*/
|
|
calc_final_kinetic_reaction(kinetics_ptr);
|
|
if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE)
|
|
{
|
|
run_reactions_iterations += iterations;
|
|
moles_reduction = 9;
|
|
goto MOLES_TOO_LARGE;
|
|
}
|
|
run_reactions_iterations += iterations;
|
|
|
|
/*
|
|
* find k2
|
|
*/
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles());
|
|
kinetics_comp_ptr->Set_moles(0.);
|
|
}
|
|
rate_sim_time = rate_sim_time_start + h_sum + 0.2 * h;
|
|
calc_kinetic_reaction(kinetics_ptr, h);
|
|
|
|
/* Reset to values of last saver() */
|
|
if (pp_assemblage_save != NULL)
|
|
{
|
|
Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save;
|
|
use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user()));
|
|
}
|
|
if (ss_assemblage_save != NULL)
|
|
{
|
|
Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save;
|
|
use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user()));
|
|
}
|
|
|
|
/* store k2 in rk_moles */
|
|
k = n_reactions;
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
if (moles_reduction * moles_max <
|
|
fabs(kinetics_comp_ptr->Get_moles()))
|
|
{
|
|
moles_reduction =
|
|
fabs(kinetics_comp_ptr->Get_moles()) / moles_max;
|
|
}
|
|
/* define reaction for calculating k3 */
|
|
rk_moles[k + j] = kinetics_comp_ptr->Get_moles();
|
|
|
|
kinetics_comp_ptr->Set_moles(b31 * rk_moles[j]
|
|
+ b32 * rk_moles[k + j]);
|
|
/*
|
|
* check for equal_rate ...
|
|
*/
|
|
if (equal_rate
|
|
&& fabs(rk_moles[j] - rk_moles[k + j]) >
|
|
kinetics_comp_ptr->Get_tol())
|
|
{
|
|
equal_rate = FALSE;
|
|
}
|
|
}
|
|
if (moles_reduction > 1.0)
|
|
goto MOLES_TOO_LARGE;
|
|
/*
|
|
* Quit rk with rk = 2 and equal rates ...
|
|
*/
|
|
if (kinetics_ptr->Get_rk() == 2 && equal_rate)
|
|
{
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_moles(
|
|
0.3 * rk_moles[j] + 0.7 * rk_moles[k + j]);
|
|
}
|
|
calc_final_kinetic_reaction(kinetics_ptr);
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles());
|
|
if (kinetics_comp_ptr->Get_m() < 1.e-30)
|
|
kinetics_comp_ptr->Set_m(0);
|
|
kinetics_comp_ptr->Set_moles(0.);
|
|
}
|
|
if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE)
|
|
{
|
|
run_reactions_iterations += iterations;
|
|
moles_reduction = 9;
|
|
goto MOLES_TOO_LARGE;
|
|
}
|
|
run_reactions_iterations += iterations;
|
|
/*
|
|
* Move next calc'n to rk = 1 when initial rate equals final rate ...
|
|
*/
|
|
calc_kinetic_reaction(kinetics_ptr, h);
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
if (fabs(rk_moles[j] - kinetics_comp_ptr->Get_moles()) >
|
|
kinetics_comp_ptr->Get_tol())
|
|
{
|
|
equal_rate = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (equal_rate)
|
|
kinetics_ptr->Set_rk(1);
|
|
|
|
saver();
|
|
|
|
/* Free space */
|
|
|
|
if (pp_assemblage_save != NULL)
|
|
{
|
|
delete pp_assemblage_save;
|
|
pp_assemblage_save = NULL;
|
|
}
|
|
if (ss_assemblage_save != NULL)
|
|
{
|
|
Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save;
|
|
use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user()));
|
|
}
|
|
goto EQUAL_RATE_OUT;
|
|
}
|
|
/*
|
|
* Continue runge_kutta..
|
|
*/
|
|
calc_final_kinetic_reaction(kinetics_ptr);
|
|
if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE)
|
|
{
|
|
run_reactions_iterations += iterations;
|
|
moles_reduction = 9;
|
|
goto MOLES_TOO_LARGE;
|
|
}
|
|
run_reactions_iterations += iterations;
|
|
/*
|
|
* find k3
|
|
*/
|
|
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_m (m_temp[j] - kinetics_comp_ptr->Get_moles());
|
|
kinetics_comp_ptr->Set_moles(0.);
|
|
}
|
|
rate_sim_time = rate_sim_time_start + h_sum + 0.3 * h;
|
|
calc_kinetic_reaction(kinetics_ptr, h);
|
|
|
|
/* Reset to values of last saver() */
|
|
if (pp_assemblage_save != NULL)
|
|
{
|
|
Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save;
|
|
use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user()));
|
|
}
|
|
if (ss_assemblage_save != NULL)
|
|
{
|
|
Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save;
|
|
use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user()));
|
|
}
|
|
|
|
/* store k3 in rk_moles */
|
|
k = 2 * n_reactions;
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
if (moles_reduction * moles_max <
|
|
fabs(kinetics_comp_ptr->Get_moles()))
|
|
{
|
|
moles_reduction =
|
|
fabs(kinetics_comp_ptr->Get_moles()) / moles_max;
|
|
}
|
|
/* define reaction for calculating k4 ... */
|
|
rk_moles[k + j] = kinetics_comp_ptr->Get_moles();
|
|
|
|
kinetics_comp_ptr->Set_moles(0.3 * rk_moles[j]
|
|
- 0.9 * rk_moles[n_reactions + j] + 1.2 * rk_moles[k + j]);
|
|
/*
|
|
* check for equal_rate ...
|
|
*/
|
|
if (equal_rate
|
|
&& fabs(rk_moles[j] - rk_moles[k + j]) >
|
|
kinetics_comp_ptr->Get_tol())
|
|
equal_rate = FALSE;
|
|
}
|
|
if (moles_reduction > 1.0)
|
|
goto MOLES_TOO_LARGE;
|
|
/*
|
|
* Quit rk with rk = 3 and equal rates ...
|
|
*/
|
|
if (kinetics_ptr->Get_rk() == 3 && equal_rate)
|
|
{
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_moles(0.5 * rk_moles[j]
|
|
- 1.5 * rk_moles[n_reactions + j] + 2 * rk_moles[k + j]);
|
|
}
|
|
calc_final_kinetic_reaction(kinetics_ptr);
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles());
|
|
if (kinetics_comp_ptr->Get_m() < 1.e-30)
|
|
kinetics_comp_ptr->Set_m(0.);
|
|
kinetics_comp_ptr->Set_moles(0.);
|
|
}
|
|
|
|
if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE)
|
|
{
|
|
run_reactions_iterations += iterations;
|
|
moles_reduction = 9;
|
|
goto MOLES_TOO_LARGE;
|
|
}
|
|
run_reactions_iterations += iterations;
|
|
/*
|
|
* Move next calc'n to rk = 1 when initial rate equals final rate ...
|
|
*/
|
|
calc_kinetic_reaction(kinetics_ptr, h);
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
if (fabs(rk_moles[j] - kinetics_comp_ptr->Get_moles()) >
|
|
kinetics_comp_ptr->Get_tol())
|
|
{
|
|
equal_rate = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (equal_rate)
|
|
kinetics_ptr->Set_rk(1);
|
|
|
|
saver();
|
|
|
|
/* Free space */
|
|
|
|
if (pp_assemblage_save != NULL)
|
|
{
|
|
delete pp_assemblage_save;
|
|
pp_assemblage_save = NULL;
|
|
}
|
|
if (ss_assemblage_save != NULL)
|
|
{
|
|
delete ss_assemblage_save;
|
|
ss_assemblage_save = NULL;
|
|
}
|
|
goto EQUAL_RATE_OUT;
|
|
}
|
|
/*
|
|
* Continue runge_kutta..
|
|
*/
|
|
|
|
calc_final_kinetic_reaction(kinetics_ptr);
|
|
if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE)
|
|
{
|
|
run_reactions_iterations += iterations;
|
|
moles_reduction = 9;
|
|
goto MOLES_TOO_LARGE;
|
|
}
|
|
run_reactions_iterations += iterations;
|
|
/*
|
|
* find k4
|
|
*/
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles());
|
|
kinetics_comp_ptr->Set_moles(0.);
|
|
}
|
|
rate_sim_time = rate_sim_time_start + h_sum + 0.6 * h;
|
|
calc_kinetic_reaction(kinetics_ptr, h);
|
|
|
|
/* Reset to values of last saver() */
|
|
if (pp_assemblage_save != NULL)
|
|
{
|
|
Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save;
|
|
use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user()));
|
|
}
|
|
if (ss_assemblage_save != NULL)
|
|
{
|
|
Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save;
|
|
use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user()));
|
|
}
|
|
|
|
/* store k4 in rk_moles */
|
|
k = 3 * n_reactions;
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
if (moles_reduction * moles_max <
|
|
fabs(kinetics_comp_ptr->Get_moles()))
|
|
{
|
|
moles_reduction =
|
|
fabs(kinetics_comp_ptr->Get_moles()) / moles_max;
|
|
}
|
|
|
|
/* define reaction for calculating k5 */
|
|
rk_moles[k + j] = kinetics_comp_ptr->Get_moles();
|
|
kinetics_comp_ptr->Set_moles(b51 * rk_moles[j]
|
|
+ 2.5 * rk_moles[(size_t)n_reactions + j]
|
|
+ b53 * rk_moles[2 * (size_t)n_reactions + j]
|
|
+ b54 * rk_moles[(size_t)k + (size_t)j]);
|
|
}
|
|
if (moles_reduction > 1.0)
|
|
goto MOLES_TOO_LARGE;
|
|
calc_final_kinetic_reaction(kinetics_ptr);
|
|
if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE)
|
|
{
|
|
run_reactions_iterations += iterations;
|
|
moles_reduction = 9;
|
|
goto MOLES_TOO_LARGE;
|
|
}
|
|
run_reactions_iterations += iterations;
|
|
/*
|
|
* find k5
|
|
*/
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles());
|
|
kinetics_comp_ptr->Set_moles(0.);
|
|
}
|
|
rate_sim_time = rate_sim_time_start + h_sum + h;
|
|
calc_kinetic_reaction(kinetics_ptr, h);
|
|
|
|
/* Reset to values of last saver() */
|
|
if (pp_assemblage_save != NULL)
|
|
{
|
|
Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save;
|
|
use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user()));
|
|
}
|
|
if (ss_assemblage_save != NULL)
|
|
{
|
|
Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save;
|
|
use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user()));
|
|
}
|
|
|
|
/* store k5 in rk_moles */
|
|
k = 4 * n_reactions;
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
if (moles_reduction * moles_max <
|
|
fabs(kinetics_comp_ptr->Get_moles()))
|
|
{
|
|
moles_reduction =
|
|
fabs(kinetics_comp_ptr->Get_moles()) / moles_max;
|
|
}
|
|
|
|
/* define reaction for calculating k6 */
|
|
rk_moles[k + j] = kinetics_comp_ptr->Get_moles();
|
|
kinetics_comp_ptr->Set_moles(b61 * rk_moles[j]
|
|
+ b62 * rk_moles[(size_t)n_reactions + j]
|
|
+ b63 * rk_moles[2 * (size_t)n_reactions + j]
|
|
+ b64 * rk_moles[3 * (size_t)n_reactions + j]
|
|
+ b65 * rk_moles[(size_t)k + (size_t)j]);
|
|
}
|
|
if (moles_reduction > 1.0)
|
|
goto MOLES_TOO_LARGE;
|
|
calc_final_kinetic_reaction(kinetics_ptr);
|
|
if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE)
|
|
{
|
|
run_reactions_iterations += iterations;
|
|
moles_reduction = 9;
|
|
goto MOLES_TOO_LARGE;
|
|
}
|
|
run_reactions_iterations += iterations;
|
|
/*
|
|
* find k6
|
|
*/
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles());
|
|
kinetics_comp_ptr->Set_moles(0.);
|
|
}
|
|
rate_sim_time = rate_sim_time_start + h_sum + 0.875 * h;
|
|
calc_kinetic_reaction(kinetics_ptr, h);
|
|
|
|
/* Reset to values of last saver() */
|
|
if (pp_assemblage_save != NULL)
|
|
{
|
|
Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save;
|
|
use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user()));
|
|
}
|
|
if (ss_assemblage_save != NULL)
|
|
{
|
|
Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save;
|
|
use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user()));
|
|
}
|
|
|
|
/* store k6 in rk_moles */
|
|
k = 5 * n_reactions;
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
rk_moles[k + j] = kinetics_comp_ptr->Get_moles();
|
|
}
|
|
|
|
/*
|
|
* Evaluate error
|
|
*/
|
|
error_max = 0.;
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
l_error = fabs(dc1 * rk_moles[j]
|
|
+ dc3 * rk_moles[2 * (size_t)n_reactions + (size_t)j]
|
|
+ dc4 * rk_moles[3 * (size_t)n_reactions + (size_t)j]
|
|
+ dc5 * rk_moles[4 * (size_t)n_reactions + (size_t)j]
|
|
+ dc6 * rk_moles[5 * (size_t)n_reactions + (size_t)j]);
|
|
|
|
/* tol is in moles/l */
|
|
l_error /= kinetics_comp_ptr->Get_tol();
|
|
if (l_error > error_max)
|
|
error_max = l_error;
|
|
}
|
|
|
|
/*
|
|
* repeat with smaller step
|
|
*/
|
|
/* printf("timest %g ; error_max %g\n", h, error_max); */
|
|
if (error_max > 1)
|
|
{
|
|
h_old = h;
|
|
if (step_ok == 0)
|
|
h = h * safety / error_max;
|
|
else
|
|
h = h * safety * pow(error_max, (LDBLE) -0.25);
|
|
l_bad = TRUE;
|
|
step_bad++;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* OK, calculate result
|
|
*/
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_moles(c1 * rk_moles[j]
|
|
+ c3 * rk_moles[2 * (size_t)n_reactions + (size_t)j]
|
|
+ c4 * rk_moles[3 * (size_t)n_reactions + (size_t)j]
|
|
+ c6 * rk_moles[5 * (size_t)n_reactions + (size_t)j]);
|
|
}
|
|
calc_final_kinetic_reaction(kinetics_ptr);
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles());
|
|
if (kinetics_comp_ptr->Get_m() < 1.e-30)
|
|
kinetics_comp_ptr->Set_m(0);
|
|
kinetics_comp_ptr->Set_moles(0.);
|
|
}
|
|
|
|
if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE)
|
|
{
|
|
run_reactions_iterations += iterations;
|
|
moles_reduction = 9;
|
|
goto MOLES_TOO_LARGE;
|
|
}
|
|
run_reactions_iterations += iterations;
|
|
/*
|
|
* Move next calc'n to rk = 1 when initial rate equals final rate ...
|
|
*/
|
|
calc_kinetic_reaction(kinetics_ptr, h);
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
if (fabs(rk_moles[j] - kinetics_comp_ptr->Get_moles()) >
|
|
kinetics_comp_ptr->Get_tol())
|
|
{
|
|
equal_rate = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (equal_rate && kinetics_ptr->Get_rk() < 6)
|
|
kinetics_ptr->Set_rk(1);
|
|
|
|
saver();
|
|
|
|
step_ok++;
|
|
h_sum += h;
|
|
/* Free space */
|
|
|
|
if (pp_assemblage_save != NULL)
|
|
{
|
|
delete pp_assemblage_save;
|
|
pp_assemblage_save = NULL;
|
|
}
|
|
if (ss_assemblage_save != NULL)
|
|
{
|
|
delete ss_assemblage_save;
|
|
ss_assemblage_save = NULL;
|
|
}
|
|
/*
|
|
* and increase step size ...
|
|
*/
|
|
if (h_sum < kin_time)
|
|
{
|
|
if (error_max > 0.000577)
|
|
{
|
|
h = h * safety * pow(error_max, (LDBLE) -0.2e0);
|
|
}
|
|
else
|
|
{
|
|
h *= 4;
|
|
}
|
|
if (h > (kin_time - h_sum))
|
|
h = (kin_time - h_sum);
|
|
}
|
|
}
|
|
{
|
|
char str[MAX_LENGTH];
|
|
snprintf(str, sizeof(str), "RK-steps: Bad%4d. OK%5d. Time %3d%%", step_bad,
|
|
step_ok, (int) (100 * h_sum / kin_time));
|
|
status(0, str, true);
|
|
}
|
|
}
|
|
|
|
EQUAL_RATE_OUT:
|
|
|
|
/*
|
|
* Run one more time to get distribution of species
|
|
*/
|
|
if (state >= REACTION || nsaver != i)
|
|
{
|
|
set_and_run_wrapper(i, NOMIX, FALSE, nsaver, 0.);
|
|
run_reactions_iterations += iterations;
|
|
}
|
|
/* saver(); */ /* reset for printing */
|
|
if (use_mix == DISP)
|
|
{
|
|
use.Set_mix_ptr(Utilities::Rxn_find(Dispersion_mix_map, i));
|
|
use.Set_mix_in(true);
|
|
use.Set_n_mix_user(i);
|
|
}
|
|
else if ((use_mix == STAG || use_mix == TRUE) && state == TRANSPORT)
|
|
{
|
|
use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i));
|
|
if (use.Get_mix_ptr() != NULL)
|
|
{
|
|
use.Set_mix_in(true);
|
|
use.Set_n_mix_user(i);
|
|
}
|
|
}
|
|
/*
|
|
* Restore solution i, if necessary
|
|
*/
|
|
if (nsaver != i)
|
|
{
|
|
Utilities::Rxn_copy(Rxn_solution_map, save_old, i);
|
|
}
|
|
rk_moles.clear();
|
|
|
|
rate_sim_time = rate_sim_time_start + kin_time;
|
|
use.Set_kinetics_in(true);
|
|
|
|
/* Free space */
|
|
|
|
if (pp_assemblage_save != NULL)
|
|
{
|
|
delete pp_assemblage_save;
|
|
pp_assemblage_save = NULL;
|
|
}
|
|
if (ss_assemblage_save != NULL)
|
|
{
|
|
delete ss_assemblage_save;
|
|
ss_assemblage_save = NULL;
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
set_and_run_wrapper(int i, int use_mix, int use_kinetics, int nsaver,
|
|
LDBLE step_fraction)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int j, converge, max_try;
|
|
int old_diag, old_itmax;
|
|
LDBLE old_tol, old_min_value, old_step, old_pe, old_pp_column_scale;
|
|
LDBLE small_pe_step, small_step;
|
|
#if (__GNUC__ && (__cplusplus >= 201103L)) || (_MSC_VER >= 1600)
|
|
std::unique_ptr<cxxPPassemblage> pp_assemblage_save=NULL;
|
|
std::unique_ptr<cxxSSassemblage> ss_assemblage_save=NULL;
|
|
std::unique_ptr<cxxKinetics> kinetics_save=NULL;
|
|
#else
|
|
std::auto_ptr<cxxPPassemblage> pp_assemblage_save(NULL);
|
|
std::auto_ptr<cxxSSassemblage> ss_assemblage_save(NULL);
|
|
std::auto_ptr<cxxKinetics> kinetics_save(NULL);
|
|
#endif
|
|
int restart = 0;
|
|
|
|
small_pe_step = 5.;
|
|
small_step = 10.;
|
|
converge = FALSE;
|
|
|
|
old_diag = diagonal_scale;
|
|
old_itmax = itmax;
|
|
old_tol = ineq_tol;
|
|
old_step = step_size;
|
|
old_pe = pe_step_size;
|
|
old_min_value = min_value;
|
|
old_pp_column_scale = pp_column_scale;
|
|
int old_equi_delay = equi_delay;
|
|
|
|
if (state == TRANSPORT || state == PHAST)
|
|
{
|
|
set_transport(i, use_mix, use_kinetics, i);
|
|
}
|
|
else if (state == ADVECTION)
|
|
{
|
|
set_advection(i, use_mix, use_kinetics, i);
|
|
}
|
|
else if (state == REACTION)
|
|
{
|
|
set_reaction(i, use_mix, use_kinetics);
|
|
}
|
|
if (use.Get_pp_assemblage_ptr() != NULL)
|
|
{
|
|
cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr();
|
|
pp_assemblage_save.reset(new cxxPPassemblage(*pp_assemblage_ptr));
|
|
}
|
|
if (use.Get_ss_assemblage_ptr() != NULL)
|
|
{
|
|
cxxSSassemblage * ss_assemblage_ptr = use.Get_ss_assemblage_ptr();
|
|
ss_assemblage_save.reset(new cxxSSassemblage(*ss_assemblage_ptr));
|
|
}
|
|
if (use.Get_kinetics_ptr() != NULL)
|
|
{
|
|
kinetics_save.reset(new cxxKinetics(*use.Get_kinetics_ptr()));
|
|
}
|
|
|
|
if (pitzer_model == TRUE || sit_model == TRUE)
|
|
{
|
|
diagonal_scale = TRUE;
|
|
always_full_pitzer = FALSE;
|
|
max_try = 14;
|
|
}
|
|
else
|
|
{
|
|
max_try = 14;
|
|
}
|
|
max_try = (max_tries < max_try) ? max_tries : max_try;
|
|
/*max_try = 1; */
|
|
|
|
restart:
|
|
for (j = 0; j < max_try; j++)
|
|
{
|
|
if (j == 1)
|
|
{
|
|
/*always_full_pitzer = TRUE;*/
|
|
if (pe_step_size <= small_pe_step && step_size <= small_step)
|
|
continue;
|
|
itmax *= 2;
|
|
step_size = small_step;
|
|
pe_step_size = small_pe_step;
|
|
error_string = sformatf(
|
|
"Trying smaller step size, pe step size %g, %g ... \n",
|
|
(double) step_size, (double) pe_step_size);
|
|
warning_msg(error_string);
|
|
}
|
|
else if (j == 2)
|
|
{
|
|
itmax *= 2;
|
|
ineq_tol /= 10.;
|
|
error_string = sformatf( "Trying reduced tolerance %g ...\n",
|
|
(double) ineq_tol);
|
|
warning_msg(error_string);
|
|
}
|
|
else if (j == 3)
|
|
{
|
|
itmax *= 2;
|
|
ineq_tol *= 10.;
|
|
error_string = sformatf( "Trying increased tolerance %g ...\n",
|
|
(double) ineq_tol);
|
|
warning_msg(error_string);
|
|
}
|
|
else if (j == 4)
|
|
{
|
|
always_full_pitzer = TRUE;
|
|
itmax *= 2;
|
|
if (diagonal_scale == TRUE)
|
|
{
|
|
diagonal_scale = FALSE;
|
|
}
|
|
else
|
|
{
|
|
diagonal_scale = TRUE;
|
|
}
|
|
error_string = sformatf( "Trying diagonal scaling ...\n");
|
|
warning_msg(error_string);
|
|
}
|
|
else if (j == 5)
|
|
{
|
|
itmax *= 2;
|
|
if (diagonal_scale == TRUE)
|
|
{
|
|
diagonal_scale = FALSE;
|
|
}
|
|
else
|
|
{
|
|
diagonal_scale = TRUE;
|
|
}
|
|
ineq_tol /= 10.;
|
|
error_string = sformatf(
|
|
"Trying diagonal scaling and reduced tolerance %g ...\n",
|
|
(double) ineq_tol);
|
|
warning_msg(error_string);
|
|
}
|
|
else if (j == 6)
|
|
{
|
|
if (pitzer_model == TRUE || sit_model == TRUE) continue;
|
|
itmax *= 2;
|
|
pp_column_scale = 1e-10;
|
|
error_string = sformatf(
|
|
"Trying scaling pure_phase columns %g ...\n",
|
|
(double) pp_column_scale);
|
|
warning_msg(error_string);
|
|
}
|
|
else if (j == 7)
|
|
{
|
|
if (pitzer_model == TRUE || sit_model == TRUE) continue;
|
|
itmax *= 2;
|
|
pp_column_scale = 1e-10;
|
|
if (diagonal_scale == TRUE)
|
|
{
|
|
diagonal_scale = FALSE;
|
|
}
|
|
else
|
|
{
|
|
diagonal_scale = TRUE;
|
|
}
|
|
error_string = sformatf(
|
|
"Trying scaling pure_phase columns and diagonal scale %g ...\n",
|
|
(double) pp_column_scale);
|
|
warning_msg(error_string);
|
|
}
|
|
else if (j == 8)
|
|
{
|
|
if (use.Get_pp_assemblage_ptr() == NULL) continue;
|
|
if (equi_delay > 0)
|
|
{
|
|
equi_delay = 0;
|
|
}
|
|
else
|
|
{
|
|
equi_delay = 1;
|
|
}
|
|
error_string = sformatf( "Trying delay removal of equilibrium phases %g ...\n",
|
|
(double) equi_delay);
|
|
warning_msg(error_string);
|
|
}
|
|
|
|
else if (j == 9)
|
|
{
|
|
if (pitzer_model == TRUE || sit_model == TRUE) continue;
|
|
itmax *= 2;
|
|
min_value *= 10;
|
|
error_string = sformatf( "Trying increased scaling %g ...\n",
|
|
(double) min_value);
|
|
warning_msg(error_string);
|
|
}
|
|
else if (j == 10)
|
|
{
|
|
if (pitzer_model == TRUE || sit_model == TRUE) continue;
|
|
aqueous_only = 5;
|
|
error_string = sformatf(
|
|
"Skipping optimize equations for first %d iterations ...\n",
|
|
aqueous_only);
|
|
warning_msg(error_string);
|
|
}
|
|
else if (j == 11)
|
|
{
|
|
if (pitzer_model == TRUE || sit_model == TRUE) continue;
|
|
negative_concentrations = TRUE;
|
|
error_string = sformatf(
|
|
"Adding inequality to make concentrations greater than zero.\n");
|
|
warning_msg(error_string);
|
|
}
|
|
else if (j == 12)
|
|
{
|
|
itmax *= 2;
|
|
ineq_tol /= 100.;
|
|
error_string = sformatf( "Trying reduced tolerance %g ...\n",
|
|
(double) ineq_tol);
|
|
warning_msg(error_string);
|
|
}
|
|
else if (j == 13)
|
|
{
|
|
itmax *= 2;
|
|
ineq_tol /= 1000.;
|
|
error_string = sformatf( "Trying reduced tolerance %g ...\n",
|
|
(double) ineq_tol);
|
|
warning_msg(error_string);
|
|
}
|
|
else if (j == 14 && use.Get_ss_assemblage_in())
|
|
{
|
|
//cxxStorageBin error_bin;
|
|
//Use2cxxStorageBin(error_bin);
|
|
//std::ostringstream error_input;
|
|
//error_bin.dump_raw(error_input, 0);
|
|
//cxxStorageBin reread;
|
|
//std::istringstream is(error_input.str());
|
|
//CParser cp(is);
|
|
//cp.set_echo_stream(CParser::EO_NONE);
|
|
//reread.read_raw(cp);
|
|
//cxxStorageBin2phreeqc(reread);
|
|
//error_string = sformatf("Trying restarting ...\n");
|
|
//warning_msg(error_string);
|
|
//if (restart < 2)
|
|
//{
|
|
// restart++;
|
|
// goto restart;
|
|
//}
|
|
}
|
|
if (j > 0)
|
|
{
|
|
if (pp_assemblage_save.get() != NULL)
|
|
{
|
|
Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save;
|
|
use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user()));
|
|
}
|
|
if (ss_assemblage_save.get() != NULL)
|
|
{
|
|
Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save;
|
|
use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user()));
|
|
}
|
|
if (kinetics_save.get() != NULL)
|
|
{
|
|
Rxn_kinetics_map[kinetics_save->Get_n_user()] = *kinetics_save;
|
|
use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, kinetics_save->Get_n_user()));
|
|
}
|
|
}
|
|
if (j == 14)
|
|
{
|
|
cxxStorageBin error_bin(this->Get_phrq_io());
|
|
Use2cxxStorageBin(error_bin);
|
|
std::ostringstream error_input;
|
|
error_bin.dump_raw(error_input, 0);
|
|
cxxStorageBin reread(this->Get_phrq_io());
|
|
std::istringstream is(error_input.str());
|
|
CParser cp(is);
|
|
cp.set_echo_stream(CParser::EO_NONE);
|
|
cp.set_echo_file(CParser::EO_NONE);
|
|
reread.read_raw(cp);
|
|
cxxStorageBin2phreeqc(reread);
|
|
error_string = sformatf("Trying restarting ...\n");
|
|
warning_msg(error_string);
|
|
|
|
step_size = 1.0 + (small_step - 1.0)/((double) restart + 1.0);
|
|
pe_step_size = 1.0 + (small_pe_step - 1)/ ((double)restart + 1.0);
|
|
if (restart < 2)
|
|
{
|
|
restart++;
|
|
goto restart;
|
|
}
|
|
}
|
|
set_and_run_attempt = j;
|
|
|
|
converge =
|
|
set_and_run(i, use_mix, use_kinetics, nsaver, step_fraction);
|
|
/* reset values */
|
|
diagonal_scale = old_diag;
|
|
itmax = old_itmax;
|
|
ineq_tol = old_tol;
|
|
step_size = old_step;
|
|
pe_step_size = old_pe;
|
|
min_value = old_min_value;
|
|
pp_column_scale = old_pp_column_scale;
|
|
equi_delay = old_equi_delay;
|
|
aqueous_only = 0;
|
|
negative_concentrations = FALSE;
|
|
always_full_pitzer = FALSE;
|
|
if (converge == TRUE)
|
|
{
|
|
break;
|
|
}
|
|
else if (converge == MASS_BALANCE)
|
|
{
|
|
break;
|
|
}
|
|
warning_msg
|
|
("Numerical method failed with this set of convergence parameters.\n");
|
|
}
|
|
if (converge == FALSE && use.Get_kinetics_ptr() != NULL
|
|
&& use.Get_kinetics_ptr()->Get_use_cvode())
|
|
{
|
|
error_string = sformatf(
|
|
"Numerical method failed on all parameter combinations, retrying integration, cell/soln %d", this->solution_number());
|
|
warning_msg(error_string);
|
|
converge = MASS_BALANCE;
|
|
}
|
|
if (converge == FALSE)
|
|
{
|
|
/*
|
|
* write to error.inp what failed to converge.
|
|
*/
|
|
std::ofstream error_input("error.inp");
|
|
cxxStorageBin error_bin(this->Get_phrq_io());
|
|
Use2cxxStorageBin(error_bin);
|
|
error_bin.dump_raw(error_input, 0);
|
|
error_input.close();
|
|
|
|
/* if (state == TRANSPORT && dump_modulus == 0) dump(); */
|
|
check_residuals();
|
|
pr.all = TRUE;
|
|
pr.gas_phase = use.Get_gas_phase_in();
|
|
pr.pp_assemblage = use.Get_pp_assemblage_in();
|
|
pr.ss_assemblage = use.Get_ss_assemblage_in();
|
|
pr.surface = use.Get_surface_in();
|
|
pr.exchange = use.Get_exchange_in();
|
|
pr.totals = TRUE;
|
|
pr.species = TRUE;
|
|
pr.saturation_indices = TRUE;
|
|
pr.irrev = use.Get_reaction_in();
|
|
pr.mix = use.Get_mix_in();
|
|
pr.reaction = TRUE;
|
|
pr.use = TRUE;
|
|
sum_species();
|
|
print_all();
|
|
error_string = sformatf(
|
|
"Numerical method failed on all combinations of convergence parameters, cell/soln/mix %d", this->solution_number());
|
|
error_msg(error_string, STOP);
|
|
}
|
|
numerical_fixed_volume = false;
|
|
if (converge == MASS_BALANCE)
|
|
{
|
|
return (MASS_BALANCE);
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
set_and_run(int i, int use_mix, int use_kinetics, int nsaver,
|
|
LDBLE step_fraction)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* i --user number for soln, reaction, etc.
|
|
* use_mix --integer flag
|
|
state == TRANSPORT: DISP, STAG, NOMIX
|
|
state == REACTION: TRUE, FALSE
|
|
* use_kinetics --true or false flag to calculate kinetic reactions
|
|
* nsaver --user number to store solution
|
|
* step_fraction--fraction of irreversible reaction to add
|
|
*/
|
|
int converge;
|
|
if (state == TRANSPORT || state == PHAST)
|
|
{
|
|
set_transport(i, use_mix, use_kinetics, nsaver);
|
|
}
|
|
else if (state == ADVECTION)
|
|
{
|
|
set_advection(i, use_mix, use_kinetics, nsaver);
|
|
}
|
|
else if (state == REACTION)
|
|
{
|
|
set_reaction(i, use_mix, use_kinetics);
|
|
}
|
|
cell = i;
|
|
/*
|
|
* Take step
|
|
*/
|
|
if (state >= REACTION)
|
|
{
|
|
if (step(step_fraction) == MASS_BALANCE)
|
|
{
|
|
return (MASS_BALANCE);
|
|
}
|
|
/*
|
|
* Always use solution, exchange, and surface -1
|
|
*/
|
|
use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, -1));
|
|
/* new */
|
|
if (use.Get_exchange_ptr() != NULL)
|
|
{
|
|
use.Set_exchange_ptr(Utilities::Rxn_find(Rxn_exchange_map, -1));
|
|
}
|
|
if (use.Get_surface_ptr() != NULL)
|
|
{
|
|
use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, -1));
|
|
}
|
|
|
|
/*
|
|
* Adjust the total pressure to the gas pressure
|
|
*/
|
|
if (use.Get_gas_phase_ptr() != NULL)
|
|
{
|
|
cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr();
|
|
if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE)
|
|
{
|
|
/*
|
|
* Fixed-pressure Gas phase and solution will react
|
|
* Change total pressure of current simulation to pressure
|
|
* of gas phase
|
|
*/
|
|
patm_x = gas_phase_ptr->Get_total_p();
|
|
}
|
|
/* fixed volume gas phase is handled in calc_gas_pressures */
|
|
|
|
}
|
|
}
|
|
/* end new */
|
|
if (use.Get_surface_ptr() != NULL)
|
|
{
|
|
dl_type_x = use.Get_surface_ptr()->Get_dl_type();
|
|
}
|
|
if (use.Get_surface_ptr() != NULL && dl_type_x != cxxSurface::NO_DL)
|
|
{
|
|
converge = surface_model();
|
|
}
|
|
else
|
|
{
|
|
prep();
|
|
k_temp(use.Get_solution_ptr()->Get_tc(), use.Get_solution_ptr()->Get_patm());
|
|
set(FALSE);
|
|
converge = model();
|
|
}
|
|
sum_species();
|
|
viscos = viscosity(NULL);
|
|
use.Get_solution_ptr()->Set_viscosity(viscos);
|
|
use.Get_solution_ptr()->Set_viscos_0(viscos_0);
|
|
if (use.Get_surface_ptr() != NULL && dl_type_x != cxxSurface::NO_DL && use.Get_surface_ptr()->Get_calc_viscosity())
|
|
use.Get_surface_ptr()->Set_DDL_viscosity(viscosity(use.Get_surface_ptr()));
|
|
return (converge);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
set_transport(int i, int use_mix, int use_kinetics, int nsaver)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* i --user number for soln, reaction, etc.
|
|
* use_mix --integer flag
|
|
state == TRANSPORT: DISP, STAG, NOMIX, MIX_BS
|
|
state == REACTION: TRUE, FALSE
|
|
* use_kinetics --true or false flag to calculate kinetic reactions
|
|
* nsaver --user number to store solution
|
|
*/
|
|
cell = i;
|
|
reaction_step = 1;
|
|
/*
|
|
* Find mixture or solution
|
|
*/
|
|
|
|
use.Set_mix_ptr(NULL);
|
|
use.Set_mix_in(false);
|
|
if (use_mix == DISP)
|
|
{
|
|
use.Set_mix_ptr(Utilities::Rxn_find(Dispersion_mix_map, i));
|
|
use.Set_mix_in(true);
|
|
use.Set_n_mix_user(i);
|
|
use.Set_n_mix_user_orig(i);
|
|
}
|
|
else if ((use_mix == STAG && multi_Dflag != TRUE) || use_mix == MIX_BS)
|
|
{
|
|
use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i));
|
|
if (use.Get_mix_ptr() != NULL)
|
|
{
|
|
use.Set_mix_in(true);
|
|
use.Set_n_mix_user(i);
|
|
use.Set_n_mix_user_orig(i);
|
|
}
|
|
else
|
|
{
|
|
use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i));
|
|
if (use.Get_solution_ptr() == NULL)
|
|
{
|
|
error_string = sformatf( "Solution %d not found, while searching mix structure for solution %d.",
|
|
i, use.Get_n_solution_user());
|
|
error_msg(error_string, STOP);
|
|
}
|
|
use.Set_n_solution_user(i);
|
|
use.Set_solution_in(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i));
|
|
if (use.Get_solution_ptr() == NULL)
|
|
{
|
|
error_string = sformatf( "Solution %d not found, while searching mix structure for solution %d.",
|
|
i, use.Get_n_solution_user());
|
|
error_msg(error_string, STOP);
|
|
}
|
|
use.Set_n_solution_user(i);
|
|
use.Set_solution_in(true);
|
|
}
|
|
save.solution = TRUE;
|
|
save.n_solution_user = nsaver;
|
|
save.n_solution_user_end = nsaver;
|
|
/*
|
|
* Find pure phase assemblage
|
|
*/
|
|
use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, i));
|
|
if (use.Get_pp_assemblage_ptr() != NULL)
|
|
{
|
|
use.Set_pp_assemblage_in(true);
|
|
use.Set_n_pp_assemblage_user(i);
|
|
save.pp_assemblage = TRUE;
|
|
save.n_pp_assemblage_user = i;
|
|
save.n_pp_assemblage_user_end = i;
|
|
}
|
|
else
|
|
{
|
|
use.Set_pp_assemblage_in(false);
|
|
save.pp_assemblage = FALSE;
|
|
}
|
|
/*
|
|
* Find irreversible reaction
|
|
*/
|
|
use.Set_reaction_ptr(Utilities::Rxn_find(Rxn_reaction_map, i));
|
|
if (use.Get_reaction_ptr() != NULL)
|
|
{
|
|
use.Set_reaction_in(true);
|
|
use.Set_n_reaction_user(i);
|
|
}
|
|
else
|
|
{
|
|
use.Set_reaction_in(false);
|
|
}
|
|
/*
|
|
* Find exchange
|
|
*/
|
|
use.Set_exchange_ptr(Utilities::Rxn_find(Rxn_exchange_map, i));
|
|
if (use.Get_exchange_ptr() != NULL)
|
|
{
|
|
use.Set_exchange_in(true);
|
|
use.Set_n_exchange_user(i);
|
|
save.exchange = TRUE;
|
|
save.n_exchange_user = i;
|
|
save.n_exchange_user_end = i;
|
|
}
|
|
else
|
|
{
|
|
use.Set_exchange_in(false);
|
|
save.exchange = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Find surface
|
|
*/
|
|
use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, i));
|
|
if (use.Get_surface_ptr() != NULL)
|
|
{
|
|
use.Set_surface_in(true);
|
|
use.Set_n_surface_user(i);
|
|
save.surface = TRUE;
|
|
save.n_surface_user = i;
|
|
save.n_surface_user_end = i;
|
|
}
|
|
else
|
|
{
|
|
use.Set_surface_in(false);
|
|
save.surface = FALSE;
|
|
dl_type_x = cxxSurface::NO_DL;
|
|
}
|
|
/*
|
|
* Find temperature; temp retardation is done in step
|
|
*/
|
|
use.Set_temperature_ptr(Utilities::Rxn_find(Rxn_temperature_map, i));
|
|
if (use.Get_temperature_ptr() != NULL)
|
|
{
|
|
use.Set_temperature_in(true);
|
|
use.Set_n_temperature_user(i);
|
|
}
|
|
else
|
|
{
|
|
use.Set_temperature_in(false);
|
|
}
|
|
/*
|
|
* Find pressure
|
|
*/
|
|
use.Set_pressure_ptr(Utilities::Rxn_find(Rxn_pressure_map, i));
|
|
if (use.Get_pressure_ptr() != NULL)
|
|
{
|
|
use.Set_pressure_in(true);
|
|
use.Set_n_pressure_user(i);
|
|
}
|
|
else
|
|
{
|
|
use.Set_pressure_in(false);
|
|
}
|
|
/*
|
|
* Find gas
|
|
*/
|
|
use.Set_gas_phase_ptr(Utilities::Rxn_find(Rxn_gas_phase_map, i));
|
|
if (use.Get_gas_phase_ptr() != NULL)
|
|
{
|
|
use.Set_gas_phase_in(true);
|
|
use.Set_n_gas_phase_user(i);
|
|
save.gas_phase = TRUE;
|
|
save.n_gas_phase_user = i;
|
|
save.n_gas_phase_user_end = i;
|
|
}
|
|
else
|
|
{
|
|
use.Set_gas_phase_in(false);
|
|
save.gas_phase = FALSE;
|
|
}
|
|
/*
|
|
* Find ss_assemblage
|
|
*/
|
|
use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, i));
|
|
if (use.Get_ss_assemblage_ptr() != NULL)
|
|
{
|
|
use.Set_ss_assemblage_in(true);
|
|
use.Set_n_ss_assemblage_user(i);
|
|
save.ss_assemblage = TRUE;
|
|
save.n_ss_assemblage_user = i;
|
|
save.n_ss_assemblage_user_end = i;
|
|
}
|
|
else
|
|
{
|
|
use.Set_ss_assemblage_in(false);
|
|
save.ss_assemblage = FALSE;
|
|
}
|
|
/*
|
|
* Find kinetics
|
|
*/
|
|
use.Set_kinetics_ptr(NULL);
|
|
use.Set_kinetics_in(false);
|
|
save.kinetics = FALSE;
|
|
if (use_kinetics == TRUE)
|
|
{
|
|
use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, i));
|
|
if (use.Get_kinetics_ptr() != NULL)
|
|
{
|
|
use.Set_n_kinetics_user(i);
|
|
use.Set_kinetics_in(true);
|
|
save.kinetics = TRUE;
|
|
save.n_kinetics_user = i;
|
|
save.n_kinetics_user_end = i;
|
|
}
|
|
}
|
|
/*
|
|
if (use.irrev_ptr != NULL && use.Get_kinetics_ptr() != NULL)
|
|
{
|
|
warning_msg("Should not use REACTION in same simulation with KINETICS.");
|
|
}
|
|
*/
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
set_reaction(int i, int use_mix, int use_kinetics)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* i --user number for soln, reaction, etc.
|
|
* use_mix --integer flag
|
|
state == TRANSPORT: DISP, STAG, NOMIX
|
|
state == REACTION: TRUE, FALSE
|
|
* use_kinetics --true or false flag to calculate kinetic reactions
|
|
*/
|
|
/*
|
|
* Find mixture or solution
|
|
*/
|
|
use.Set_mix_ptr(NULL);
|
|
use.Set_solution_ptr(NULL);
|
|
if (use_mix == TRUE && use.Get_mix_in() == TRUE)
|
|
{
|
|
use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i));
|
|
if (use.Get_mix_ptr() == NULL)
|
|
{
|
|
error_string = sformatf( "MIX %d not found.", i);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i));
|
|
if (use.Get_solution_ptr() == NULL)
|
|
{
|
|
error_string = sformatf( "Solution %d not found.", i);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
}
|
|
/*
|
|
* Find pure phase assemblage
|
|
*/
|
|
if (use.Get_pp_assemblage_in() == TRUE)
|
|
{
|
|
use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, i));
|
|
if (use.Get_pp_assemblage_ptr() == NULL)
|
|
{
|
|
error_string = sformatf( "PP_ASSEMBLAGE %d not found.", i);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Find irreversible reaction
|
|
*/
|
|
if (use.Get_reaction_in() == TRUE)
|
|
{
|
|
use.Set_reaction_ptr(Utilities::Rxn_find(Rxn_reaction_map, i));
|
|
if (use.Get_reaction_ptr() == NULL)
|
|
{
|
|
error_string = sformatf( "REACTION %d not found.", i);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
}
|
|
/*
|
|
* Find exchange
|
|
*/
|
|
if (use.Get_exchange_in() == TRUE)
|
|
{
|
|
use.Set_exchange_ptr(Utilities::Rxn_find(Rxn_exchange_map, i));
|
|
if (use.Get_exchange_ptr() == NULL)
|
|
{
|
|
error_string = sformatf( "EXCHANGE %d not found.", i);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
}
|
|
/*
|
|
* Find surface
|
|
*/
|
|
//if (use.Get_surface_in() && use.Get_kinetics_in() && use.Get_kinetics_ptr() && !use.Get_kinetics_ptr()->Get_use_cvode() && reaction_step > 1)
|
|
//{
|
|
// // use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, i));
|
|
// // appt: we may come here with zero kinetic reaction, but surface may have to keep DONNAN_DL
|
|
//}
|
|
//else
|
|
// dl_type_x = cxxSurface::NO_DL;
|
|
if (use.Get_surface_in() == TRUE)
|
|
{
|
|
use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, i));
|
|
if (use.Get_surface_ptr() == NULL)
|
|
{
|
|
error_string = sformatf( "SURFACE %d not found.", i);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
}
|
|
/*
|
|
* Find temperature; temp retardation is done in step
|
|
*/
|
|
if (use.Get_temperature_in() == TRUE)
|
|
{
|
|
use.Set_temperature_ptr(Utilities::Rxn_find(Rxn_temperature_map, i));
|
|
if (use.Get_temperature_ptr() == NULL)
|
|
{
|
|
error_string = sformatf( "TEMPERATURE %d not found.", i);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
}
|
|
/*
|
|
* Find pressure
|
|
*/
|
|
if (use.Get_pressure_in() == TRUE)
|
|
{
|
|
use.Set_pressure_ptr(Utilities::Rxn_find(Rxn_pressure_map, i));
|
|
if (use.Get_pressure_ptr() == NULL)
|
|
{
|
|
error_string = sformatf( "PRESSURE %d not found.", i);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
}
|
|
/*
|
|
* Find gas
|
|
*/
|
|
if (use.Get_gas_phase_in() == TRUE)
|
|
{
|
|
use.Set_gas_phase_ptr(Utilities::Rxn_find(Rxn_gas_phase_map, i));
|
|
if (use.Get_gas_phase_ptr() == NULL)
|
|
{
|
|
error_string = sformatf( "GAS_PHASE %d not found.", i);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
}
|
|
/*
|
|
* Find ss_assemblage
|
|
*/
|
|
if (use.Get_ss_assemblage_in() == TRUE)
|
|
{
|
|
use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, i));
|
|
if (use.Get_ss_assemblage_ptr() == NULL)
|
|
{
|
|
error_string = sformatf( "Solid-solution Assemblage %d not found.",
|
|
i);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
}
|
|
/*
|
|
* Find kinetics
|
|
*/
|
|
if (use_kinetics == TRUE && use.Get_kinetics_in() == TRUE)
|
|
{
|
|
use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, i));
|
|
if (use.Get_kinetics_ptr() == NULL)
|
|
{
|
|
error_string = sformatf( "KINETICS %d not found.", i);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
use.Set_kinetics_ptr(NULL);
|
|
}
|
|
/*
|
|
if (use.irrev_ptr != NULL && use.Get_kinetics_ptr() != NULL)
|
|
{
|
|
warning_msg("Should not use REACTION in same simulation with KINETICS.");
|
|
}
|
|
*/
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
run_reactions(int i, LDBLE kin_time, int use_mix, LDBLE step_fraction)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Kinetics calculations
|
|
* Rates and moles of each reaction are calculated in calc_kinetic_reaction
|
|
* Total number of moles in reaction is stored in kinetics[i].totals
|
|
*/
|
|
//int increase_tol = 0; // appt
|
|
int converge, m_iter;
|
|
int pr_all_save;
|
|
int nsaver;
|
|
cxxKinetics *kinetics_ptr;
|
|
cxxPPassemblage *pp_assemblage_ptr;
|
|
cxxSSassemblage *ss_assemblage_ptr;
|
|
cxxUse use_save;
|
|
int save_old, n_reactions /*, nok, nbad */ ;
|
|
|
|
/* CVODE definitions */
|
|
realtype ropt[OPT_SIZE], reltol, t, tout, tout1, sum_t;
|
|
long int iopt[OPT_SIZE];
|
|
int flag;
|
|
/*
|
|
* Set nsaver
|
|
*/
|
|
run_reactions_iterations = 0;
|
|
overall_iterations = 0;
|
|
kin_time_x = kin_time;
|
|
rate_kin_time = kin_time;
|
|
nsaver = i;
|
|
if (state == TRANSPORT || state == PHAST)
|
|
{
|
|
if (use_mix == DISP)
|
|
{
|
|
nsaver = -2;
|
|
}
|
|
else if (use_mix == STAG)
|
|
{
|
|
nsaver = -2 - i;
|
|
}
|
|
}
|
|
if (state == ADVECTION)
|
|
{
|
|
nsaver = -2;
|
|
}
|
|
/*
|
|
* Check that reaction exists for this cell ..
|
|
*/
|
|
kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i);
|
|
if (kin_time <= 0 ||
|
|
(kinetics_ptr && kinetics_ptr->Get_kinetics_comps().size() == 0) ||
|
|
(state == REACTION && use.Get_kinetics_in() == FALSE) ||
|
|
(state == TRANSPORT && kinetics_ptr == NULL) ||
|
|
(state == PHAST && kinetics_ptr == NULL) ||
|
|
(state == ADVECTION && kinetics_ptr == NULL))
|
|
{
|
|
converge =
|
|
set_and_run_wrapper(i, use_mix, FALSE, nsaver, step_fraction);
|
|
if (converge == MASS_BALANCE)
|
|
{
|
|
error_string = sformatf("Negative concentration in solution %d. Stopping calculation.", cell_no);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
run_reactions_iterations += iterations;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Save moles of kinetic reactants for printout...
|
|
*/
|
|
size_t count_comps = kinetics_ptr->Get_kinetics_comps().size();
|
|
m_temp.resize(count_comps);
|
|
m_original.resize(count_comps);
|
|
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
m_original[j] = kinetics_comp_ptr->Get_m();
|
|
m_temp[j] = kinetics_comp_ptr->Get_m();
|
|
}
|
|
/*
|
|
* Start the loop for timestepping ...
|
|
* Use either Runge-Kutta-Fehlberg, or final result extrapolation
|
|
*/
|
|
pr_all_save = pr.all;
|
|
pr.all = FALSE;
|
|
/*
|
|
* This condition makes output equal for incremental_reactions TRUE/FALSE...
|
|
* (if (incremental_reactions == FALSE || reaction_step == 1)
|
|
*/
|
|
store_get_equi_reactants(i, FALSE);
|
|
if (!kinetics_ptr->Get_use_cvode())
|
|
{
|
|
/* in case dispersivity is not wanted..
|
|
if (multi_Dflag)
|
|
rk_kinetics(i, kin_time, NOMIX, nsaver, step_fraction);
|
|
else
|
|
*/
|
|
rk_kinetics(i, kin_time, use_mix, nsaver, step_fraction);
|
|
}
|
|
else
|
|
{
|
|
save_old = -2 - (count_cells * (1 + stag_data.count_stag) + 2);
|
|
if (nsaver != i)
|
|
{
|
|
Utilities::Rxn_copy(Rxn_solution_map, i, save_old);
|
|
}
|
|
for (int j = 0; j < OPT_SIZE; j++)
|
|
{
|
|
iopt[j] = 0;
|
|
ropt[j] = 0;
|
|
}
|
|
/*
|
|
* Do mix first
|
|
*/
|
|
kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i);
|
|
n_reactions = (int) kinetics_ptr->Get_kinetics_comps().size();
|
|
cvode_n_user = i;
|
|
cvode_kinetics_ptr = (void *) kinetics_ptr;
|
|
cvode_n_reactions = n_reactions;
|
|
cvode_rate_sim_time_start = rate_sim_time_start;
|
|
cvode_rate_sim_time = rate_sim_time;
|
|
|
|
if (multi_Dflag)
|
|
converge = set_and_run_wrapper(i, NOMIX, FALSE, i, step_fraction);
|
|
else
|
|
converge = set_and_run_wrapper(i, use_mix, FALSE, i, step_fraction);
|
|
if (converge == MASS_BALANCE)
|
|
{
|
|
error_string = sformatf("Negative concentration in solution %d. Stopping calculation.", cell_no);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
saver();
|
|
pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, i);
|
|
ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, i);
|
|
if (pp_assemblage_ptr != NULL)
|
|
{
|
|
cvode_pp_assemblage_save = new cxxPPassemblage(*pp_assemblage_ptr);
|
|
}
|
|
if (ss_assemblage_ptr != NULL)
|
|
{
|
|
cvode_ss_assemblage_save = new cxxSSassemblage(*ss_assemblage_ptr);
|
|
}
|
|
/* allocate space for CVODE */
|
|
kinetics_machEnv = M_EnvInit_Serial(n_reactions);
|
|
kinetics_machEnv->phreeqc_ptr = this;
|
|
kinetics_y = N_VNew(n_reactions, kinetics_machEnv); /* Allocate y, abstol vectors */
|
|
if (kinetics_y == NULL)
|
|
malloc_error();
|
|
cvode_last_good_y = N_VNew(n_reactions, kinetics_machEnv); /* Allocate y, abstol vectors */
|
|
if (cvode_last_good_y == NULL)
|
|
malloc_error();
|
|
cvode_prev_good_y = N_VNew(n_reactions, kinetics_machEnv); /* Allocate y, abstol vectors */
|
|
if (cvode_prev_good_y == NULL)
|
|
malloc_error();
|
|
kinetics_abstol = N_VNew(n_reactions, kinetics_machEnv);
|
|
if (kinetics_abstol == NULL)
|
|
malloc_error();
|
|
for (int j = 0; j < n_reactions; j++)
|
|
{
|
|
Ith(cvode_last_good_y, j + 1) = 0.0;
|
|
Ith(cvode_prev_good_y, j + 1) = 0.0;
|
|
Ith(kinetics_abstol, j + 1) = 0.0;
|
|
}
|
|
/*
|
|
* Set y to 0.0
|
|
*/
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_moles(0.0);
|
|
Ith(kinetics_y, j + 1) = 0.0;
|
|
Ith(kinetics_abstol, j + 1) = kinetics_comp_ptr->Get_tol();
|
|
/*Ith(abstol,j+1) = 1e-8; */
|
|
/* m_temp[j] = kinetics_ptr->comps[j].m; */
|
|
}
|
|
reltol = 0.0;
|
|
|
|
/* Call CVodeMalloc to initialize CVODE:
|
|
|
|
NEQ is the problem size = number of equations
|
|
f is the user's right hand side function in y'=f(t,y)
|
|
T0 is the initial time
|
|
y is the initial dependent variable vector
|
|
BDF specifies the Backward Differentiation Formula
|
|
NEWTON specifies a Newton iteration
|
|
SV specifies scalar relative and vector absolute tolerances
|
|
&reltol is a pointer to the scalar relative tolerance
|
|
abstol is the absolute tolerance vector
|
|
FALSE indicates there are no optional inputs in iopt and ropt
|
|
iopt is an array used to communicate optional integer input and output
|
|
ropt is an array used to communicate optional real input and output
|
|
|
|
A pointer to CVODE problem memory is returned and stored in cvode_mem. */
|
|
/* Don`t know what this does */
|
|
/*
|
|
iopt[SLDET] = TRUE;
|
|
cvode_mem = CVodeMalloc(n_reactions, f, 0.0, y, BDF, NEWTON, SV, &reltol, abstol, NULL, NULL, TRUE, iopt, ropt, machEnv);
|
|
cvode_mem = CVodeMalloc(n_reactions, f, 0.0, y, ADAMS, FUNCTIONAL, SV, &reltol, abstol, NULL, NULL, FALSE, iopt, ropt, machEnv);
|
|
iopt[MXSTEP] is maximum number of steps that CVODE tries.
|
|
*/
|
|
//iopt[SLDET] = TRUE; // appt
|
|
iopt[MXSTEP] = kinetics_ptr->Get_cvode_steps();
|
|
iopt[MAXORD] = kinetics_ptr->Get_cvode_order();
|
|
kinetics_cvode_mem =
|
|
CVodeMalloc(n_reactions, f, 0.0, kinetics_y, BDF, NEWTON, SV,
|
|
&reltol, kinetics_abstol, this, NULL, TRUE, iopt,
|
|
ropt, kinetics_machEnv);
|
|
if (kinetics_cvode_mem == NULL)
|
|
malloc_error();
|
|
|
|
/* Call CVDense to specify the CVODE dense linear solver with the
|
|
user-supplied Jacobian routine Jac. */
|
|
flag = CVDense(kinetics_cvode_mem, Jac, this);
|
|
if (flag != SUCCESS)
|
|
{
|
|
error_msg("CVDense failed.", STOP);
|
|
}
|
|
t = 0;
|
|
tout = kin_time;
|
|
/*ropt[HMAX] = tout/10.; */
|
|
/*ropt[HMIN] = 1e-17; */
|
|
use_save = use;
|
|
flag = CVode(kinetics_cvode_mem, tout, kinetics_y, &t, NORMAL);
|
|
rate_sim_time = rate_sim_time_start + t;
|
|
/*
|
|
printf("At t = %0.4e y =%14.6e %14.6e %14.6e\n",
|
|
t, Ith(y,1), Ith(y,2), Ith(y,3));
|
|
*/
|
|
m_iter = 0;
|
|
sum_t = 0;
|
|
RESTART:
|
|
while (flag != SUCCESS)
|
|
{
|
|
sum_t += cvode_last_good_time;
|
|
{
|
|
error_string = sformatf("CV_ODE: Time: %8.2e s. Delta t: %8.2e s. Calls: %d.", (double)(sum_t), (double) cvode_last_good_time, m_iter);
|
|
status(0, error_string, true);
|
|
}
|
|
//if (state != TRANSPORT)
|
|
//{
|
|
// error_string = sformatf(
|
|
// "CVode incomplete at cvode_steps %d. Cell: %d. Time: %8.2e s. Cvode calls: %d, continuing...\n",
|
|
// (int)iopt[NST], cell_no, (double)sum_t, m_iter + 1);
|
|
// warning_msg(error_string);
|
|
//}
|
|
#ifdef DEBUG_KINETICS
|
|
if (m_iter > 5)
|
|
dump_kinetics_stderr(cell_no);
|
|
#endif
|
|
|
|
//if (m_iter > 0.5 * kinetics_ptr->Get_bad_step_max() &&
|
|
// (cvode_last_good_time < 1e-6 || cvode_last_good_time < 1e-6 * tout)) // appt
|
|
//{
|
|
// if (increase_tol < 3)
|
|
// {
|
|
// increase_tol += 1;
|
|
// for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
// {
|
|
// cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
// LDBLE tr = kinetics_comp_ptr->Get_tol() * 10.0;
|
|
// kinetics_comp_ptr->Set_tol(tr);
|
|
// tr += 0;
|
|
// }
|
|
// }
|
|
//}
|
|
cvode_last_good_time = 0;
|
|
if (++m_iter >= kinetics_ptr->Get_bad_step_max())
|
|
{
|
|
m_temp.clear();
|
|
m_original.clear();
|
|
error_string = sformatf(
|
|
"CVode is at maximum calls: %d. Cell: %d. Time: %8.2e s\nERROR: Please increase the maximum calls with -bad_step_max.",
|
|
m_iter, cell_no, (double)sum_t);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
tout1 = tout - sum_t;
|
|
t = 0;
|
|
N_VScale(1.0, cvode_last_good_y, kinetics_y);
|
|
for (int j = 0; j < OPT_SIZE; j++)
|
|
{
|
|
iopt[j] = 0;
|
|
ropt[j] = 0;
|
|
}
|
|
CVodeFree(kinetics_cvode_mem); /* Free the CVODE problem memory */
|
|
iopt[MXSTEP] = kinetics_ptr->Get_cvode_steps();
|
|
iopt[MAXORD] = kinetics_ptr->Get_cvode_order();
|
|
kinetics_cvode_mem =
|
|
CVodeMalloc(n_reactions, f, 0.0, kinetics_y, BDF, NEWTON,
|
|
SV, &reltol, kinetics_abstol, this, NULL,
|
|
TRUE, iopt, ropt, kinetics_machEnv);
|
|
if (kinetics_cvode_mem == NULL)
|
|
malloc_error();
|
|
|
|
/* Call CVDense to specify the CVODE dense linear solver with the
|
|
user-supplied Jacobian routine Jac. */
|
|
flag = CVDense(kinetics_cvode_mem, Jac, this);
|
|
if (flag != SUCCESS)
|
|
{
|
|
error_msg("CVDense failed.", STOP);
|
|
}
|
|
flag =
|
|
CVode(kinetics_cvode_mem, tout1, kinetics_y, &t, NORMAL);
|
|
/*
|
|
error_string = sformatf( "CVode failed, flag=%d.\n", flag);
|
|
error_msg(error_string, STOP);
|
|
*/
|
|
}
|
|
/*
|
|
odeint(&ystart[-1], n_reactions, 0, kin_time, kinetics_ptr->comps[0].tol, kin_time/kinetics_ptr->step_divide, 0.0, &nok, &nbad, i, nsaver );
|
|
*/
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_moles(Ith(kinetics_y, j + 1));
|
|
kinetics_comp_ptr->Set_m(m_original[j] - kinetics_comp_ptr->Get_moles());
|
|
if (kinetics_comp_ptr->Get_m() < 0)
|
|
{
|
|
kinetics_comp_ptr->Set_moles(m_original[j]);
|
|
kinetics_comp_ptr->Set_m(0.0);
|
|
}
|
|
}
|
|
if (use.Get_pp_assemblage_ptr() != NULL)
|
|
{
|
|
Rxn_pp_assemblage_map[cvode_pp_assemblage_save->Get_n_user()] = *cvode_pp_assemblage_save;
|
|
use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, cvode_pp_assemblage_save->Get_n_user()));
|
|
}
|
|
if (use.Get_ss_assemblage_ptr() != NULL)
|
|
{
|
|
Rxn_ss_assemblage_map[cvode_ss_assemblage_save->Get_n_user()] = *cvode_ss_assemblage_save;
|
|
use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, cvode_ss_assemblage_save->Get_n_user()));
|
|
}
|
|
calc_final_kinetic_reaction(kinetics_ptr);
|
|
if (set_and_run_wrapper(i, NOMIX, TRUE, nsaver, 0) ==
|
|
MASS_BALANCE)
|
|
{
|
|
/*error_msg("FAIL 2 after successful integration in CVode", CONTINUE); */
|
|
warning_msg("FAIL 2 after successful integration in CVode");
|
|
flag = -1;
|
|
goto RESTART;
|
|
}
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_m(m_original[j] - kinetics_comp_ptr->Get_moles());
|
|
}
|
|
/*
|
|
* Restore solution i, if necessary
|
|
*/
|
|
if (nsaver != i)
|
|
{
|
|
Utilities::Rxn_copy(Rxn_solution_map, save_old, i);
|
|
}
|
|
free_cvode();
|
|
use.Set_mix_in(use_save.Get_mix_in());
|
|
use.Set_mix_ptr(use_save.Get_mix_ptr());
|
|
|
|
error_string = sformatf("CV_ODE: Final Delta t: %8.2e s. Calls: %d. ", (double)cvode_last_good_time, m_iter);
|
|
status(0, error_string, true);
|
|
|
|
//status(0, NULL);
|
|
}
|
|
|
|
rate_sim_time = rate_sim_time_start + kin_time;
|
|
store_get_equi_reactants(i, TRUE);
|
|
pr.all = pr_all_save;
|
|
|
|
kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i);
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_moles(m_original[j] - kinetics_comp_ptr->Get_m());
|
|
/* if (kinetics_ptr->comps[j].moles < 1.e-15) kinetics_ptr->comps[j].moles = 0.0;
|
|
*/ }
|
|
m_temp.clear();
|
|
m_original.clear();
|
|
}
|
|
iterations = run_reactions_iterations;
|
|
if (cvode_pp_assemblage_save != NULL)
|
|
{
|
|
delete cvode_pp_assemblage_save;
|
|
cvode_pp_assemblage_save = NULL;
|
|
}
|
|
if (cvode_ss_assemblage_save != NULL)
|
|
{
|
|
delete cvode_ss_assemblage_save;
|
|
cvode_ss_assemblage_save = NULL;
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
free_cvode(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
if (kinetics_y != NULL)
|
|
N_VFree(kinetics_y); /* Free vector */
|
|
kinetics_y = NULL;
|
|
if (cvode_last_good_y != NULL)
|
|
N_VFree(cvode_last_good_y); /* Free vector */
|
|
cvode_last_good_y = NULL;
|
|
if (cvode_prev_good_y != NULL)
|
|
N_VFree(cvode_prev_good_y); /* Free vector */
|
|
cvode_prev_good_y = NULL;
|
|
if (kinetics_abstol != NULL)
|
|
N_VFree(kinetics_abstol); /* Free vector */
|
|
kinetics_abstol = NULL;
|
|
if (kinetics_cvode_mem != NULL)
|
|
CVodeFree(kinetics_cvode_mem); /* Free the CVODE problem memory */
|
|
kinetics_cvode_mem = NULL;
|
|
if (kinetics_machEnv != NULL)
|
|
M_EnvFree_Serial(kinetics_machEnv); /* Free the machine environment memory */
|
|
kinetics_machEnv = NULL;
|
|
if (cvode_pp_assemblage_save != NULL)
|
|
{
|
|
delete cvode_pp_assemblage_save;
|
|
cvode_pp_assemblage_save = NULL;
|
|
}
|
|
if (cvode_ss_assemblage_save != NULL)
|
|
{
|
|
delete cvode_ss_assemblage_save;
|
|
cvode_ss_assemblage_save = NULL;
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
set_advection(int i, int use_mix, int use_kinetics, int nsaver)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* i --user number for soln, reaction, etc.
|
|
* use_mix --integer flag
|
|
state == TRANSPORT: DISP, STAG, NOMIX
|
|
state == REACTION: TRUE, FALSE
|
|
state == ADVECTION: TRUE, FALSE
|
|
* use_kinetics --true or false flag to calculate kinetic reactions
|
|
* nsaver --user number to store solution
|
|
*/
|
|
|
|
cell = i;
|
|
reaction_step = 1;
|
|
/*
|
|
* Find mixture or solution
|
|
*/
|
|
|
|
use.Set_mix_ptr(NULL);
|
|
use.Set_mix_in(false);
|
|
use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i));
|
|
if (use_mix == TRUE && use.Get_mix_ptr() != NULL)
|
|
{
|
|
use.Set_mix_in(true);
|
|
use.Set_n_mix_user(i);
|
|
use.Set_n_mix_user_orig(i);
|
|
use.Set_n_solution_user(i);
|
|
}
|
|
else
|
|
{
|
|
use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i));
|
|
if (use.Get_solution_ptr() == NULL)
|
|
{
|
|
error_string = sformatf( "Solution %d not found.",
|
|
use.Get_n_solution_user());
|
|
error_msg(error_string, STOP);
|
|
}
|
|
use.Set_n_solution_user(i);
|
|
use.Set_solution_in(true);
|
|
}
|
|
save.solution = TRUE;
|
|
save.n_solution_user = nsaver;
|
|
save.n_solution_user_end = nsaver;
|
|
/*
|
|
* Find pure phase assemblage
|
|
*/
|
|
use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, i));
|
|
if (use.Get_pp_assemblage_ptr() != NULL)
|
|
{
|
|
use.Set_pp_assemblage_in(true);
|
|
use.Set_n_pp_assemblage_user(i);
|
|
save.pp_assemblage = TRUE;
|
|
save.n_pp_assemblage_user = i;
|
|
save.n_pp_assemblage_user_end = i;
|
|
}
|
|
else
|
|
{
|
|
use.Set_pp_assemblage_in(false);
|
|
save.pp_assemblage = FALSE;
|
|
}
|
|
/*
|
|
* Find irreversible reaction
|
|
*/
|
|
use.Set_reaction_ptr(Utilities::Rxn_find(Rxn_reaction_map, i));
|
|
if (use.Get_reaction_ptr() != NULL)
|
|
{
|
|
use.Set_reaction_in(true);
|
|
use.Set_n_reaction_user(i);
|
|
}
|
|
else
|
|
{
|
|
use.Set_reaction_in(false);
|
|
}
|
|
/*
|
|
* Find exchange
|
|
*/
|
|
use.Set_exchange_ptr(Utilities::Rxn_find(Rxn_exchange_map, i));
|
|
if (use.Get_exchange_ptr() != NULL)
|
|
{
|
|
use.Set_exchange_in(true);
|
|
use.Set_n_exchange_user(i);
|
|
save.exchange = TRUE;
|
|
save.n_exchange_user = i;
|
|
save.n_exchange_user_end = i;
|
|
}
|
|
else
|
|
{
|
|
use.Set_exchange_in(false);
|
|
save.exchange = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Find surface
|
|
*/
|
|
use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, i));
|
|
if (use.Get_surface_ptr() != NULL)
|
|
{
|
|
use.Set_surface_in(true);
|
|
use.Set_n_surface_user(i);
|
|
save.surface = TRUE;
|
|
save.n_surface_user = i;
|
|
save.n_surface_user_end = i;
|
|
}
|
|
else
|
|
{
|
|
use.Set_surface_in(false);
|
|
save.surface = FALSE;
|
|
dl_type_x = cxxSurface::NO_DL;
|
|
}
|
|
/*
|
|
* Find temperature; temp retardation is done in step
|
|
*/
|
|
use.Set_temperature_ptr(Utilities::Rxn_find(Rxn_temperature_map, i));
|
|
if (use.Get_temperature_ptr() != NULL)
|
|
{
|
|
use.Set_temperature_in(true);
|
|
use.Set_n_temperature_user(i);
|
|
}
|
|
else
|
|
{
|
|
use.Set_temperature_in(false);
|
|
}
|
|
/*
|
|
* Find pressure
|
|
*/
|
|
use.Set_pressure_ptr(Utilities::Rxn_find(Rxn_pressure_map, i));
|
|
if (use.Get_pressure_ptr() != NULL)
|
|
{
|
|
use.Set_pressure_in(true);
|
|
use.Set_n_pressure_user(i);
|
|
}
|
|
else
|
|
{
|
|
use.Set_pressure_in(false);
|
|
}
|
|
/*
|
|
* Find gas
|
|
*/
|
|
use.Set_gas_phase_ptr(Utilities::Rxn_find(Rxn_gas_phase_map, i));
|
|
if (use.Get_gas_phase_ptr() != NULL)
|
|
{
|
|
use.Set_gas_phase_in(true);
|
|
use.Set_n_gas_phase_user(i);
|
|
save.gas_phase = TRUE;
|
|
save.n_gas_phase_user = i;
|
|
save.n_gas_phase_user_end = i;
|
|
}
|
|
else
|
|
{
|
|
use.Set_gas_phase_in(false);
|
|
save.gas_phase = FALSE;
|
|
}
|
|
/*
|
|
* Find ss_assemblage
|
|
*/
|
|
use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, i));
|
|
if (use.Get_ss_assemblage_ptr() != NULL)
|
|
{
|
|
use.Set_ss_assemblage_in(true);
|
|
use.Set_n_ss_assemblage_user(i);
|
|
save.ss_assemblage = TRUE;
|
|
save.n_ss_assemblage_user = i;
|
|
save.n_ss_assemblage_user_end = i;
|
|
}
|
|
else
|
|
{
|
|
use.Set_ss_assemblage_in(false);
|
|
save.ss_assemblage = FALSE;
|
|
}
|
|
/*
|
|
* Find kinetics
|
|
*/
|
|
use.Set_kinetics_ptr(NULL);
|
|
use.Set_kinetics_in(false);
|
|
save.kinetics = FALSE;
|
|
if (use_kinetics == TRUE)
|
|
{
|
|
use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, i));
|
|
if (use.Get_kinetics_ptr() != NULL)
|
|
{
|
|
use.Set_n_kinetics_user(i);
|
|
use.Set_kinetics_in(true);
|
|
save.kinetics = TRUE;
|
|
save.n_kinetics_user = i;
|
|
save.n_kinetics_user_end = i;
|
|
}
|
|
}
|
|
/*
|
|
if (use.irrev_ptr != NULL && use.Get_kinetics_ptr() != NULL)
|
|
{
|
|
warning_msg("Should not use REACTION in same simulation with KINETICS.");
|
|
}
|
|
*/
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
store_get_equi_reactants(int l, int kin_end)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int i, k;
|
|
|
|
if (use.Get_pp_assemblage_in() == TRUE)
|
|
{
|
|
use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, l));
|
|
}
|
|
else
|
|
use.Set_pp_assemblage_ptr(NULL);
|
|
cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr();
|
|
if (use.Get_gas_phase_in() == TRUE)
|
|
{
|
|
use.Set_gas_phase_ptr(Utilities::Rxn_find(Rxn_gas_phase_map, l));
|
|
}
|
|
else
|
|
use.Set_gas_phase_ptr(NULL);
|
|
if (use.Get_ss_assemblage_in() == TRUE)
|
|
{
|
|
use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, l));
|
|
}
|
|
else
|
|
use.Set_ss_assemblage_ptr(NULL);
|
|
|
|
if (kin_end == FALSE)
|
|
{
|
|
count_pp = count_ss = count_pg = 0;
|
|
if (use.Get_pp_assemblage_ptr() != NULL)
|
|
count_pp = (int) pp_assemblage_ptr->Get_pp_assemblage_comps().size();
|
|
if (use.Get_gas_phase_ptr() != NULL)
|
|
{
|
|
cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr();
|
|
count_pg = (int) gas_phase_ptr->Get_gas_comps().size();
|
|
}
|
|
if (use.Get_ss_assemblage_ptr() != NULL)
|
|
{
|
|
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];
|
|
count_ss += (int) ss_ptr->Get_ss_comps().size();
|
|
}
|
|
}
|
|
k = count_pp + count_ss + count_pg;
|
|
|
|
if (k == 0)
|
|
return (OK);
|
|
x0_moles.resize(k);
|
|
for (i = 0; i < k; i++) x0_moles[i] = 0.0;
|
|
k = -1;
|
|
if (pp_assemblage_ptr)
|
|
{
|
|
std::map<std::string, cxxPPassemblageComp>::iterator it;
|
|
it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin();
|
|
for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++)
|
|
{
|
|
x0_moles[++k] = it->second.Get_moles();
|
|
}
|
|
}
|
|
|
|
{
|
|
cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr();
|
|
if (gas_phase_ptr)
|
|
{
|
|
for (size_t l = 0; l < gas_phase_ptr->Get_gas_comps().size(); l++)
|
|
{
|
|
x0_moles[++k] += gas_phase_ptr->Get_gas_comps()[l].Get_moles();
|
|
}
|
|
}
|
|
}
|
|
if (count_ss != 0)
|
|
{
|
|
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];
|
|
for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++)
|
|
{
|
|
cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]);
|
|
x0_moles[++k] = comp_ptr->Get_moles();
|
|
}
|
|
/*!!!! also miscibility gap comps ??
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
k = -1;
|
|
if (pp_assemblage_ptr && count_pp > 0)
|
|
{
|
|
std::map<std::string, cxxPPassemblageComp>::iterator it;
|
|
it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin();
|
|
for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++)
|
|
{
|
|
it->second.Set_moles(x0_moles[++k]);
|
|
it->second.Set_delta(0.0);
|
|
}
|
|
}
|
|
|
|
{
|
|
cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr();
|
|
if (gas_phase_ptr && count_pg)
|
|
{
|
|
std::vector<cxxGasComp> temp_comps(gas_phase_ptr->Get_gas_comps());
|
|
for (size_t l = 0; l < temp_comps.size(); l++)
|
|
{
|
|
temp_comps[l].Set_moles(x0_moles[++k]);
|
|
}
|
|
gas_phase_ptr->Set_gas_comps(temp_comps);
|
|
}
|
|
}
|
|
if (count_ss != 0)
|
|
{
|
|
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];
|
|
for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++)
|
|
{
|
|
cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]);
|
|
comp_ptr->Set_initial_moles(x0_moles[++k]);
|
|
}
|
|
/*!!!! also miscibility gap comps ??
|
|
*/
|
|
}
|
|
}
|
|
/*
|
|
* This condition makes output equal for incremental_reactions TRUE/FALSE...
|
|
* if (incremental_reactions == FALSE || reaction_step == count_total_steps)
|
|
*/
|
|
x0_moles.clear();
|
|
}
|
|
return (OK);
|
|
}
|
|
void Phreeqc::
|
|
f(integertype N, realtype t, N_Vector y, N_Vector ydot,
|
|
void *f_data)
|
|
{
|
|
int n_user;
|
|
//LDBLE step_fraction;
|
|
cxxKinetics *kinetics_ptr;
|
|
Phreeqc *pThis = (Phreeqc *) f_data;
|
|
|
|
pThis->cvode_error = FALSE;
|
|
n_user = pThis->cvode_n_user;
|
|
kinetics_ptr = (cxxKinetics *) pThis->cvode_kinetics_ptr;
|
|
//step_fraction = pThis->cvode_step_fraction;
|
|
pThis->rate_sim_time = pThis->cvode_rate_sim_time;
|
|
|
|
for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]);
|
|
/*
|
|
kinetics_ptr->comps[i].moles = y[i + 1];
|
|
kinetics_ptr->comps[i].m = m_original[i] - y[i + 1];
|
|
*/
|
|
kinetics_comp_ptr->Set_moles(Ith(y, i + 1));
|
|
kinetics_comp_ptr->Set_m(pThis->m_original[i] - Ith(y, i + 1));
|
|
if (kinetics_comp_ptr->Get_m() < 0)
|
|
{
|
|
/*
|
|
NOTE: y is not correct if it is greater than m_original
|
|
However, it seems to work to let y wander off, but use
|
|
.moles as the correct integral.
|
|
It does not work to reset Y to m_original, presumably
|
|
because the rational extrapolation gets screwed up.
|
|
*/
|
|
|
|
/*
|
|
Ith(y,i + 1) = m_original[i];
|
|
*/
|
|
//if (kinetics_ptr->Get_use_cvode())
|
|
//{
|
|
// pThis->cvode_error = TRUE;
|
|
// return;
|
|
//}
|
|
kinetics_comp_ptr->Set_moles(pThis->m_original[i]);
|
|
kinetics_comp_ptr->Set_m(0.0);
|
|
}
|
|
}
|
|
pThis->calc_final_kinetic_reaction(kinetics_ptr);
|
|
/* if (set_and_run(n_user, FALSE, TRUE, n_user, step_fraction) == MASS_BALANCE) { */
|
|
if (pThis->use.Get_pp_assemblage_ptr() != NULL)
|
|
{
|
|
pThis->Rxn_pp_assemblage_map[pThis->cvode_pp_assemblage_save->Get_n_user()] = *pThis->cvode_pp_assemblage_save;
|
|
pThis->use.Set_pp_assemblage_ptr(Utilities::Rxn_find(pThis->Rxn_pp_assemblage_map, pThis->cvode_pp_assemblage_save->Get_n_user()));
|
|
}
|
|
if (pThis->use.Get_ss_assemblage_ptr() != NULL)
|
|
{
|
|
pThis->Rxn_ss_assemblage_map[pThis->cvode_ss_assemblage_save->Get_n_user()] = *pThis->cvode_ss_assemblage_save;
|
|
pThis->use.Set_ss_assemblage_ptr(Utilities::Rxn_find(pThis->Rxn_ss_assemblage_map, pThis->cvode_ss_assemblage_save->Get_n_user()));
|
|
}
|
|
|
|
if (pThis->set_and_run_wrapper(n_user, FALSE, TRUE, n_user, 0.0) == MASS_BALANCE)
|
|
{
|
|
pThis->run_reactions_iterations += pThis->iterations;
|
|
pThis->cvode_error = TRUE;
|
|
/*
|
|
error_msg("Mass balance error in f", CONTINUE);
|
|
*/
|
|
return;
|
|
}
|
|
if (pThis->cvode_test == TRUE)
|
|
{
|
|
return;
|
|
}
|
|
pThis->run_reactions_iterations += pThis->iterations;
|
|
for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]);
|
|
kinetics_comp_ptr->Set_moles(0.0);
|
|
}
|
|
pThis->calc_kinetic_reaction(kinetics_ptr, 1.0);
|
|
for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]);
|
|
/*
|
|
dydx[i + 1] = kinetics_ptr->comps[i].moles;
|
|
*/
|
|
Ith(ydot, i + 1) = kinetics_comp_ptr->Get_moles();
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
static void Jac(integertype N, DenseMat J, RhsFn f, void *f_data, realtype t,
|
|
N_Vector y, N_Vector fy, N_Vector ewt, realtype h,
|
|
realtype uround, void *jac_data, long int *nfePtr,
|
|
N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3);
|
|
*/
|
|
void Phreeqc::
|
|
Jac(integertype N, DenseMat J, RhsFn f, void *f_data,
|
|
realtype t, N_Vector y, N_Vector fy, N_Vector ewt,
|
|
realtype h, realtype uround, void *jac_data,
|
|
long int *nfePtr, N_Vector vtemp1, N_Vector vtemp2,
|
|
N_Vector vtemp3)
|
|
{
|
|
int count_cvode_errors;
|
|
int n_reactions, n_user;
|
|
LDBLE del;
|
|
std::vector<double> initial_rates;
|
|
cxxKinetics *kinetics_ptr;
|
|
LDBLE step_fraction;
|
|
|
|
Phreeqc *pThis = (Phreeqc *) f_data;
|
|
|
|
pThis->cvode_error = FALSE;
|
|
n_reactions = pThis->cvode_n_reactions;
|
|
n_user = pThis->cvode_n_user;
|
|
kinetics_ptr = (cxxKinetics *) pThis->cvode_kinetics_ptr;
|
|
step_fraction = pThis->cvode_step_fraction;
|
|
pThis->rate_sim_time = pThis->cvode_rate_sim_time;
|
|
|
|
initial_rates.resize(n_reactions);
|
|
|
|
for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]);
|
|
/*
|
|
kinetics_ptr->comps[i].moles = y[i + 1];
|
|
kinetics_ptr->comps[i].m = m_original[i] - y[i + 1];
|
|
*/
|
|
kinetics_comp_ptr->Set_moles(Ith(y, i + 1));
|
|
kinetics_comp_ptr->Set_m(pThis->m_original[i] - Ith(y, i + 1));
|
|
if (kinetics_comp_ptr->Get_m() < 0)
|
|
{
|
|
/*
|
|
NOTE: y is not correct if it is greater than m_original
|
|
However, it seems to work to let y wander off, but use
|
|
.moles as the correct integral.
|
|
It does not work to reset Y to m_original, presumably
|
|
because the rational extrapolation gets screwed up.
|
|
*/
|
|
|
|
/*
|
|
Ith(y,i + 1) = m_original[i];
|
|
*/
|
|
kinetics_comp_ptr->Set_moles(pThis->m_original[i]);
|
|
kinetics_comp_ptr->Set_m(0.0);
|
|
}
|
|
}
|
|
pThis->calc_final_kinetic_reaction(kinetics_ptr);
|
|
/* if (set_and_run(n_user, FALSE, TRUE, n_user, step_fraction) == MASS_BALANCE) { */
|
|
if (pThis->use.Get_pp_assemblage_ptr() != NULL)
|
|
{
|
|
pThis->Rxn_pp_assemblage_map[pThis->cvode_pp_assemblage_save->Get_n_user()] = *pThis->cvode_pp_assemblage_save;
|
|
pThis->use.Set_pp_assemblage_ptr(Utilities::Rxn_find(pThis->Rxn_pp_assemblage_map, pThis->cvode_pp_assemblage_save->Get_n_user()));
|
|
}
|
|
if (pThis->set_and_run_wrapper(n_user, FALSE, TRUE, n_user, 0.0) == MASS_BALANCE)
|
|
{
|
|
pThis->run_reactions_iterations += pThis->iterations;
|
|
pThis->cvode_error = TRUE;
|
|
/*
|
|
error_msg("Mass balance error in jacobian", CONTINUE);
|
|
*/
|
|
initial_rates.clear();
|
|
return;
|
|
}
|
|
pThis->run_reactions_iterations += pThis->iterations;
|
|
for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]);
|
|
kinetics_comp_ptr->Set_moles(0.0);
|
|
}
|
|
pThis->calc_kinetic_reaction(kinetics_ptr, 1.0);
|
|
for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]);
|
|
initial_rates[i] = kinetics_comp_ptr->Get_moles();
|
|
}
|
|
for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_i_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]);
|
|
/* calculate reaction up to current time */
|
|
del = 1e-12;
|
|
pThis->cvode_error = TRUE;
|
|
count_cvode_errors = 0;
|
|
while (pThis->cvode_error == TRUE)
|
|
{
|
|
del /= 10.;
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_j_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
/*
|
|
kinetics_ptr->comps[j].moles = y[j + 1];
|
|
kinetics_ptr->comps[j].m = m_original[j] - y[j + 1];
|
|
*/
|
|
kinetics_comp_j_ptr->Set_moles(Ith(y, j + 1));
|
|
kinetics_comp_j_ptr->Set_m(pThis->m_original[j] - Ith(y, j + 1));
|
|
if (kinetics_comp_i_ptr->Get_m() < 0)
|
|
{
|
|
/*
|
|
NOTE: y is not correct if it is greater than m_original
|
|
However, it seems to work to let y wander off, but use
|
|
.moles as the correct integral.
|
|
It does not work to reset Y to m_original, presumably
|
|
because the rational extrapolation gets screwed up.
|
|
*/
|
|
|
|
/*
|
|
Ith(y,i + 1) = m_original[i];
|
|
*/
|
|
kinetics_comp_i_ptr->Set_moles(pThis->m_original[i]);
|
|
kinetics_comp_i_ptr->Set_m(0.0);
|
|
}
|
|
}
|
|
|
|
/* Add small amount of ith reaction */
|
|
kinetics_comp_i_ptr->Set_m(kinetics_comp_i_ptr->Get_m() - del);
|
|
if (kinetics_comp_i_ptr->Get_m() < 0)
|
|
{
|
|
kinetics_comp_i_ptr->Set_m(0);
|
|
}
|
|
kinetics_comp_i_ptr->Set_moles(kinetics_comp_i_ptr->Get_moles() + del);
|
|
pThis->calc_final_kinetic_reaction(kinetics_ptr);
|
|
if (pThis->use.Get_pp_assemblage_ptr() != NULL)
|
|
{
|
|
pThis->Rxn_pp_assemblage_map[pThis->cvode_pp_assemblage_save->Get_n_user()] = *pThis->cvode_pp_assemblage_save;
|
|
pThis->use.Set_pp_assemblage_ptr(Utilities::Rxn_find(pThis->Rxn_pp_assemblage_map, pThis->cvode_pp_assemblage_save->Get_n_user()));
|
|
}
|
|
if (pThis->set_and_run_wrapper
|
|
(n_user, FALSE, TRUE, n_user, step_fraction) == MASS_BALANCE)
|
|
{
|
|
count_cvode_errors++;
|
|
pThis->cvode_error = TRUE;
|
|
if (count_cvode_errors > 30)
|
|
{
|
|
initial_rates.clear();
|
|
return;
|
|
}
|
|
pThis->run_reactions_iterations += pThis->iterations;
|
|
continue;
|
|
}
|
|
pThis->cvode_error = FALSE;
|
|
pThis->run_reactions_iterations += pThis->iterations;
|
|
/*kinetics_ptr->comps[i].moles -= del; */
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
kinetics_comp_ptr->Set_moles(0.0);
|
|
}
|
|
pThis->calc_kinetic_reaction(kinetics_ptr, 1.0);
|
|
|
|
/* calculate new rates for df/dy[i] */
|
|
/* dfdx[i + 1] = 0.0; */
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
IJth(J, j + 1, i + 1) =
|
|
(kinetics_comp_ptr->Get_moles() - initial_rates[j]) / del;
|
|
}
|
|
}
|
|
}
|
|
for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]);
|
|
kinetics_comp_ptr->Set_moles(0);
|
|
}
|
|
initial_rates.clear();
|
|
return;
|
|
}
|
|
|
|
void Phreeqc::
|
|
cvode_init(void)
|
|
{
|
|
cvode_kinetics_ptr = NULL;
|
|
cvode_test = 0;
|
|
cvode_error = 0;
|
|
cvode_n_user = -99;
|
|
cvode_n_reactions = -99;
|
|
cvode_step_fraction = 0.0;
|
|
cvode_rate_sim_time = 0.0;
|
|
cvode_rate_sim_time_start = 0.0;
|
|
cvode_last_good_time = 0.0;
|
|
cvode_prev_good_time = 0.0;
|
|
cvode_last_good_y = NULL;
|
|
cvode_prev_good_y = NULL;
|
|
kinetics_machEnv = NULL;
|
|
kinetics_y = kinetics_abstol = NULL;
|
|
kinetics_cvode_mem = NULL;
|
|
cvode_pp_assemblage_save = NULL;
|
|
cvode_ss_assemblage_save = NULL;
|
|
return;
|
|
}
|
|
bool Phreeqc::
|
|
cvode_update_reactants(int i, int nsaver, bool save_it)
|
|
{
|
|
cxxKinetics *kinetics_ptr = use.Get_kinetics_ptr();
|
|
int n_reactions = (int) kinetics_ptr->Get_kinetics_comps().size();
|
|
|
|
for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
|
|
// Adds reaction defined by last_good_y
|
|
kinetics_comp_ptr->Set_moles(Ith(cvode_last_good_y, j + 1));
|
|
// Reduces m
|
|
kinetics_comp_ptr->Set_m(m_original[j] - kinetics_comp_ptr->Get_moles());
|
|
// Don't update until after calc_final_reaction
|
|
//m_original[j] = kinetics_comp_ptr->Get_m();
|
|
//m_temp[j] = kinetics_comp_ptr->Get_m();
|
|
if (kinetics_comp_ptr->Get_m() < 0)
|
|
{
|
|
kinetics_comp_ptr->Set_moles(m_original[j]);
|
|
kinetics_comp_ptr->Set_m(0.0);
|
|
}
|
|
}
|
|
// calculates net reaction
|
|
calc_final_kinetic_reaction(kinetics_ptr);
|
|
if (use.Get_pp_assemblage_ptr() != NULL)
|
|
{
|
|
Rxn_pp_assemblage_map[cvode_pp_assemblage_save->Get_n_user()] = *cvode_pp_assemblage_save;
|
|
use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, cvode_pp_assemblage_save->Get_n_user()));
|
|
}
|
|
if (use.Get_ss_assemblage_ptr() != NULL)
|
|
{
|
|
Rxn_ss_assemblage_map[cvode_ss_assemblage_save->Get_n_user()] = *cvode_ss_assemblage_save;
|
|
use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, cvode_ss_assemblage_save->Get_n_user()));
|
|
}
|
|
// runs previous solution plus net reaction
|
|
if (set_and_run_wrapper(i, NOMIX, TRUE, nsaver, 1.0) == MASS_BALANCE)
|
|
{
|
|
error_msg("CVODE step was bad", STOP);
|
|
return false;
|
|
}
|
|
|
|
// saves result to reactants defined by saver
|
|
if (save_it)
|
|
{
|
|
saver();
|
|
|
|
cxxPPassemblage *pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, nsaver);
|
|
cxxSSassemblage *ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, nsaver);
|
|
if (cvode_pp_assemblage_save != NULL)
|
|
{
|
|
delete cvode_pp_assemblage_save;
|
|
cvode_pp_assemblage_save = new cxxPPassemblage(*pp_assemblage_ptr);
|
|
}
|
|
if (cvode_ss_assemblage_save != NULL)
|
|
{
|
|
delete cvode_ss_assemblage_save;
|
|
cvode_ss_assemblage_save = new cxxSSassemblage(*ss_assemblage_ptr);
|
|
}
|
|
|
|
for (int j = 0; j < n_reactions; j++)
|
|
{
|
|
Ith(cvode_last_good_y, j + 1) = 0.0;
|
|
Ith(cvode_prev_good_y, j + 1) = 0.0;
|
|
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
|
|
m_original[j] = kinetics_comp_ptr->Get_m();
|
|
m_temp[j] = kinetics_comp_ptr->Get_m();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
bool Phreeqc::
|
|
limit_rates(cxxKinetics *kinetics_ptr)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Go through kinetic components to
|
|
* determine rates and
|
|
* a list of elements and amounts in
|
|
* the reaction.
|
|
*/
|
|
|
|
// check if any small concentrations with negative rates
|
|
if (!use_kinetics_limiter)
|
|
{
|
|
return false;
|
|
}
|
|
std::vector<std::string> negative_rate;
|
|
cxxNameDouble::iterator it = kinetics_ptr->Get_totals().begin();
|
|
for ( ; it != kinetics_ptr->Get_totals().end(); it++)
|
|
{
|
|
if (total(it->first.c_str()) < 1e-10 && it->second < -1e-20)
|
|
{
|
|
//if (total(it->first.c_str()) > fabs(it->second))
|
|
// continue;
|
|
negative_rate.push_back(it->first);
|
|
}
|
|
}
|
|
if (negative_rate.size() == 0) return false;
|
|
|
|
for (size_t j = 0; j < negative_rate.size(); j++)
|
|
{
|
|
std::string elt = negative_rate[j];
|
|
LDBLE positive_rates = 0;
|
|
LDBLE negative_rates = 0;
|
|
for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]);
|
|
cxxNameDouble::iterator it = kinetics_comp_ptr->Get_moles_of_reaction().find(elt);
|
|
if (it != kinetics_comp_ptr->Get_moles_of_reaction().end())
|
|
{
|
|
if (it->second >= 0.0)
|
|
{
|
|
positive_rates += it->second;
|
|
}
|
|
else
|
|
{
|
|
negative_rates += it->second;
|
|
}
|
|
}
|
|
}
|
|
|
|
// factor to reduce precipitation to equal dissolution
|
|
LDBLE limiter_fraction = 1.0;
|
|
if (negative_rates < 0.0)
|
|
{
|
|
limiter_fraction = fabs(positive_rates / negative_rates);
|
|
//limiter_fraction = fabs((0.9*total(elt.c_str()) + positive_rates) / negative_rates);
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
|
|
// Now limit precipitation
|
|
for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++)
|
|
{
|
|
cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]);
|
|
cxxNameDouble::iterator it = kinetics_comp_ptr->Get_moles_of_reaction().find(elt);
|
|
if (it != kinetics_comp_ptr->Get_moles_of_reaction().end())
|
|
{
|
|
if (it->second < 0.0)
|
|
{
|
|
kinetics_comp_ptr->Set_moles(kinetics_comp_ptr->Get_moles() * limiter_fraction);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|