iphreeqc/phreeqcpp/transport.cpp
Darth Vader 1b2eb6ca0d Squashed 'src/' changes from 6e248c34..81f180a0
81f180a0 Fixed bugs in inverse
6d575967 strcpy_safe and strcat_safe
6d98c4e1 trying different header files
72796f15 added <cstring>
e8481607 warnings, strcat, strcpy
988bdee0 Try using goto(s)
9b10ce3f Try updated logical expression
812061be Turn off optimizing on k_temp

git-subtree-dir: src
git-subtree-split: 81f180a069285bcb4d180c860664b4b2a193854d
2023-11-16 05:04:39 +00:00

6191 lines
181 KiB
C++

#include "Utils.h"
#include "Phreeqc.h"
#include "phqalloc.h"
#include "Exchange.h"
#include "GasPhase.h"
#include "PPassemblage.h"
#include "SSassemblage.h"
#include "cxxKinetics.h"
#include "Solution.h"
#include <limits.h>
LDBLE F_Re3 = F_C_MOL / (R_KJ_DEG_MOL * 1e3);
LDBLE tk_x2; // average tk_x of icell and jcell
LDBLE dV_dcell; // difference in Volt among icell and jcell
int find_current;
char token[MAX_LENGTH];
// implicit...
std::set <std::string> dif_spec_names;
std::set <std::string> dif_els_names;
std::map<int, std::map<std::string, double> > neg_moles;
std::map<std::string, double> els;
double *Ct2, *l_tk_x2, **A, **LU, **mixf, **mixf_stag;
int mixf_comp_size = 0;
struct CURRENT_CELLS
{
LDBLE dif, ele, R; // diffusive and electric components, relative cell resistance
} *current_cells;
LDBLE sum_R, sum_Rd; // sum of R, sum of (current_cells[0].dif - current_cells[i].dif) * R
struct V_M // For calculating Vinograd and McBain's zero-charge, diffusive tranfer of individual solutes
{
LDBLE grad, D, z, c, zc, Dz, Dzc;
LDBLE b_ij; // harmonic mean of cell properties, with EDL enrichment
};
struct CT /* summed parts of V_M and mcd transfer in a timestep for all cells, for free + DL water */
{
LDBLE kgw, dl_s, Dz2c, Dz2c_stag, visc1, visc2, J_ij_sum;
LDBLE A_ij_il, Dz2c_il, mixf_il;
int J_ij_count_spec, J_ij_il_count_spec;
struct V_M *v_m, *v_m_il;
class J_ij *J_ij, *J_ij_il;
int count_m_s;
class M_S *m_s;
int v_m_size, J_ij_size, m_s_size;
} *ct = NULL;
std::map<int, std::map<std::string, J_ij_save> > cell_J_ij;
struct MOLES_ADDED /* total moles added to balance negative conc's */
{
char *name;
LDBLE moles;
} *moles_added;
int count_moles_added;
#if defined(PHREEQCI_GUI)
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#endif
/* ---------------------------------------------------------------------- */
int Phreeqc::
transport(void)
/* ---------------------------------------------------------------------- */
{
int i, j, k, n;
int j_imm;
LDBLE b, f, mix_f_m, mix_f_imm;
LDBLE water_m, water_imm;
int first_c, last_c, b_c;
int max_iter;
char token[MAX_LENGTH];
LDBLE kin_time, stagkin_time, kin_time_save;
int punch_boolean = 0;
LDBLE step_fraction;
state = TRANSPORT;
diffc_tr = diffc;
diffc_max = 0.0;
transp_surf = warn_fixed_Surf = warn_MCD_X = 0;
dV_dcell = current_A = 0.0;
current_cells = NULL;
/* mass_water_switch = TRUE; */
/*
* Check existence of solutions
*/
j = -1;
/* check column solutions */
for (i = 1; i <= count_cells; i++)
{
use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i));
if (use.Get_solution_ptr() == NULL)
{
input_error++;
error_string = sformatf(
"Solution %d is needed for transport, but is not defined.", i);
error_msg(error_string, CONTINUE);
}
else
cell_data[i].temp = use.Get_solution_ptr()->Get_tc();
}
if (multi_Dflag)
{
sol_D = (class sol_D *) PHRQ_malloc((size_t) (all_cells)* sizeof(class sol_D));
if (sol_D == NULL)
malloc_error();
for (i = 0; i < all_cells; i++)
{
sol_D[i].count_spec = 0;
sol_D[i].count_exch_spec = 0;
sol_D[i].exch_total = 0;
sol_D[i].x_max = 0;
sol_D[i].viscos_f = 1.0;
sol_D[i].tk_x = 298.15;
sol_D[i].spec = NULL;
sol_D[i].spec_size = 0;
}
//sol_D_dbg = sol_D;
ct = (struct CT *) PHRQ_malloc((size_t) (all_cells)* sizeof(struct CT));
if (ct == NULL)
malloc_error();
for (i = 0; i < all_cells; i++)
{
ct[i].kgw = 1.0;
ct[i].dl_s = 0.0;
ct[i].Dz2c = ct[i].Dz2c_stag = 0.0;
ct[i].visc1 = ct[i].visc2 = 0.0;
ct[i].J_ij_sum = 0.0;
ct[i].A_ij_il = 0.0;
ct[i].Dz2c_il = 0.0;
ct[i].mixf_il = 0.0;
ct[i].J_ij_count_spec = 0;
ct[i].J_ij_il_count_spec = 0;
ct[i].v_m = NULL;
ct[i].v_m_il = NULL;
ct[i].J_ij = NULL;
ct[i].J_ij_il = NULL;
ct[i].m_s = NULL;
ct[i].v_m_size = ct[i].J_ij_size = ct[i].m_s_size = 0;
}
count_moles_added = (int)elements.size();
moles_added = (struct MOLES_ADDED *) PHRQ_malloc((size_t) (count_moles_added)* sizeof(struct MOLES_ADDED));
if (moles_added == NULL)
malloc_error();
for (i = 0; i < count_moles_added; i++)
{
moles_added[i].name = NULL;
moles_added[i].moles = 0;
}
}
try
{
/* check solution 0 */
use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, 0));
if (!use.Get_solution_ptr())
{
if (ishift == 1)
{
input_error++;
error_string = sformatf(
"Solution 0 is needed for transport, but is not defined.");
error_msg(error_string, CONTINUE);
}
else
Utilities::Rxn_copy(Rxn_solution_map, 1, 0);
}
else
{
if ((cell_data[0].potV = use.Get_solution_ptr()->Get_potV()))
dV_dcell = 1;
}
/* check solution count_cells */
use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, count_cells + 1));
if (!use.Get_solution_ptr())
{
if (ishift == -1)
{
input_error++;
error_string = sformatf(
"Solution %d is needed for transport, but is not defined.",
count_cells + 1);
error_msg(error_string, CONTINUE);
}
else
Utilities::Rxn_copy(Rxn_solution_map, count_cells, count_cells + 1);
}
else
{
if ((cell_data[count_cells + 1].potV = use.Get_solution_ptr()->Get_potV()))
dV_dcell = 1;
}
/*
* Initialize temperature in stagnant cells ...
*/
for (n = 1; n <= stag_data.count_stag; n++)
{
for (i = 1; i <= count_cells; i++)
{
k = i + 1 + n * count_cells;
use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, k));
if (use.Get_solution_ptr() != NULL)
cell_data[k].temp = use.Get_solution_ptr()->Get_tc();
if (n == 1 && implicit && use.Get_solution_ptr() == NULL)
{
input_error++;
error_string = sformatf(
"Stagnant solution %d not found for implicit diffusion with 1 stagnant layer.\n Please define it, or set -implicit false.", k);
error_msg(error_string, CONTINUE);
}
}
}
if (fix_current && !dV_dcell && !implicit)
{
warning_msg("fix_current (A) was defined, but potential in a boundary cell was not.\n\tUsing 1e-8 V in cell 0 for calculating the potentials.");
cell_data[0].potV = 1e-8;
dV_dcell = 1;
}
if (dV_dcell)
{
if (ishift)
{
input_error++;
error_string = sformatf(
"Electro-diffusion cannot be combined with advective transport.");
error_msg(error_string, CONTINUE);
}
if (!multi_Dflag)
{
input_error++;
error_string = sformatf(
"Electrical Field (potential) was defined, but needs -multi_D.");
error_msg(error_string, CONTINUE);
}
else
{
if (bcon_first != 1)
{
bcon_first = 1;
error_string = sformatf(
"Electrical Field (potential) was defined, assuming constant boundary condition for first cell.");
warning_msg(error_string);
}
if (bcon_last != 1)
{
bcon_last = 1;
error_string = sformatf(
"Electrical Field (potential) was defined, assuming constant boundary condition for last cell.");
warning_msg(error_string);
}
current_cells = (struct CURRENT_CELLS *) PHRQ_malloc(
(count_cells + 1) * sizeof(struct CURRENT_CELLS));
if (current_cells == NULL)
malloc_error();
for (int i = 0; i < count_cells + 1; i++)
{
current_cells[i].dif = 0.0;
current_cells[i].ele = 0.0;
current_cells[i].R = 0.0;
}
}
}
if (implicit && interlayer_Dflag)
{
input_error++;
error_string = sformatf(
"Interlayer diffusion cannot be calculated implicitly. Please remove -implicit true.");
error_msg(error_string, CONTINUE);
}
if (implicit && !multi_Dflag)
{
input_error++;
error_string = sformatf(
"Implicit diffusion needs diffusion coefficients for individual species. Please add -multi_d true.");
error_msg(error_string, CONTINUE);
}
if (implicit && !timest)
{
error_string = sformatf(
"Time step not defined for diffusion, using -time_step 1 # seconds");
warning_msg(error_string);
timest = 1;
}
//if (implicit && stag_data.count_stag > 1)
//{
// error_string = sformatf(
// "Sorry, implicit diffusion can handle only 1 stagnant layer for now. Please remove -implicit true, or set -stagnant 1.");
// //error_msg(error_string, CONTINUE);
//}
if (implicit && current_cells == NULL)
{
current_cells = (struct CURRENT_CELLS *) PHRQ_malloc(
(count_cells + 1) * sizeof(struct CURRENT_CELLS));
if (current_cells == NULL)
malloc_error();
for (int i = 0; i < count_cells + 1; i++)
{
current_cells[i].dif = 0.0;
current_cells[i].ele = 0.0;
current_cells[i].R = 0.0;
}
}
/*
* First equilibrate solutions
*/
dup_print("Equilibrating initial solutions", TRUE);
transport_step = 0;
for (i = 0; i <= count_cells + 1; i++)
{
if (!implicit && ((bcon_first == 2 && i == 0) ||
(bcon_last == 2 && i == count_cells + 1)))
continue;
set_initial_moles(i);
cell_no = i;
set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0);
if (use.Get_surface_ptr() != NULL && use.Get_surface_ptr()->Get_transport())
transp_surf = TRUE;
if (transp_surf && !multi_Dflag)
{
error_string = sformatf(
"-multi_d must be defined for surface transport");
error_msg(error_string, CONTINUE);
}
if (multi_Dflag == TRUE)
{
fill_spec(cell_no, 0);
}
print_punch(i, true);
/* if (i > 0 && i <= count_cells)*/
saver();
}
/*
* Also stagnant cells
*/
for (n = 1; n <= stag_data.count_stag; n++)
{
for (i = 1; i <= count_cells; i++)
{
k = i + 1 + n * count_cells;
cell_no = k;
if (Utilities::Rxn_find(Rxn_solution_map, k) != 0)
{
set_initial_moles(k);
set_and_run_wrapper(k, NOMIX, FALSE, k, 0.0);
if (multi_Dflag == TRUE)
{
fill_spec(cell_no, i);
}
print_punch(k, true);
saver();
}
}
}
/*
* Initialize mixing factors, define kinetics times
* for multicomponent diffusion, limit mixing by diffc_max (usually from H+)
*/
if (multi_Dflag)
diffc_tr = diffc_max;
if ((stag_data.exch_f > 0) && (stag_data.count_stag == 1))
{
/* multi_D calc's are OK if all cells have the same amount of water */
if (multi_Dflag == TRUE)
{
snprintf(token, sizeof(token), "multi_D calc's and stagnant: define MIXing factors explicitly, or \n\t give in -multi_D the Dw used for calculating the mobile-immobile exchange factor.");
warning_msg(token);
}
Rxn_mix_map.clear();
}
/*
* mix[] is extended in init_mix(), to accommodate column mix factors
*/
nmix = init_mix();
heat_nmix = init_heat_mix(nmix);
if (nmix < 2)
stagkin_time = timest;
else
stagkin_time = timest / (double)nmix;
if (ishift != 0)
kin_time = timest / (1 + (double)nmix);
else
kin_time = stagkin_time;
kin_time_save = kin_time;
/* Reaction defined for a shift... */
if (!ishift)
{
if (nmix < 2)
step_fraction = 1.0;
else
step_fraction = 1.0 / nmix;
}
else
step_fraction = 1.0 / (1.0 + nmix);
/*
* Set boundary conditions, transport direction
*/
last_model.force_prep = true;
if ((ishift == 0) || (bcon_first == 1) || (bcon_last == 1))
b_c = 1;
else
b_c = 0;
if (ishift >= 0)
{
last_c = count_cells;
first_c = 1;
}
else
{
last_c = 1;
first_c = count_cells;
}
/*
* Define stagnant/mobile mix structure, if not read explicitly.
*
* With count_stag = 1, mix factors are calculated from exchange factor alpha
* (= exch_f), mobile th_m and immobile th_im porosity.
* These variables are read under keyword TRANSPORT, after stagnant, in
* structure stag_data.
* MIX 'cell_no' in input file can be an alternative for the calculation here.
*/
if ((stag_data.exch_f > 0) && (stag_data.count_stag == 1))
{
b = stag_data.th_m / (stag_data.th_m + stag_data.th_im);
f = exp(-stag_data.exch_f * stagkin_time / (b * stag_data.th_im));
mix_f_imm = b - b * f;
mix_f_m = mix_f_imm * stag_data.th_im / stag_data.th_m;
for (j = 1; j <= count_cells; j++)
{
j_imm = j + (1 + count_cells);
if (Utilities::Rxn_find(Rxn_solution_map, j) == NULL)
error_msg
("Could not find mobile cell solution in TRANSPORT.",
STOP);
if (Utilities::Rxn_find(Rxn_solution_map, j_imm) == NULL)
//error_msg
//("Could not find immobile cell solution in TRANSPORT.",
//STOP);
continue;
water_m = Utilities::Rxn_find(Rxn_solution_map, j)->Get_mass_water();
water_imm = Utilities::Rxn_find(Rxn_solution_map, j_imm)->Get_mass_water();
/*
* Define C_m = (1 - mix_f_m) * C_m0 + mix_f_m) * C_im0
*/
{
cxxMix temp_mix;
temp_mix.Set_n_user(j);
temp_mix.Set_n_user_end(j);
temp_mix.Add(j, 1 - mix_f_m);
temp_mix.Add(j_imm, mix_f_m * water_m / water_imm);
Rxn_mix_map[j] = temp_mix;
}
/*
* Define C_im = mix_f_imm * C_m0 + (1 - mix_f_imm) * C_im0, or...
*/
{
cxxMix temp_mix;
temp_mix.Set_n_user(j_imm);
temp_mix.Set_n_user_end(j_imm);
temp_mix.Add(j_imm, 1 - mix_f_imm);
temp_mix.Add(j, mix_f_imm * water_imm / water_m);
Rxn_mix_map[j_imm] = temp_mix;
}
}
if (heat_nmix > 0)
{
/*
* Assumption: D_e used for calculating exch_f in input file equals diffc
*/
f = stag_data.exch_f * (heat_diffc - diffc) / diffc / tempr;
f = exp(-f * stagkin_time / (b * stag_data.th_im));
heat_mix_f_imm = b - b * f;
heat_mix_f_m =
heat_mix_f_imm * stag_data.th_im / stag_data.th_m;
}
}
/*
* Stop if error
*/
if (get_input_errors() > 0)
{
error_msg("Program terminating due to input errors.", STOP);
}
/*
* Now transport
*/
if (implicit)
snprintf(token, sizeof(token), "\nCalculating implicit transport: %d (mobile) cells, %d shifts, %d mixruns, max. mixf = %g.\n\n",
count_cells, count_shifts - transport_start + 1, nmix, max_mixf);
else
snprintf(token, sizeof(token), "\nCalculating transport: %d (mobile) cells, %d shifts, %d mixruns...\n\n",
count_cells, count_shifts - transport_start + 1, nmix);
warning_msg(token);
max_iter = 0;
for (transport_step = transport_start; transport_step <= count_shifts;
transport_step++)
{
/*
* Set initial moles of phases
*/
for (i = 0; i <= count_cells + 1; i++)
{
if (!dV_dcell && !implicit && (i == 0 || i == count_cells + 1))
continue;
set_initial_moles(i);
}
/*
* Also stagnant cells
*/
for (n = 1; n <= stag_data.count_stag; n++)
{
for (i = 1; i <= count_cells; i++)
{
k = i + 1 + n * count_cells;
cell_no = k;
if (Utilities::Rxn_find(Rxn_solution_map, k) != 0)
set_initial_moles(k);
}
}
/*
* Start diffusing if boundary cond = 1, (fixed c, or closed)
*/
if (b_c == 1)
{
/* For half of mixing steps */
for (j = 1; j <= floor((LDBLE)nmix / 2); j++)
{
rate_sim_time_start = ((double)transport_step - 1) *
timest + ((double)j - 1) * kin_time;
rate_sim_time = rate_sim_time_start + kin_time;
mixrun = j;
if (multi_Dflag && j == floor((LDBLE)nmix / 2))
{
//snprintf(token, sizeof(token),
// "Transport step %3d. Multicomponent diffusion run %3d.",
// transport_step, j);
//dup_print(token, FALSE);
}
else if (!multi_Dflag)
{
snprintf(token, sizeof(token), "Transport step %3d. Mixrun %3d.",
transport_step, j);
dup_print(token, FALSE);
}
if (heat_nmix > 0 && !implicit)
{
heat_mix(heat_nmix);
/* equilibrate again ... */
for (i = 1; i <= count_cells; i++)
{
cell_no = i;
set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0);
if (multi_Dflag)
fill_spec(i, i - 1);
saver();
}
}
/* Go through cells */
if (transp_surf)
{
if (disp_surf(stagkin_time) == ERROR)
error_msg("Error in surface transport, stopping.",
STOP);
}
if (implicit)
diffuse_implicit(stagkin_time, stag_data.count_stag);
else if (multi_Dflag)
multi_D(stagkin_time, 1, FALSE);
for (i = 0; i <= count_cells + 1; i++)
{
if (!dV_dcell && (i == 0 || i == count_cells + 1) && !implicit)
{
if (j == nmix && stag_data.count_stag == 0 &&
(cell_data[0].print || cell_data[0].punch ||
cell_data[count_cells + 1].print || cell_data[count_cells + 1].punch))
print_punch(i, false);
continue;
}
if (overall_iterations > max_iter)
max_iter = overall_iterations;
cell_no = i;
if (multi_Dflag)
snprintf(token, sizeof(token),
"Transport step %3d. MCDrun %3d. Cell %3d. (Max. iter %3d)",
transport_step, j, i, max_iter);
else
snprintf(token, sizeof(token),
"Transport step %3d. Mixrun %3d. Cell %3d. (Max. iter %3d)",
transport_step, j, i, max_iter);
status(0, token);
if (i == 0 || i == count_cells + 1)
{
if (dV_dcell)
run_reactions(i, kin_time, MIX_BS, step_fraction); // nsaver = i
else
run_reactions(i, kin_time, NOMIX, step_fraction); // nsaver = i
}
else
{
run_reactions(i, kin_time, DISP, step_fraction); // n_saver = -2
}
if (multi_Dflag)
fill_spec(i, 0);
/* punch and output file */
if (ishift == 0 && j == nmix && (stag_data.count_stag == 0 || (implicit && stag_data.count_stag == 1)))
print_punch(i, true);
if (i > 1)
Utilities::Rxn_copy(Rxn_solution_map, -2, i - 1);
saver();
}
if (!dV_dcell)
Utilities::Rxn_copy(Rxn_solution_map, -2, count_cells);
/* Stagnant zone mixing after completion of each
diffusive/dispersive step ... */
rate_sim_time_start = ((double)transport_step - 1) *
timest + ((double)j - 1) * stagkin_time;
rate_sim_time = rate_sim_time_start + stagkin_time;
if (stag_data.count_stag > 0)
{
if (ishift == 0 && j == nmix)
punch_boolean = TRUE;
else
punch_boolean = FALSE;
for (i = 0; i <= count_cells + 1; i++) // allow for stagnant cell mixing with boundary cells
{
mix_stag(i, stagkin_time, punch_boolean, step_fraction);
}
}
if (ishift == 0 && j == nmix && stag_data.count_stag > 0)
{
for (n = 1; n <= stag_data.count_stag; n++)
{
for (i = 1; i <= count_cells; i++)
{
k = i + 1 + n * count_cells;
if (Utilities::Rxn_find(Rxn_solution_map, k) == NULL)
continue;
print_punch(k, false);
}
}
}
}
}
/*
* Advective transport
*/
if (ishift != 0)
{
snprintf(token, sizeof(token), "Transport step %3d.", transport_step);
dup_print(token, FALSE);
if (b_c == 1)
rate_sim_time_start = ((double)transport_step - 1) *
timest + ((double)j - 1) * kin_time;
else
rate_sim_time_start = ((double)transport_step - 1) * timest;
rate_sim_time = rate_sim_time_start + kin_time;
/* halftime kinetics for resident water in first cell ... */
if (Utilities::Rxn_find(Rxn_kinetics_map, first_c) != NULL && count_cells > 1)
{
cell_no = first_c;
kin_time = kin_time_save / 2;
run_reactions(first_c, kin_time, NOMIX, 0.0);
saver();
kin_time = kin_time_save;
}
/* for each cell in column */
for (i = last_c; i != (first_c - ishift); i -= ishift)
Utilities::Rxn_copy(Rxn_solution_map, i - ishift, i);
/* if boundary_solutions must be flushed by the flux from the column...
if (ishift == 1 && bcon_last == 3)
solution_duplicate (last_c, last_c + 1);
else if (ishift == -1 && bcon_first == 3)
solution_duplicate (last_c, last_c - 1);
*/
if (transp_surf)
{
for (i = last_c + ishift; i != (first_c - ishift);
i -= ishift)
{
if ((ishift == 1 && i == last_c + 1 && bcon_last != 3) ||
(ishift == -1 && i == last_c - 1 && bcon_first != 3))
continue;
cxxSurface * surface_ptr = Utilities::Rxn_find(Rxn_surface_map, i - ishift);
if (surface_ptr == NULL)
{
if ((Utilities::Rxn_find(Rxn_surface_map, i) != NULL) &&
((i == 0 && bcon_first == 3)
|| (i == count_cells + 1 && bcon_last == 3)))
{
Rxn_surface_map.erase(i);
}
continue;
}
if (surface_ptr->Get_transport())
{
cxxSurface * surface_ptr1 = Utilities::Rxn_find(Rxn_surface_map, i);
if (surface_ptr1 == NULL)
{
cxxSurface surf;
surf.Set_n_user(i);
surf.Set_n_user_end(i);
Rxn_surface_map[i] = surf;
}
if (i == first_c)
{
Rxn_surface_map[i] = mobile_surface_copy(surface_ptr, i, false);
}
else
{
Rxn_surface_map[i] = mobile_surface_copy(surface_ptr, i, true);
}
}
}
}
/*
* thermal diffusion when nmix = 0...
*/
if (nmix == 0 && heat_nmix > 0 && !implicit)
{
heat_mix(heat_nmix);
/* equilibrate again ... */
for (i = 1; i <= count_cells; i++)
{
cell_no = i;
set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0);
if (multi_Dflag)
fill_spec(i, i - 1);
saver();
}
}
for (i = 1; i <= count_cells; i++)
{
if (i == first_c && count_cells > 1)
kin_time /= 2;
cell_no = i;
if (multi_Dflag)
snprintf(token, sizeof(token),
"Transport step %3d. MCDrun %3d. Cell %3d. (Max. iter %3d)",
transport_step, 0, i, max_iter);
else
snprintf(token, sizeof(token),
"Transport step %3d. Mixrun %3d. Cell %3d. (Max. iter %3d)",
transport_step, 0, i, max_iter);
status(0, token);
run_reactions(i, kin_time, NOMIX, step_fraction);
if (multi_Dflag == TRUE)
fill_spec(i, i - 1);
if (overall_iterations > max_iter)
max_iter = overall_iterations;
if (nmix == 0 && stag_data.count_stag == 0)
print_punch(i, true);
if (i == first_c && count_cells > 1)
kin_time = kin_time_save;
saver();
/* If nmix is zero, stagnant zone mixing after advective step ... */
if ((nmix == 0) && (stag_data.count_stag > 0))
{
mix_stag(i, stagkin_time, TRUE, step_fraction);
}
}
if (nmix == 0 && stag_data.count_stag > 0)
{
for (n = 1; n <= stag_data.count_stag; n++)
{
for (i = 1; i <= count_cells; i++)
{
k = i + 1 + n * count_cells;
if (Utilities::Rxn_find(Rxn_solution_map, k) == NULL)
continue;
print_punch(k, false);
}
}
}
}
/*
* Further dispersive and diffusive transport
*/
if (b_c != 1)
j = 1;
for (; j <= nmix; j++) // loop on j
{
mixrun = j;
if (multi_Dflag && j == nmix && (transport_step % print_modulus == 0))
{
snprintf(token, sizeof(token),
"Transport step %3d. Multicomponent diffusion run %3d.",
transport_step, j);
dup_print(token, FALSE);
}
else if (!multi_Dflag)
{
snprintf(token, sizeof(token), "Transport step %3d. Mixrun %3d.",
transport_step, j);
dup_print(token, FALSE);
}
rate_sim_time_start = ((double)transport_step - 1) *
timest + ((double)j - 1) * kin_time;
if (ishift != 0)
rate_sim_time_start += kin_time;
rate_sim_time = rate_sim_time_start + kin_time;
if (heat_nmix > 0 && !implicit)
{
heat_mix(heat_nmix);
/* equilibrate again ... */
for (i = 1; i <= count_cells; i++)
{
cell_no = i;
set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0);
if (multi_Dflag)
fill_spec(i, i - 1);
saver();
}
}
if (transp_surf)
{
if (disp_surf(stagkin_time) == ERROR)
error_msg("Error in surface transport, stopping.", STOP);
}
if (implicit)
diffuse_implicit(stagkin_time, stag_data.count_stag);
else if (multi_Dflag)
multi_D(stagkin_time, 1, FALSE);
/* for each cell in column */
for (i = 0; i <= count_cells + 1; i++)
{
if (!dV_dcell && (i == 0 || i == count_cells + 1) && !implicit)
{
if (j == nmix && stag_data.count_stag == 0 &&
(cell_data[0].print || cell_data[0].punch ||
cell_data[count_cells + 1].print || cell_data[count_cells + 1].punch))
print_punch(i, false);
continue;
}
if (overall_iterations > max_iter)
max_iter = overall_iterations;
cell_no = i;
if (multi_Dflag)
snprintf(token, sizeof(token),
"Transport step %3d. MCDrun %3d. Cell %3d. (Max. iter %3d)",
transport_step, j, i, max_iter);
else
snprintf(token, sizeof(token),
"Transport step %3d. Mixrun %3d. Cell %3d. (Max. iter %3d)",
transport_step, j, i, max_iter);
status(0, token);
if (i == 0 || i == count_cells + 1)
{
if (dV_dcell)
run_reactions(i, kin_time, MIX_BS, step_fraction); // nsaver = i
else
run_reactions(i, kin_time, NOMIX, step_fraction); // nsaver = i
}
else
{
run_reactions(i, kin_time, DISP, step_fraction);
}
if (multi_Dflag == TRUE)
fill_spec(i, 0);
if (j == nmix && (stag_data.count_stag == 0 || (implicit && stag_data.count_stag == 1)))
print_punch(i, true);
if (i > 1)
Utilities::Rxn_copy(Rxn_solution_map, -2, i - 1);
saver();
}
if (!dV_dcell)
Utilities::Rxn_copy(Rxn_solution_map, -2, count_cells);
/* Stagnant zone mixing after completion of each diffusive/dispersive step ... */
rate_sim_time_start = ((double)transport_step - 1) *
timest + ((double)j - 1) * stagkin_time;
rate_sim_time = rate_sim_time_start + stagkin_time;
if (stag_data.count_stag > 0)
{
if (j == nmix)
punch_boolean = TRUE;
else
punch_boolean = FALSE;
for (i = 0; i <= count_cells + 1; i++) // allow for stagnant cell mixing with boundary cells
{
mix_stag(i, stagkin_time, punch_boolean, step_fraction);
}
}
if (j == nmix && ((stag_data.count_stag > 0/* && !implicit) || (implicit && stag_data.count_stag == 1*/)))
{
for (n = 1; n <= stag_data.count_stag; n++)
{
for (i = 1; i <= count_cells; i++)
{
k = i + 1 + n * count_cells;
if (Utilities::Rxn_find(Rxn_solution_map, k) == NULL)
continue;
cell_no = k;
print_punch(k, false);
}
}
}
}
if (dump_modulus != 0 && (transport_step % dump_modulus) == 0)
dump();
}
screen_msg("\n");
if (implicit && neg_moles.size())
{
std::map<int, std::map<std::string, double> > ::iterator it1;
std::map<std::string, double> ::iterator it2;
for (i = 0; i <= all_cells; i++)
{
if ((it1 = neg_moles.find(i)) != neg_moles.end() && (els = it1->second).size())
{
for (it2 = els.begin(); it2 != els.end(); it2++)
{
for (int i1 = 0; i1 < count_moles_added; i1++)
{
if (moles_added[i1].name && !strcmp(moles_added[i1].name, it2->first.c_str()))
{
moles_added[i1].moles -= it2->second;
break;
}
else if (!moles_added[i1].moles)
{
moles_added[i1].name = string_duplicate(it2->first.c_str());
moles_added[i1].moles -= it2->second;
break;
}
}
}
}
}
}
if (multi_Dflag && moles_added[0].moles > 0)
{
snprintf(token, sizeof(token),
"\nFor balancing negative concentrations in MCD, added in total to the system:");
warning_msg(token);
for (i = 0; i < count_moles_added; i++)
{
if (!moles_added[i].moles)
break;
snprintf(token, sizeof(token),
"\t %.4e moles %s.",
(double)moles_added[i].moles, moles_added[i].name);
warning_msg(token);
}
}
}
catch (...)
{
transport_cleanup();
throw;
}
transport_cleanup();
initial_total_time += rate_sim_time;
rate_sim_time = 0;
mass_water_switch = FALSE;
return (OK);
}
/* ---------------------------------------------------------------------- */
void Phreeqc::
transport_cleanup(void)
/* ---------------------------------------------------------------------- */
{
int i;
/*
* free mix structures
*/
Dispersion_mix_map.clear();
if ((stag_data.exch_f > 0) && (stag_data.count_stag == 1))
{
Rxn_mix_map.clear();
}
if (heat_nmix > 0)
{
heat_mix_array = (LDBLE *)free_check_null(heat_mix_array);
temp1 = (LDBLE *)free_check_null(temp1);
temp2 = (LDBLE *)free_check_null(temp2);
}
if (multi_Dflag)
{
for (i = 0; i < all_cells; i++)
{
sol_D[i].spec = (class spec *) free_check_null(sol_D[i].spec);
}
sol_D = (class sol_D *) free_check_null(sol_D);
for (int i = 0; i < all_cells; i++)
{
ct[i].v_m = (struct V_M *) free_check_null(ct[i].v_m);
ct[i].v_m_il = (struct V_M *) free_check_null(ct[i].v_m_il);
ct[i].J_ij = (class J_ij *) free_check_null(ct[i].J_ij);
ct[i].J_ij_il = (class J_ij *) free_check_null(ct[i].J_ij_il);
ct[i].m_s = (class M_S *) free_check_null(ct[i].m_s);
}
ct = (struct CT *) free_check_null(ct);
for (int i = 0; i < count_moles_added; i++)
{
moles_added[i].name = (char *)free_check_null(moles_added[i].name);
}
moles_added = (struct MOLES_ADDED *) free_check_null(moles_added);
}
if (implicit)
{
int l_stag = (stag_data.count_stag < 2 ? stag_data.count_stag : 0);
Ct2 = (LDBLE *)free_check_null(Ct2);
l_tk_x2 = (LDBLE *)free_check_null(l_tk_x2);
if (A)
{
for (i = 0; i < count_cells + 2 + l_stag * count_cells; i++)
{
A[i] = (LDBLE *)free_check_null(A[i]);
LU[i] = (LDBLE *)free_check_null(LU[i]);
}
}
if (mixf)
{
for (i = 0; i < count_cells + 2; i++)
{
mixf[i] = (LDBLE *)free_check_null(mixf[i]);
if (l_stag)
mixf_stag[i] = (LDBLE *)free_check_null(mixf_stag[i]);
if (!dV_dcell && !fix_current)
{
cell_data[i].potV = 0.0;
use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i));
use.Get_solution_ptr()->Set_potV(0);
}
}
}
A = (LDBLE **)free_check_null(A);
LU = (LDBLE **)free_check_null(LU);
mixf = (LDBLE **)free_check_null(mixf);
mixf_stag = (LDBLE **)free_check_null(mixf_stag);
dif_spec_names.clear();
mixf_comp_size = 0;
}
current_cells = (struct CURRENT_CELLS *) free_check_null(current_cells);
}
/* ---------------------------------------------------------------------- */
void Phreeqc::
print_punch(int i, boolean active)
/* ---------------------------------------------------------------------- */
{
if ((!(cell_data[i].punch && (transport_step % punch_modulus == 0)) &&
!(cell_data[i].print && (transport_step % print_modulus == 0))) ||
(bcon_first == 2 && i == 0) ||
(bcon_last == 2 && i == count_cells + 1))
return;
if (!active)
run_reactions(i, 0, NOMIX, 0);
cell_no = i;
if (dV_dcell || fix_current)
{
use.Set_n_solution_user(i);
use.Get_solution_ptr()->Set_potV(cell_data[i].potV);
potV_x = cell_data[i].potV;
}
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);
}
if (cell_data[i].punch && (transport_step % punch_modulus == 0))
punch_all();
if (cell_data[i].print && (transport_step % print_modulus == 0))
print_all();
/* maybe sorb a surface component... */
if (change_surf_count > 0)
{
for (int k = 0; k < change_surf_count; k++)
{
if (change_surf[k].cell_no != i)
break;
reformat_surf(change_surf[k].comp_name,
change_surf[k].fraction,
change_surf[k].new_comp_name,
change_surf[k].new_Dw,
change_surf[k].cell_no);
change_surf[k].cell_no = -99;
}
change_surf_count = 0;
save.n_surface_user = save.n_solution_user;
save.n_surface_user_end = save.n_solution_user_end;
}
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
init_mix(void)
/* ---------------------------------------------------------------------- */
{
LDBLE lav, dav = 0.0, mf12, maxmix, corr_disp, diffc_here, mD;
bool warning = false;
int i, l_nmix;
LDBLE *m, *m1;
m = (LDBLE *)PHRQ_malloc((count_cells + 1) * sizeof(LDBLE));
if (m == NULL)
malloc_error();
m1 = (LDBLE *)PHRQ_malloc((count_cells + 1) * sizeof(LDBLE));
if (m1 == NULL)
malloc_error();
for (i = 0; i < count_cells + 1; i++)
{
m[i] = 0;
m1[i] = 0;
}
corr_disp = 1.;
if (correct_disp == TRUE && ishift != 0)
{
if (bcon_first == 3)
corr_disp += 1. / count_cells;
if (bcon_last == 3)
corr_disp += 1. / count_cells;
}
maxmix = 0.0;
if (multi_Dflag)
{
if (dV_dcell)
dV_dcell = (cell_data[count_cells + 1].potV - cell_data[0].potV) / count_cells;
for (i = 1; i <= count_cells; i++)
{
// estimate the number of mixes
if (i < count_cells)
{
lav = (cell_data[i + 1].length + cell_data[i].length) / 2;
mD = diffc_max * timest / (lav * lav);
if (mD > maxmix)
maxmix = mD;
}
if (ishift != 0)
{
/* define dispersive mixing (diffusion is done in multi_D),
with harmonic mean of cell properties (manual eqn 49):
M1 = -a1 * (v1 * timest / 2) * A1 * (c12 - c1) / (h1 / 2)
a1 is dispersivity in cell 1, A1 = V1 / h1, v1 * timest / 2 = h1 / 2.
All cells have equal V in advective transport, delta(c) = M1 / V1.
*/
if (i < count_cells)
{
if (cell_data[i].disp)
dav = cell_data[i].length / cell_data[i].disp;
if (cell_data[i + 1].disp)
dav += cell_data[i + 1].length / cell_data[i + 1].disp;
if (dav)
m1[i] = 2 * corr_disp / dav; /* m1[i] has mixf with higher cell */
}
if (i > 1)
{
if (cell_data[i].disp)
dav = cell_data[i].length / cell_data[i].disp;
if (cell_data[i - 1].disp)
dav += cell_data[i - 1].length / cell_data[i - 1].disp;
if (dav)
m[i] = 2 * corr_disp / dav; /* m[i] has mixf with lower cell */
}
mf12 = m[i] + m1[i];
if (mf12 > maxmix)
maxmix = mf12;
}
}
/*
* Also for boundary cells
*/
if (bcon_first == 1)
{
mD = 2 * diffc_max * timest / (cell_data[1].length * cell_data[1].length);
if (mD > maxmix)
maxmix = mD;
if (ishift != 0)
{
m[1] = 2 * cell_data[1].disp / cell_data[1].length * corr_disp;
mf12 = m[1] + m1[1];
if (mf12 > maxmix)
maxmix = mf12;
}
}
if (bcon_last == 1)
{
mD = 2 * diffc_max * timest / (cell_data[count_cells].length * cell_data[count_cells].length);
if (mD > maxmix)
maxmix = mD;
if (ishift != 0)
{
m1[count_cells] = 2 * cell_data[count_cells].disp / cell_data[count_cells].length * corr_disp;
mf12 = m[count_cells] + m1[count_cells];
if (mf12 > maxmix)
maxmix = mf12;
}
}
/*
* Find number of mixes
*/
if (maxmix == 0)
{
l_nmix = 0;
if (mcd_substeps > 1 && stag_data.count_stag > 0)
l_nmix = (int) ceil(mcd_substeps);
}
else if (implicit)
{
l_nmix = 1;
if (maxmix > max_mixf)
l_nmix = 1 + (int)floor(maxmix / max_mixf);
if (ishift != 0 && (bcon_first == 1 || bcon_last == 1))
{
if (l_nmix < 2)
l_nmix = 2;
}
if (mcd_substeps > 1)
l_nmix = (int)ceil(l_nmix * mcd_substeps);
}
else
{
if (2.25 * maxmix + 1.0 > (double)INT_MAX)
{
m = (LDBLE *)free_check_null(m);
m1 = (LDBLE *)free_check_null(m1);
char token[MAX_LENGTH];
snprintf(token, sizeof(token), "Calculated number of mixes %g, is beyond program limit,\nERROR: please set implicit true, or decrease time_step, or increase cell-lengths.", 2.25 * maxmix);
error_msg(token, STOP);
}
if (bcon_first == 1 || bcon_last == 1)
l_nmix = 1 + (int)floor(2.25 * maxmix);
else
l_nmix = 1 + (int)floor(1.5 * maxmix);
if (ishift != 0 && (bcon_first == 1 || bcon_last == 1))
{
if (l_nmix < 2)
l_nmix = 2;
}
if (mcd_substeps > 1)
l_nmix = (int)ceil(l_nmix * mcd_substeps);
}
for (i = 1; i <= count_cells; i++)
{
m[i] /= l_nmix;
m1[i] /= l_nmix;
/*
* Fill mix structure
*/
cxxMix temp_mix;
temp_mix.Set_n_user(i);
temp_mix.Set_n_user_end(i);
temp_mix.Add(i - 1, m[i]);
temp_mix.Add(i + 1, m1[i]);
temp_mix.Add(i, 1.0 - m[i] - m1[i]);
Dispersion_mix_map[i] = temp_mix;
}
m = (LDBLE *)free_check_null(m);
m1 = (LDBLE *)free_check_null(m1);
return l_nmix;
}
else // multi_D false
{
diffc_here = 2 * diffc_tr * timest;
/*
* Define mixing factors among inner cells
*/
for (i = 1; i <= count_cells; i++)
{
if (i < count_cells)
{
// find mix with higher numbered cell...
if (ishift != 0)
{
if (cell_data[i].disp)
dav = cell_data[i].length / cell_data[i].disp;
if (cell_data[i + 1].disp)
dav += cell_data[i + 1].length / cell_data[i + 1].disp;
if (dav)
m1[i] = 2 / dav;
}
// add diffusive mixf...
m1[i] += diffc_here /
(cell_data[i].length * cell_data[i].length + cell_data[i].length * cell_data[i + 1].length);
m1[i] *= corr_disp; /* m1[i] has mixf with higher cell */
}
if (i > 1)
{
// and with lower numbered cell...
if (ishift != 0)
{
if (cell_data[i].disp)
dav = cell_data[i].length / cell_data[i].disp;
if (cell_data[i - 1].disp)
dav += cell_data[i - 1].length / cell_data[i - 1].disp;
if (dav)
m[i] = 2 / dav;
}
// add diffusive mixf...
m[i] += diffc_here /
(cell_data[i].length * cell_data[i].length + cell_data[i].length * cell_data[i - 1].length);
m[i] *= corr_disp; /* m[i] has mixf with lower numbered cell */
if (m[i] != m1[i - 1] && !warning && (!dav || m[i] / (2 / dav) > 1.00001))
{
warning_msg("Unequal cell-lengths may give mass-balance error, consider using -multi_D");
warning = true;
}
}
mf12 = m[i] + m1[i];
if (mf12 > maxmix)
maxmix = mf12;
}
/*
* Also for boundary cells
*/
if (bcon_first == 1)
{
m[1] = diffc_here / (cell_data[1].length * cell_data[1].length);
if (ishift != 0)
m[1] += cell_data[1].disp / cell_data[1].length;
mf12 = m[1] + m1[1];
if (mf12 > maxmix)
maxmix = mf12;
}
if (bcon_last == 1)
{
m1[count_cells] = diffc_here / (cell_data[count_cells].length * cell_data[count_cells].length);
if (ishift != 0)
m1[count_cells] += cell_data[count_cells].disp / cell_data[count_cells].length;
mf12 = m[count_cells] + m1[count_cells];
if (mf12 > maxmix)
maxmix = mf12;
}
/*
* Find number of mixes
*/
if (maxmix == 0)
l_nmix = 0;
else
{
if (1.5 * maxmix > (double)INT_MAX)
{
m = (LDBLE *)free_check_null(m);
m1 = (LDBLE *)free_check_null(m1);
char token[MAX_LENGTH];
snprintf(token, sizeof(token), "Calculated number of mixes %g, is beyond program limit,\nERROR: please set implicit true, or decrease time_step, or increase cell-lengths.", 1.5 * maxmix);
error_msg(token, STOP);
}
l_nmix = 1 + (int) floor(1.5 * maxmix);
if ((ishift != 0) && ((bcon_first == 1) || (bcon_last == 1)))
{
if (l_nmix < 2)
l_nmix = 2;
}
for (i = 1; i <= count_cells; i++)
{
m[i] /= l_nmix;
m1[i] /= l_nmix;
/*
* Fill mix structure
*/
cxxMix temp_mix;
temp_mix.Set_n_user(i);
temp_mix.Set_n_user_end(i);
temp_mix.Add(i - 1, m[i]);
temp_mix.Add(i + 1, m1[i]);
temp_mix.Add(i, 1.0 - m[i] - m1[i]);
Dispersion_mix_map[i] = temp_mix;
}
}
m = (LDBLE *)free_check_null(m);
m1 = (LDBLE *)free_check_null(m1);
return (l_nmix);
}
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
mix_stag(int i, LDBLE kin_time, int l_punch, LDBLE step_fraction)
/* ---------------------------------------------------------------------- */
{
int n, k;
LDBLE t_imm;
cxxSolution *ptr_imm, *ptr_m;
k = -1000; // compiler says k may be undefined
ptr_imm = NULL;
boolean done_mixing = false;
/*
* Kinetics in transport cell is done while transporting
*/
for (n = 1; n <= stag_data.count_stag; n++)
{
if (i == 0 || i == count_cells + 1)
{
use.Set_mix_ptr(NULL);
use.Set_mix_in(false);
use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i));
if (use.Get_mix_ptr())
{
for (std::map < int, LDBLE >::const_iterator it = use.Get_mix_ptr()->Get_mixComps().begin();
it != use.Get_mix_ptr()->Get_mixComps().end(); it++)
{
if (it->first > i && it->first < all_cells && it->first != count_cells + 1)
{
k = it->first;
ptr_imm = Utilities::Rxn_find(Rxn_solution_map, k);
break;
}
}
}
}
else
{
k = i + 1 + n * count_cells;
if (k < all_cells)
ptr_imm = Utilities::Rxn_find(Rxn_solution_map, k);
}
if (ptr_imm != NULL)
{
if (n == 1)
{
if (heat_nmix > 0 && (!implicit || (implicit && stag_data.count_stag > 1)))
{
ptr_m = Utilities::Rxn_find(Rxn_solution_map, i);
t_imm =
heat_mix_f_imm * ptr_m->Get_tc() + (1 - heat_mix_f_imm) * ptr_imm->Get_tc();
ptr_m->Set_tc(heat_mix_f_m * ptr_imm->Get_tc() + (1 - heat_mix_f_m) * ptr_m->Get_tc());
cell_data[i].temp = ptr_m->Get_tc();
cell_data[k].temp = t_imm = ptr_imm->Get_tc();
/* equilibrate again ... */
cell_no = i;
set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0);
if (multi_Dflag == TRUE)
fill_spec(cell_no, 0);
saver();
cell_no = k;
set_and_run_wrapper(k, NOMIX, FALSE, k, 0.0);
if (multi_Dflag == TRUE)
fill_spec(cell_no, i);
saver();
}
/*
* Mobile cell, kinetics already done ...
*/
cell_no = i;
if (transp_surf)
{
if (diff_stag_surf(i) == ERROR)
error_msg("Error in surface transport, stopping.",
STOP);
}
if (!implicit || (implicit && stag_data.count_stag > 1))
{
if (multi_Dflag == TRUE)
multi_D(1.0, i, 2);
set_and_run_wrapper(i, STAG, FALSE, -2, 0.0);
if (multi_Dflag == TRUE)
fill_spec(cell_no, 0);
if (l_punch)
print_punch(i, true);
saver(); // save solution i in -2, original can be used in other stagnant mixes
}
}
cell_no = k;
if (implicit)
run_reactions(k, kin_time, NOMIX, step_fraction);
else
run_reactions(k, kin_time, STAG, step_fraction);
if (multi_Dflag == TRUE)
fill_spec(cell_no, i);
saver(); // save solution k in -2 - k, original k can be used in other stagnant mixes
done_mixing = true;
}
else if (n == 1 && l_punch && !implicit)
print_punch(i, false);
}
if (done_mixing) // after all mixing is done, the temporal solution becomes the original for the next timestep
{
for (n = 1; n <= stag_data.count_stag; n++)
{
k = i + 1 + n * count_cells;
if (Utilities::Rxn_find(Rxn_solution_map, k) != 0)
{
Utilities::Rxn_copy(Rxn_solution_map, -2 - k, k);
if (n == 1 && !implicit)
Utilities::Rxn_copy(Rxn_solution_map, -2, i);
}
}
}
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
init_heat_mix(int l_nmix)
/* ---------------------------------------------------------------------- */
{
LDBLE lav, mixf, maxmix, corr_disp, l_diffc;
int i, k, n;
int l_heat_nmix;
LDBLE t0;
/*
* Check for need to model thermal diffusion...
*/
if (heat_diffc <= diffc && !implicit)
return (0);
if (count_cells < 2)
return (0);
l_heat_nmix = 0;
if (implicit)
l_diffc = heat_diffc;
else
l_diffc = heat_diffc - diffc_tr;
t0 = Utilities::Rxn_find(Rxn_solution_map, 0)->Get_tc();
for (i = 1; i <= count_cells; i++)
{
if (fabs(cell_data[i].temp - t0) > 1.0)
{
l_heat_nmix = 1;
break;
}
}
if (l_heat_nmix == 0)
{
if (fabs(Utilities::Rxn_find(Rxn_solution_map, count_cells + 1)->Get_tc() - t0) > 1.0)
l_heat_nmix = 1;
for (n = 1; n <= stag_data.count_stag; n++)
{
for (i = 1; i < count_cells; i++)
{
k = i + 1 + n * count_cells;
if (Utilities::Rxn_find(Rxn_solution_map, k) != 0)
{
if (fabs(cell_data[k].temp - t0) > 1.0)
{
l_heat_nmix = 1;
break;
}
}
}
}
}
if (l_heat_nmix == 0)
return (0);
/*
* Initialize arrays...
*/
heat_mix_array = (LDBLE *)PHRQ_malloc((count_cells + 2) * sizeof(LDBLE));
if (heat_mix_array == NULL)
malloc_error();
temp1 = (LDBLE *)PHRQ_malloc((count_cells + 2) * sizeof(LDBLE));
if (temp1 == NULL)
malloc_error();
temp2 = (LDBLE *)PHRQ_malloc((count_cells + 2) * sizeof(LDBLE));
if (temp2 == NULL)
malloc_error();
/*
* Define mixing factors among inner cells...
*/
corr_disp = 1.;
if (correct_disp == TRUE && ishift != 0)
{
mixf = (l_nmix > 1 ? l_nmix : 1);
if (bcon_first == 3)
corr_disp += 1. / count_cells / mixf;
if (bcon_last == 3)
corr_disp += 1. / count_cells / mixf;
}
maxmix = 0.0;
for (i = 1; i < count_cells; i++)
{
lav = (cell_data[i + 1].length + cell_data[i].length) / 2;
mixf = (l_diffc) * timest * corr_disp / tempr / (lav * lav);
if (mixf > maxmix)
maxmix = mixf;
heat_mix_array[i + 1] = mixf; /* m[i] has mixf with lower cell */
}
/*
* Also for boundary cells
*/
if (bcon_first == 1)
{
lav = cell_data[1].length;
mixf = (l_diffc) * timest * corr_disp / tempr / (lav * lav);
if (2 * mixf > maxmix)
maxmix = 2 * mixf;
heat_mix_array[1] = 2 * mixf;
}
else
heat_mix_array[1] = 0;
if (bcon_last == 1)
{
lav = cell_data[count_cells].length;
mixf = (l_diffc) * timest * corr_disp / tempr / (lav * lav);
if (2 * mixf > maxmix)
maxmix = 2 * mixf;
heat_mix_array[count_cells + 1] = 2 * mixf;
}
else
heat_mix_array[count_cells + 1] = 0;
/*
* Find number of mixes
*/
if (maxmix == 0)
l_heat_nmix = 0;
else
{
if (implicit)
{
LDBLE viscos_f;
l_heat_nmix = l_nmix;
for (i = 1; i <= count_cells + 1; i++)
{
heat_mix_array[i - 1] = heat_mix_array[i] / l_heat_nmix; /* for implicit, m[i] has mixf with higher cell */
viscos_f = sol_D[i - 1].viscos_f * exp(heat_diffc / sol_D[i - 1].tk_x - heat_diffc / 298.15);
viscos_f += sol_D[i].viscos_f * exp(heat_diffc / sol_D[i].tk_x - heat_diffc / 298.15);
heat_mix_array[i - 1] *= (viscos_f / 2);
}
}
else
{
l_heat_nmix = 1 + (int)floor(3.0 * maxmix);
for (i = 1; i <= count_cells + 1; i++)
heat_mix_array[i] /= l_heat_nmix;
}
}
return (l_heat_nmix);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
heat_mix(int l_heat_nmix)
/* ---------------------------------------------------------------------- */
{
int i, j;
for (i = 1; i <= count_cells; i++)
temp1[i] = Utilities::Rxn_find(Rxn_solution_map, i)->Get_tc();
temp1[0] = Utilities::Rxn_find(Rxn_solution_map, 0)->Get_tc();
temp1[count_cells + 1] =
Utilities::Rxn_find(Rxn_solution_map, (count_cells + 1))->Get_tc();
for (i = 1; i <= l_heat_nmix; i++)
{
for (j = 1; j <= count_cells; j++)
temp2[j] =
heat_mix_array[j] * temp1[j - 1] + heat_mix_array[j + 1] * temp1[j + 1] +
(1 - heat_mix_array[j] - heat_mix_array[j + 1]) * temp1[j];
for (j = 1; j <= count_cells; j++)
temp1[j] = temp2[j];
}
for (i = 1; i <= count_cells; i++)
{
cell_data[i].temp = temp1[i];
Utilities::Rxn_find(Rxn_solution_map, i)->Set_tc(temp1[i]);
}
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
set_initial_moles(int i)
/* ---------------------------------------------------------------------- */
{
cxxKinetics *kinetics_ptr;
char token[MAX_LENGTH];
const char* cptr;
int j, k, l;
/*
* Pure phase assemblage
*/
{
cxxPPassemblage * pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, i);
if (pp_assemblage_ptr != NULL)
{
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_initial_moles(it->second.Get_moles());
if (it->second.Get_initial_moles() < 0)
it->second.Set_initial_moles(0.0);
}
}
}
/*
* Gas phase
*/
{
cxxGasPhase * gas_phase_ptr = Utilities::Rxn_find(Rxn_gas_phase_map, i);
if (gas_phase_ptr != NULL)
{
std::vector<cxxGasComp> gc = gas_phase_ptr->Get_gas_comps();
for (size_t l = 0; l < gc.size(); l++)
{
gc[l].Set_initial_moles(gc[l].Get_moles());
}
gas_phase_ptr->Set_gas_comps(gc);
}
}
/*
* Kinetics
*/
kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i);
if (kinetics_ptr != NULL)
{
for (j = 0; j < (int) kinetics_ptr->Get_kinetics_comps().size(); j++)
{
cxxKineticsComp *kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]);
kinetics_comp_ptr->Set_initial_moles(kinetics_comp_ptr->Get_m());
}
}
/*
* Solid solutions
*/
{
cxxSSassemblage *ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, i);
if (ss_assemblage_ptr != NULL)
{
std::vector<cxxSS *> ss_ptrs = ss_assemblage_ptr->Vectorize();
for (k = 0; k < (int) ss_ptrs.size(); k++)
{
cxxSS * ss_ptr = ss_ptrs[k];
for (j = 0; j < (int) ss_ptr->Get_ss_comps().size(); j++)
{
cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]);
comp_ptr->Set_init_moles(comp_ptr->Get_moles());
}
}
}
}
/*
* For interlayer diffusion: add tiny bit of exchanger if absent
*/
cxxExchange * exchange_ptr = Utilities::Rxn_find(Rxn_exchange_map, i);
if (interlayer_Dflag && exchange_ptr == NULL)
{
cxxExchange temp_exchange;
temp_exchange.Set_n_user_both(i);
temp_exchange.Set_description("Interlayer diffusion: added 2e-10 moles X-");
use.Set_exchange_in(true);
use.Set_n_exchange_user(i);
temp_exchange.Set_new_def(true);
temp_exchange.Set_solution_equilibria(true);
temp_exchange.Set_n_solution(i);
cxxExchComp comp;
count_elts = 0;
paren_count = 0;
Utilities::strcpy_safe(token, MAX_LENGTH, "X");
cptr = token;
get_elts_in_species(&cptr, 2e-10);
cptr = token;
LDBLE z;
{
std::string token1;
get_token(&cptr, token1, &z, &l);
comp.Set_formula(token1.c_str());
}
comp.Set_formula_z(z);
comp.Set_totals(elt_list_NameDouble());
comp.Set_charge_balance(0.0);
temp_exchange.Get_exchange_comps().push_back(comp);
Rxn_exchange_map[i] = temp_exchange;
state = INITIAL_EXCHANGE;
initial_exchangers(TRUE);
state = TRANSPORT;
}
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
fill_spec(int l_cell_no, int ref_cell)
/* ---------------------------------------------------------------------- */
{
/* copy species activities into sol_D.spec... */
int i, i1, i2, i3, count_spec, count_exch_spec, size_xt;
char token[MAX_LENGTH];
const char * name;
class species *s_ptr, *s_ptr2;
class master *master_ptr;
LDBLE dum, dum2;
LDBLE lm;
LDBLE por, por_il, viscos_f, viscos_il_f, viscos;
bool x_max_done = false;
std::set <std::string> loc_spec_names;
s_ptr2 = NULL;
// for implicit
std::set <std::string> ::iterator it;
std::pair <std::set<std::string> ::iterator, bool> name_ret;
if (implicit && !l_cell_no)
dif_spec_names.clear();
size_xt = 5;
//sol_D[l_cell_no].spec = (class spec *) free_check_null(sol_D[l_cell_no].spec);
if (sol_D[l_cell_no].spec == NULL)
{
sol_D[l_cell_no].spec = (class spec *) PHRQ_malloc((species_list.size() + (size_t)size_xt) * sizeof(class spec));
sol_D[l_cell_no].spec_size = (int)species_list.size() + size_xt;
}
else if ((int)species_list.size() + size_xt > sol_D[l_cell_no].spec_size)
{
sol_D[l_cell_no].spec = (class spec *) PHRQ_realloc(sol_D[l_cell_no].spec, (species_list.size() + (size_t)size_xt) * sizeof(class spec));
sol_D[l_cell_no].spec_size = (int)species_list.size() + size_xt;
}
if (sol_D[l_cell_no].spec == NULL)
malloc_error();
for (i = 0; i < sol_D[l_cell_no].spec_size; i++)
{
sol_D[l_cell_no].spec[i].name = NULL;
sol_D[l_cell_no].spec[i].aq_name = NULL;
sol_D[l_cell_no].spec[i].type = -1;
sol_D[l_cell_no].spec[i].a = 0.0;
sol_D[l_cell_no].spec[i].lm = min_dif_LM;
sol_D[l_cell_no].spec[i].lg = -0.04;
sol_D[l_cell_no].spec[i].c = 0.0;
sol_D[l_cell_no].spec[i].z = 0.0;
sol_D[l_cell_no].spec[i].Dwt = 0.0;
sol_D[l_cell_no].spec[i].dw_t = 0.0;
sol_D[l_cell_no].spec[i].erm_ddl = 0.0;
sol_D[l_cell_no].count_exch_spec = sol_D[l_cell_no].count_spec = 0;
}
sol_D[l_cell_no].tk_x = tk_x;
viscos_f = viscos_il_f = 1.0;
if (l_cell_no == 0)
{
por = cell_data[1].por;
por_il = cell_data[1].por_il;
}
else if (l_cell_no == count_cells + 1)
{
por = cell_data[count_cells].por;
por_il = cell_data[count_cells].por_il;
}
else
{
por = cell_data[l_cell_no].por;
por_il = cell_data[l_cell_no].por_il;
}
if (por < multi_Dpor_lim)
por = viscos_f = 0.0;
if (por_il < interlayer_Dpor_lim)
por_il = viscos_il_f = 0.0;
/*
* correct diffusion coefficient for temperature and viscosity, D_T = D_298 * viscos_298 / viscos
* modify viscosity effect: Dw(TK) = Dw(298.15) * exp(dw_t / TK - dw_t / 298.15), SC data from Robinson and Stokes, 1959
*/
viscos = viscos_0;
/*
* put temperature factor in por_factor which corrects for porous medium...
*/
dum = viscos_0_25 / viscos;
viscos_f *= dum;
viscos_il_f *= dum;
sol_D[l_cell_no].viscos_f = dum;
count_spec = count_exch_spec = 0;
/*
* sort species by name...
*/
if (species_list.size() > 1)
{
qsort(&species_list[0], species_list.size(),
sizeof(class species_list), sort_species_name);
}
if (correct_Dw)
calc_SC();
for (i = 0; i < (int)species_list.size(); i++)
{
/*
* copy species data
*/
s_ptr = species_list[i].s;
if (s_ptr->type > HPLUS &&
!(s_ptr->type == EX && interlayer_Dflag))
continue;
//if (s_ptr->type == EX && !interlayer_Dflag)
// continue;
//if (s_ptr->type == SURF)
// continue;
if (i > 0 && strcmp(s_ptr->name, species_list[(size_t)i - 1].s->name) == 0)
continue;
//if (s_ptr == s_h2o)
// continue;
if (s_ptr->type == EX)
{
if (s_ptr->lm > min_dif_LM)
{
/* find exchanger's name, use only master exchanger 'X' */
if (species_list[i].master_s->secondary != NULL)
master_ptr = species_list[i].master_s->secondary;
else
master_ptr = species_list[i].master_s->primary;
if (s_ptr->equiv != 0.0)
dum = fabs(s_ptr->equiv) / master_ptr->total;
else
{
if (species_list[i].master_s->z == 0)
dum = 1 / master_ptr->total;
else
dum = 1;
}
name = master_ptr->elt->name;
if (strcmp(name, "X") != 0)
{
if (!warn_MCD_X)
{
snprintf(token, sizeof(token),
"MCD found more than 1 exchanger, uses X for interlayer diffusion.");
warning_msg(token);
warn_MCD_X = 1;
}
continue;
}
dum2 = s_ptr->moles * dum; /* equivalent fraction */
sol_D[l_cell_no].spec[count_spec].name = s_ptr->name;
//string_hsave(s_ptr->name);
sol_D[l_cell_no].spec[count_spec].type = EX;
sol_D[l_cell_no].spec[count_spec].c = dum2;
sol_D[l_cell_no].spec[count_spec].lg = s_ptr->lg - log10(dum);
sol_D[l_cell_no].spec[count_spec].a = dum2 * pow(10, sol_D[l_cell_no].spec[count_spec].lg);
sol_D[l_cell_no].exch_total = master_ptr->total;
if (transport_step == 0 && !x_max_done)
{
x_max_done = true;
dum = master_ptr->total / Utilities::Rxn_find(Rxn_solution_map, l_cell_no)->Get_mass_water();
if (dum > sol_D[1].x_max)
sol_D[1].x_max = dum;
}
/* find the aqueous species in the exchange reaction... */
for (i2 = 0; (s_ptr->rxn.token[i2].s != NULL); i2++)
{
if ((s_ptr2 = s_ptr->rxn.token[i2].s)->type == AQ)
break;
}
/* copy its name and Dw and charge... */
sol_D[l_cell_no].spec[count_spec].aq_name = s_ptr2->name;
//string_hsave(s_ptr2->name);
sol_D[l_cell_no].spec[count_spec].z = s_ptr2->z;
if (s_ptr2->dw == 0)
sol_D[l_cell_no].spec[count_spec].Dwt = default_Dw * viscos_il_f;
else
{
if (s_ptr2->dw_t)
{
sol_D[l_cell_no].spec[count_spec].Dwt = s_ptr2->dw *
exp(s_ptr2->dw_t / 298.15 - s_ptr2->dw_t / tk_x) * viscos_il_f;
sol_D[l_cell_no].spec[count_spec].dw_t = s_ptr2->dw_t;
}
else
sol_D[l_cell_no].spec[count_spec].Dwt = s_ptr2->dw * viscos_il_f;
}
//if (implicit) // && name_ret.second && (l_cell_no > 1 || (l_cell_no == 1 && bcon_first != 2)))
//{
// // name_ret = dif_spec_names.insert(s_ptr->name); // but not in implicit now...
// // must fill in the spec in previous cells, order the names, see below for aqueous species...
//}
count_exch_spec++;
count_spec++;
}
continue;
}
lm = s_ptr->lm;
if (lm > min_dif_LM)
{
if (implicit/* && l_cell_no < count_cells + 2 */)
{
name_ret = dif_spec_names.insert(s_ptr->name);
loc_spec_names.insert(s_ptr->name);
i2 = 0;
for (it = dif_spec_names.begin(); it != dif_spec_names.end(); it++)
{
if (*it == s_ptr->name)
break;
i2++;
}
if (i2 > count_spec)
{
if (l_cell_no == 0)
{
for (i1 = i2; i1 > count_spec; i1--)
{
it = dif_spec_names.find(s_ptr->name);
dif_spec_names.erase(--it);
}
}
else
{
// there are species before s_ptr->name that must be included first
i1 = 0;
for (it = loc_spec_names.begin(); it != loc_spec_names.end(); it++)
{
if (*it == s_ptr->name)
break;
i1++;
}
i3 = i2 - i1;
if (i3 + count_spec + 1 > sol_D[l_cell_no].spec_size)
{
sol_D[l_cell_no].spec = (class spec *) PHRQ_realloc(sol_D[l_cell_no].spec,
((size_t)i3 + count_spec + 1 + (size_t)size_xt) * sizeof(class spec));
if (sol_D[l_cell_no].spec == NULL)
malloc_error();
sol_D[l_cell_no].spec_size = i3 + count_spec + 1 + size_xt;
}
for (; i1 < i2; i1++) // i1 is loop variable
{
// memmove(&sol_D[l_cell_no].spec[i1], &sol_D[ref_cell].spec[i1], sizeof(class spec));
sol_D[l_cell_no].spec[i1] = sol_D[ref_cell].spec[i1];
sol_D[l_cell_no].spec[i1].c = 0.0;
sol_D[l_cell_no].spec[i1].a = 0.0;
sol_D[l_cell_no].spec[i1].lm = min_dif_LM;
sol_D[l_cell_no].spec[i1].lg = -0.04;
loc_spec_names.insert(sol_D[l_cell_no].spec[i1].name);
count_spec++;
}
}
}
}
if (count_spec >= sol_D[l_cell_no].spec_size)
{
sol_D[l_cell_no].spec = (class spec *) PHRQ_realloc(sol_D[l_cell_no].spec,
(count_spec + (size_t)size_xt) * sizeof(class spec));
if (sol_D[l_cell_no].spec == NULL)
malloc_error();
sol_D[l_cell_no].spec_size = count_spec + size_xt;
}
sol_D[l_cell_no].spec[count_spec].name = s_ptr->name;
sol_D[l_cell_no].spec[count_spec].type = AQ;
sol_D[l_cell_no].spec[count_spec].c = s_ptr->moles / mass_water_aq_x;
sol_D[l_cell_no].spec[count_spec].a = under(lm + s_ptr->lg);
sol_D[l_cell_no].spec[count_spec].lm = lm;
sol_D[l_cell_no].spec[count_spec].lg = s_ptr->lg;
sol_D[l_cell_no].spec[count_spec].z = s_ptr->z;
if (s_ptr->dw == 0)
sol_D[l_cell_no].spec[count_spec].Dwt = default_Dw * viscos_f;
else
{
if (s_ptr->dw_t)
{
sol_D[l_cell_no].spec[count_spec].Dwt = s_ptr->dw *
exp(s_ptr->dw_t / tk_x - s_ptr->dw_t / 298.15) * viscos_f;
sol_D[l_cell_no].spec[count_spec].dw_t = s_ptr->dw_t;
}
else
sol_D[l_cell_no].spec[count_spec].Dwt = s_ptr->dw * viscos_f;
}
if (correct_Dw)
{
//calc_SC(); // removed that neutral species are corrected as if z = 1, but is viscosity-dependent
sol_D[l_cell_no].spec[count_spec].Dwt = s_ptr->dw_corr * viscos_f;
}
if (l_cell_no <= count_cells + 1 && sol_D[l_cell_no].spec[count_spec].Dwt * pow(por, multi_Dn) > diffc_max)
diffc_max = sol_D[l_cell_no].spec[count_spec].Dwt * pow(por, multi_Dn);
sol_D[l_cell_no].spec[count_spec].erm_ddl = s_ptr->erm_ddl;
if (implicit/* && l_cell_no < count_cells + 2 */)
{
if (name_ret.second && l_cell_no)
{
// must fill in the spec in previous cells, order the names...
i3 = 0;
for (it = dif_spec_names.begin(); it != dif_spec_names.end(); ++it)
{
if (it == name_ret.first)
break;
i3++;
}
for (i1 = 0; i1 < l_cell_no; i1++)
{
i2 = sol_D[i1].count_spec + 1;
if (i2 > sol_D[i1].spec_size)
{
sol_D[i1].spec = (class spec *) PHRQ_realloc(sol_D[i1].spec,
((size_t)i2 + (size_t)size_xt) * sizeof(class spec));
if (sol_D[i1].spec == NULL)
malloc_error();
sol_D[i1].spec_size = i2 + size_xt;
}
i2--;
for (; i2 > i3; i2--) // i2 is loop variable
sol_D[i1].spec[i2] = sol_D[i1].spec[i2 - 1];
// memmove(&sol_D[i1].spec[i2], &sol_D[l_cell_no].spec[i2], sizeof(class spec));
sol_D[i1].spec[i2] = sol_D[l_cell_no].spec[i2];
sol_D[i1].spec[i2].a = 0.0;
sol_D[i1].spec[i2].lm = min_dif_LM;
sol_D[i1].spec[i2].lg = -0.04;
sol_D[i1].spec[i2].c = 0.0;
sol_D[i1].count_spec += 1;
}
}
}
count_spec++;
}
}
sol_D[l_cell_no].count_spec = count_spec;
sol_D[l_cell_no].count_exch_spec = count_exch_spec;
if (implicit/* && l_cell_no < count_cells + 2 */ && loc_spec_names.size() < dif_spec_names.size())
{
i3 = (int) dif_spec_names.size();
if (i3 > sol_D[l_cell_no].spec_size)
{
sol_D[l_cell_no].spec = (class spec *) PHRQ_realloc(sol_D[l_cell_no].spec,
((size_t)i3 + (size_t)size_xt) * sizeof(class spec));
if (sol_D[l_cell_no].spec == NULL)
malloc_error();
sol_D[l_cell_no].spec_size = i3 + size_xt;
}
for (i1 = count_spec; i1 < i3; i1++)
{
// memmove(&sol_D[l_cell_no].spec[i1], &sol_D[ref_cell].spec[i1], sizeof(class spec));
sol_D[l_cell_no].spec[i1] = sol_D[ref_cell].spec[i1];
sol_D[l_cell_no].spec[i1].c = 0.0;
sol_D[l_cell_no].spec[i1].a = 0.0;
sol_D[l_cell_no].spec[i1].lm = min_dif_LM;
sol_D[l_cell_no].spec[i1].lg = -0.04;
sol_D[l_cell_no].count_spec += 1;
loc_spec_names.insert(sol_D[l_cell_no].spec[i1].name);
count_spec++;
}
//if (loc_spec_names != dif_spec_names)
//{
// error_string = sformatf(
// "Species in implicit diffusion in cell %d are %; different from previous cells with %d species.",
// l_cell_no, loc_spec_names.size(), dif_spec_names.size());
// error_msg(error_string, CONTINUE);
//}
}
if (implicit && l_cell_no/* && l_cell_no < count_cells + 2 */)
{
for (i = 0; i < count_spec; i++)
{
//name = sol_D[l_cell_no].spec[i].name;
if (strcmp(sol_D[l_cell_no].spec[i].name, sol_D[ref_cell].spec[i].name))
{
error_string = sformatf(
"Implicit diffusion: species %s in cell %d differs from species %s in previous cells.",
sol_D[l_cell_no].spec[i].name, l_cell_no, sol_D[ref_cell].spec[i].name);
error_msg(error_string, CONTINUE);
}
}
}
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
sort_species_name(const void *ptr1, const void *ptr2)
/* ---------------------------------------------------------------------- */
{
const class species_list *nptr1, *nptr2;
nptr1 = (const class species_list *) ptr1;
nptr2 = (const class species_list *) ptr2;
return (strcmp(nptr1->s->name, nptr2->s->name));
}
/* ---------------------------------------------------------------------- */
void Phreeqc::
diffuse_implicit(LDBLE DDt, int stagnant)
/* ---------------------------------------------------------------------- */
{
// The structure comes from example 11.3 in A&P and Appelo 2017, CCR 101, 102:
// Ct2 is implicitly solved for the individual species, corrected for electro-neutrality, this always gives Ct2 > 0.
// Version 3.5.2 includes diffusion in 1 stagnant layer in the implicit calc'n.
// Transport of aqueous species is summarized into master species.
// With electro-migration, transport of anions and cations is calculated in opposite directions since (sign) J = - z * dV.
// Only available moles are transported, thus are > 0, but if concentrations oscillate,
// change max_mixf in input file: -implicit true 1 # max_mixf = 1 (default).
int i, icell, cp, comp;
// ifirst = (bcon_first == 2 ? 1 : 0); ilast = (bcon_last == 2 ? count_cells - 1 : count_cells);
int ifirst, ilast;
int i_1, i0, i1, i2 = 0;
//double mfr, mfr1, max_b = 0, b, grad, dVc, j_0e, min_dif_M = pow(10, min_dif_LM);
double mfr, mfr1, grad, dVc, j_0e, min_dif_M = pow(10, min_dif_LM);
LDBLE dum1, dum2, dum_stag = 0.0, min_mol;
LDBLE dum = 0;
//cxxSurfaceCharge * charge_ptr = NULL;
if (stagnant > 1)
stagnant = 0;
cxxSolution *sptr1, *sptr2, *sptr_stag;
std::vector<int> n_solution;
std::vector<LDBLE> fraction;
std::map<int, std::map<std::string, double> > ::iterator it1;
std::map<std::string, double> ::iterator it2;
// c= count_cells, c1=(c+1)= end boundary-cell, c_1=(c-1), c2=(c+2)= first stagnant cell, cc1=(c+c+1)= last stagnant cell
int c = count_cells, c1 = c + 1, c2 = c + 2, c_1 = c - 1, cc = c + stagnant * c, cc1 = cc + 1;
comp = sol_D[1].count_spec - sol_D[1].count_exch_spec;
cell_J_ij.clear();
for (i = 0; i <= count_cells; i++)
{
std::map<std::string, class J_ij_save> J_map;
for (cp = 0; cp < comp; cp++)
{
class J_ij_save J_save;
J_map[sol_D[1].spec[cp].name] = J_save;
}
cell_J_ij[i] = J_map;
}
if (heat_nmix)
comp += 1;
if (Ct2 == NULL)
Ct2 = (LDBLE *) PHRQ_malloc((count_cells + 2 + (size_t)stagnant * count_cells) * sizeof(LDBLE));
if (Ct2 == NULL) malloc_error();
if (l_tk_x2 == NULL)
l_tk_x2 = (LDBLE *) PHRQ_malloc((count_cells + 2 + (size_t)stagnant * count_cells) * sizeof(LDBLE));
if (l_tk_x2 == NULL) malloc_error();
if (A == NULL)
{
A = (LDBLE **)PHRQ_malloc((count_cells + 2 + (size_t)stagnant * count_cells) * sizeof(LDBLE *));
if (A == NULL) malloc_error();
for (i = 0; i < count_cells + 2 + stagnant * count_cells; i++)
{
if (stagnant)
A[i] = (LDBLE *)PHRQ_calloc((2 * count_cells + 2), sizeof(LDBLE));
else
A[i] = (LDBLE *)PHRQ_malloc(3 * sizeof(LDBLE));
if (A[i] == NULL) malloc_error();
}
}
if (LU == NULL)
{
LU = (LDBLE **)PHRQ_malloc((count_cells + 2 + (size_t)stagnant * count_cells) * sizeof(LDBLE *));
if (LU == NULL) malloc_error();
for (i = 0; i < count_cells + 2 + stagnant * count_cells; i++)
{
if (stagnant)
LU[i] = (LDBLE *)PHRQ_calloc((2 * count_cells + 2), sizeof(LDBLE));
else
LU[i] = (LDBLE *)PHRQ_malloc(3 * sizeof(LDBLE));
if (LU[i] == NULL) malloc_error();
}
}
if (mixf == NULL)
{
mixf = (LDBLE **)PHRQ_malloc((count_cells + 2) * sizeof(LDBLE *));
if (mixf == NULL) malloc_error();
for (i = 0; i < count_cells + 2; i++)
{
mixf[i] = NULL;
}
}
if (stagnant)
{
if (mixf_stag == NULL)
{
mixf_stag = (LDBLE **)PHRQ_malloc((count_cells + 2) * sizeof(LDBLE *));
if (mixf_stag == NULL) malloc_error();
for (i = 0; i < count_cells + 2; i++)
{
mixf_stag[i] = NULL;
}
}
}
if (comp + 2 > mixf_comp_size)
{
for (i = 0; i < count_cells + 2; i++)
{
if (mixf[i] == NULL)
mixf[i] = (LDBLE *)PHRQ_malloc(((size_t)comp + 2) * sizeof(LDBLE));
else
mixf[i] = (LDBLE *)PHRQ_realloc(mixf[i], ((size_t)comp + 2) * sizeof(LDBLE));
if (mixf[i] == NULL) malloc_error();
if (stagnant)
{
if (mixf_stag[i] == NULL)
mixf_stag[i] = (LDBLE *)PHRQ_malloc(((size_t)comp + 2) * sizeof(LDBLE));
else
mixf_stag[i] = (LDBLE *)PHRQ_realloc(mixf_stag[i], ((size_t)comp + 2) * sizeof(LDBLE));
if (mixf_stag[i] == NULL) malloc_error();
for (cp = 0; cp < comp; cp++)
mixf_stag[i][cp] = 0;
}
}
mixf_comp_size = comp + 2;
}
if (dV_dcell)
find_current = 1;
// obtain b_ij...
dummy = default_Dw * pow(multi_Dpor, multi_Dn) * multi_Dpor;
for (i = 0; i <= count_cells + 1; i++)
{
if (!heat_nmix)
{
if (i <= count_cells)
l_tk_x2[i] = (sol_D[i].tk_x + sol_D[i + 1].tk_x) / 2;
//if (stagnant && i > 0 && i <= c)
// l_tk_x2[i + c1] = (sol_D[i].tk_x + sol_D[i + c1].tk_x) / 2;
}
if (stagnant)
{
use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i));
if (use.Get_mix_ptr() == NULL)
{
i1 = 0;
if (i == 0 && (sptr1 = Utilities::Rxn_find(Rxn_solution_map, c2)) != NULL)
{
// the 1st boundary solution diffuses into 1st stagnant cell (=c2) by the ratio immobile_kgw in cell c2 / mobile_kgw in cell 1
find_J(0, 1, 1, 1, 0);
ct[c2].kgw = sptr1->Get_mass_water();
for (cp = 0; cp < comp; cp++)
{
if (!heat_nmix || cp < comp - 1)
mixf_stag[i][cp] = DDt * ct[i].v_m[cp].b_ij * ct[c2].kgw / ct[1].kgw;
else if (heat_nmix && cp == comp - 1)
mixf_stag[i][cp] = mixf_stag[i][0] * heat_diffc / tempr / sol_D[i].spec[0].Dwt;
}
i1 = c2;
}
else if (i == c1 && (sptr1 = Utilities::Rxn_find(Rxn_solution_map, cc1)) != NULL)
{
// the end boundary solution diffuses into stagnant cell cc1
find_J(c, c1, 1, 1, 0);
ct[cc1].kgw = sptr1->Get_mass_water();
for (cp = 0; cp < comp; cp++)
{
if (!heat_nmix || cp < comp - 1)
mixf_stag[i][cp] = DDt * ct[c].v_m[cp].b_ij * ct[cc1].kgw / ct[c].kgw;
else if (heat_nmix && cp == comp - 1)
mixf_stag[i][cp] = mixf_stag[i][0] * heat_diffc / tempr / sol_D[c].spec[0].Dwt;
}
i1 = cc1;
}
if (i1)
{
find_J(i, i1, 1, 1, 1);
}
}
else
{
use.Get_mix_ptr()->Vectorize(n_solution, fraction);
for (i1 = 0; i1 < (int) use.Get_mix_ptr()->Get_mixComps().size(); i1++)
{
if (n_solution[i1] > count_cells && n_solution[i1] <= cc1)
{
mfr = fraction[i1] / nmix;
find_J(i, n_solution[i1], mfr, 1, 1);
mfr *= (2.0 / dummy);
for (cp = 0; cp < comp; cp++)
{
if (!heat_nmix || cp < comp - 1)
mixf_stag[i][cp] = ct[i].v_m[cp].b_ij * mfr;
else if (heat_nmix && cp == comp - 1)
mixf_stag[i][cp] = mixf_stag[i][0] * heat_diffc / tempr / sol_D[i].spec[0].Dwt;
}
}
}
}
}
if (i <= count_cells)
find_J(i, i + 1, 1, 1, 0);
}
if (dV_dcell)
find_current = 0;
ifirst = (bcon_first == 2 ? 1 : 0);
ilast = (bcon_last == 2 ? count_cells - 1 : count_cells);
for (cp = 0; cp < comp; cp++)
{
for (i = 0; i <= cc1; i++)
{
if (heat_nmix && cp == comp - 1)
Ct2[i] = sol_D[i].tk_x;
else
Ct2[i] = sol_D[i].spec[cp].c;
}
// fill coefficient matrix A ...
// boundary cells ...
if (heat_nmix && cp == comp - 1)
{
mfr = mixf[0][cp] = heat_mix_array[0];
mfr1 = mixf[1][cp] = heat_mix_array[1];
}
else
{
mfr = mixf[0][cp] = DDt * ct[0].v_m[cp].b_ij;
mfr1 = mixf[1][cp] = DDt * ct[1].v_m[cp].b_ij;
}
if (bcon_first == 2)
{
A[1][0] = 0; A[1][1] = 1 + mfr1; A[1][2] = -mfr1;
if (stagnant)
{
A[0][0] = A[c2][c2] = 1;
if (mixf_stag[1][cp])
{
A[1][1] += mixf_stag[1][cp];
A[1][c2] = A[c2][1] = -mixf_stag[1][cp];
A[c2][c2] += mixf_stag[1][cp];
}
}
else
{
A[0][1] = 1; A[0][2] = 0;
}
}
else
{
A[1][0] = -mfr; A[1][1] = 1 + mfr + mfr1; A[1][2] = -mfr1;
if (stagnant)
{
if (dV_dcell)
{
A[0][0] = 1 + mfr; A[0][1] = -mfr;
}
else
A[0][0] = 1;
A[c2][c2] = 1;
if (mixf_stag[0][cp])
{
if (dV_dcell)
{
A[0][0] += mixf_stag[0][cp]; A[0][c2] = -mixf_stag[0][cp];
}
A[c2][0] = -mixf_stag[0][cp];
A[c2][c2] += mixf_stag[0][cp];
}
if (mixf_stag[1][cp])
{
A[1][1] += mixf_stag[1][cp];
A[1][c2] = A[c2][1] = -mixf_stag[1][cp];
A[c2][c2] += mixf_stag[1][cp];
}
}
else
{
if (dV_dcell)
{
A[0][1] = 1 + mfr; A[0][2] = -mfr;
}
else
{
A[0][1] = 1; A[0][2] = 0;
}
}
}
if (heat_nmix && cp == comp - 1)
{
mfr = mixf[c_1][cp] = heat_mix_array[c_1];
mfr1 = mixf[count_cells][cp] = heat_mix_array[count_cells];
}
else
{
mfr = mixf[c_1][cp] = DDt * ct[c_1].v_m[cp].b_ij;
mfr1 = mixf[count_cells][cp] = DDt * ct[count_cells].v_m[cp].b_ij;
}
if (bcon_last == 2)
{
if (stagnant)
{
A[count_cells][c_1] = -mfr; A[count_cells][count_cells] = 1 + mfr;
A[c1][c1] = A[cc1][cc1] = 1;
if (mixf_stag[count_cells][cp])
{
A[count_cells][count_cells] += mixf_stag[count_cells][cp];
A[count_cells][cc1] = A[cc1][count_cells] = -mixf_stag[count_cells][cp];
A[cc1][cc1] += mixf_stag[count_cells][cp];
}
}
else
{
A[count_cells][0] = -mfr; A[count_cells][1] = 1 + mfr; A[count_cells][2] = 0;
A[c1][0] = 0; A[c1][1] = 1;
}
}
else
{
if (stagnant)
{
A[count_cells][c_1] = -mfr; A[count_cells][count_cells] = 1 + mfr + mfr1;
A[count_cells][c1] = -mfr1;
if (dV_dcell)
{
A[c1][count_cells] = -mfr1; A[c1][c1] = 1 + mfr1;
}
else
A[c1][c1] = 1;
A[cc1][cc1] = 1;
if (mixf_stag[count_cells][cp])
{
A[count_cells][count_cells] += mixf_stag[count_cells][cp];
A[count_cells][cc1] = A[cc1][count_cells] = -mixf_stag[count_cells][cp];
A[cc1][cc1] += mixf_stag[count_cells][cp];
}
if (mixf_stag[c1][cp])
{
if (dV_dcell)
{
A[c1][c1] += mixf_stag[c1][cp]; A[c1][cc1] = -mixf_stag[c1][cp];
}
A[cc1][c1] = -mixf_stag[c1][cp];
A[cc1][cc1] += mixf_stag[c1][cp];
}
}
else
{
A[count_cells][0] = -mfr; A[count_cells][1] = 1 + mfr + mfr1; A[count_cells][2] = -mfr1;
if (dV_dcell)
{
A[c1][0] = -mfr1; A[c1][1] = 1 + mfr1;
}
else
{
A[c1][0] = 0; A[c1][1] = 1;
}
}
}
// inner cells ...
for (i = 2; i < count_cells; i++)
{
if (heat_nmix && cp == comp - 1)
{
mfr = mixf[i - 1][cp] = heat_mix_array[i - 1];
mfr1 = mixf[i][cp] = heat_mix_array[i];
}
else
{
mfr = mixf[i - 1][cp] = DDt * ct[i - 1].v_m[cp].b_ij;
mfr1 = mixf[i][cp] = DDt * ct[i].v_m[cp].b_ij;
}
if (stagnant && mixf_stag[i][cp])
{
A[i][i - 1] = -mfr; A[i][i] = 1 + mfr + mfr1 + mixf_stag[i][cp]; A[i][i + 1] = -mfr1;
A[i + c1][i + c1] = 1 + mixf_stag[i][cp];
A[i][i + c1] = A[i + c1][i] = -mixf_stag[i][cp];
}
else
{
A[i][0] = -mfr; A[i][1] = 1 + mfr + mfr1; A[i][2] = -mfr1;
}
}
if (stagnant)
// decompose full A in LU, bypass A[][] = 0...
{
LDBLE s;
LU[0][0] = A[0][0]; LU[1][0] = A[1][0]; LU[c2][0] = A[c2][0];
LU[0][1] = A[0][1] / LU[0][0]; LU[0][c2] = A[0][c2] / LU[0][0];
for (i = 1; i <= cc1; i++)
{
i_1 = i - 1;
if (i < c2)
{
LU[i][i] = A[i][i] - LU[i][i_1] * LU[i_1][i];
LU[i + 1][i] = A[i + 1][i];
LU[i][i + 1] = A[i][i + 1] / LU[i][i];
if (i == 1)
{
LU[c2][1] = A[c2][1] - LU[c2][0] * LU[0][1];
LU[1][c2] = (A[1][c2] - LU[1][0] * LU[0][c2]) / LU[1][1];
}
else
{
for (i0 = 0; i0 <= i_1; i0++)
{
if (i0 == i_1 && i0 < c)
{
LU[c2 + i0][i] = A[c2 + i0][i];
LU[i][c2 + i0] = A[i][c2 + i0] / LU[i][i];
}
else if (i0 < c)
{
LU[c2 + i0][i] = -LU[c2 + i0][i_1] * LU[i_1][i];
LU[i][c2 + i0] = -LU[i][i_1] * LU[i_1][c2 + i0] / LU[i][i];
}
if (i > c && i0 == c_1)
{
LU[c2 + i0][i] += A[c2 + i0][i];
LU[i][c2 + i0] += A[i][c2 + i0] / LU[i][i];
}
}
}
}
else
{
// the i = count_cells + 2 and higher rows are filled from i0 onwards, need to subtract the L*U terms for the ith column of L.
for (i0 = i; i0 <= cc1; i0++)
{
s = 0;
if (i0 == i)
{
i2 = (i == c2 ? 0 : i - c1);
for (i1 = i2; i1 <= i_1; i1++)
s += LU[i0][i1] * LU[i1][i];
LU[i][i] = A[i][i] - s;
i2 += (i2 == 0 ? 2 : 1);
}
else
{
for (i1 = i2; i1 <= i_1; i1++)
s += LU[i0][i1] * LU[i1][i];
LU[i0][i] = -s;
}
}
// and the ith row of U...
if (i == cc1)
break;
for (i0 = i + 1; i0 <= cc1; i0++)
{
s = 0;
if (i0 == i + 1)
i2 = i - c;
for (i1 = i2; i1 <= i_1; i1++)
s += LU[i][i1] * LU[i1][i0];
LU[i][i0] = -s / LU[i][i];
i2++;
}
}
}
Ct2[0] /= LU[0][0];
for (i = 1; i <= cc1; i++)
{
i_1 = i - 1;
if (i < c2)
s = LU[i][i_1] * Ct2[i_1];
else
{
i2 = 0;
s = 0;
for (i1 = i2; i1 <= i_1; i1++)
{
s += LU[i][i1] * Ct2[i1];
i2 += (i2 == 0 ? 2 : 1);
}
}
Ct2[i] = (Ct2[i] - s) / LU[i][i];
}
for (i = cc; i >= 0; i--)
{
s = 0;
for (i1 = i + 1; i1 <= cc1; i1++)
s += LU[i][i1] * Ct2[i1];
Ct2[i] -= s;
}
}
else
{
// decompose A in LU : store L in A[..][0..1] and U in A[..][2] ...
for (i = 1; i <= count_cells + 1; i++)
{
A[i - 1][2] /= A[i - 1][1];
A[i][1] -= A[i][0] * A[i - 1][2];
}
// solve Ct2 in A.Ct2 = L.U.Ct2 = Ct1, Ct1 was put in Ct2 ...
// First, find y in L.y = Ct1, put y in Ct2
Ct2[0] /= A[0][1];
for (i = 1; i <= count_cells + 1; i++)
Ct2[i] = (Ct2[i] - A[i][0] * Ct2[i - 1]) / A[i][1];
// Now obtain Ct2 in U.Ct2 = 'y' ...
for (i = count_cells; i >= 0; i--)
Ct2[i] -= A[i][2] * Ct2[i + 1];
}
// Moles transported by concentration gradient from cell [i] to [i + 1] go in tot1,
// moles by stagnant exchange from cell [i] to [i + c1] go in tot_stag
// Correct for electro-neutrality...
for (i = ifirst; i <= ilast + 1; i++)
{
if (heat_nmix && cp == comp - 1)
{
l_tk_x2[i] = (Ct2[i + 1] + Ct2[i]) / 2;
cell_data[i].temp = Ct2[i] - 273.15;
sptr1 = Utilities::Rxn_find(Rxn_solution_map, i);
sptr1->Set_tc(Ct2[i] - 273.15);
if (stagnant && i > 0 && i < c1 && mixf_stag[i][cp])
{
i1 = i + c1;
cell_data[i1].temp = Ct2[i1] - 273.15;
sptr2 = Utilities::Rxn_find(Rxn_solution_map, i1);
sptr2->Set_tc(Ct2[i1] - 273.15);
}
continue;
}
else if (i == ilast + 1 && (!stagnant || !mixf_stag[i][cp]))
continue;
if (!cp)
{
ct[i].J_ij_sum = 0;
ct[i].Dz2c_stag = 0;
if (i < ilast + 1)
current_cells[i].ele = current_cells[i].dif = 0;
}
for (i0 = stagnant; i0 >= 0; i0--)
{
i1 = (i0 == 0 ? i + 1 : i == 0 ? c2 : i == c1 ? cc1 : i + c1);
if (Utilities::Rxn_find(Rxn_solution_map, i1) == NULL)
continue;
ct[i].J_ij[cp].name = sol_D[i].spec[cp].name;
ct[i].J_ij[cp].charge = ct[i].v_m[cp].z;
grad = (Ct2[i1] - Ct2[i])/* * ct[i].v_m[cp].D*/; // .D has the d{lg(gamma)} / d{lg(molal)} correction
if (!i0)
{
ct[i].J_ij[cp].tot1 = -mixf[i][cp] * grad;
std::map<int, std::map<std::string, class J_ij_save> >::iterator
cell_iter = cell_J_ij.find(i);
if (cell_iter == cell_J_ij.end())
continue;
std::map< std::string, class J_ij_save >::iterator
s_iter = cell_iter->second.find(sol_D[1].spec[cp].name);
s_iter->second.flux_c = ct[i].J_ij[cp].tot1;
s_iter->second.flux_t = ct[i].J_ij[cp].tot1;
}
else
ct[i].J_ij[cp].tot_stag = -mixf_stag[i][cp] * grad;
if (ct[i].v_m[cp].z)
{
if (i == 0)
ct[i].v_m[cp].zc = ct[i].v_m[cp].z * Ct2[i1];
else if (i == ilast)
{
if (!i0)
ct[i].v_m[cp].zc = ct[i].v_m[cp].z * Ct2[i];
else
ct[i].v_m[cp].zc = ct[i].v_m[cp].z * Ct2[i1];
}
else
ct[i].v_m[cp].zc = ct[i].v_m[cp].z * (Ct2[i] + Ct2[i1]) / 2;
if (i0)
{
mixf_stag[i][cp] *= ct[i].v_m[cp].zc;
ct[i].J_ij_sum += ct[i].v_m[cp].z * ct[i].J_ij[cp].tot_stag;
ct[i].Dz2c_stag -= ct[i].v_m[cp].z * mixf_stag[i][cp];
}
else if (i < ilast + 1)
{
mixf[i][cp] *= ct[i].v_m[cp].zc;
current_cells[i].dif += ct[i].v_m[cp].z * ct[i].J_ij[cp].tot1;
current_cells[i].ele -= ct[i].v_m[cp].z * mixf[i][cp];
}
}
}
}
// define element list
if (cp == 0)
dif_els_names.clear();
if (heat_nmix && cp == comp - 1)
{
comp -= 1;
break;
}
char * temp_name = string_duplicate(ct[1].J_ij[cp].name);
const char* cptr = temp_name;
count_elts = 0;
get_elts_in_species(&cptr, 1);
free_check_null(temp_name);
for (int k = 0; k < count_elts; k++)
{
if (!strcmp(elt_list[k].elt->name, "X")) continue;
dif_els_names.insert(elt_list[k].elt->name);
}
}
count_m_s = (int) dif_els_names.size();
sum_R = sum_Rd = 0;
for (i = ifirst; i <= ilast; i++)
{
ct[i].J_ij_count_spec = comp;
current_cells[i].R = l_tk_x2[i] / (current_cells[i].ele * F_Re3);
if (dV_dcell && !fix_current)
{
sum_R += current_cells[i].R;
if (i > 1)
sum_Rd += (current_cells[0].dif - current_cells[i].dif) * current_cells[i].R;
}
}
if (dV_dcell || fix_current)
{
if (fix_current)
{
int sign = (current_x >= 0 ? 1 : -1);
current_x = sign * fix_current * DDt / F_C_MOL;
j_0e = current_x - current_cells[ifirst].dif;
}
else
{
j_0e = (dV_dcell * count_cells - sum_Rd) / sum_R;
current_x = j_0e + current_cells[ifirst].dif;
}
}
else
{
current_x = 1e-10 / F_C_MOL;
cell_data[ifirst].potV = 0e-8;
j_0e = current_x - current_cells[ifirst].dif;
}
dVc = j_0e * current_cells[ifirst].R;
cell_data[ifirst + 1].potV = cell_data[ifirst].potV + dVc;
for (i = ifirst + 1; i <= ilast; i++)
{
dVc = current_cells[i].R * (current_x - current_cells[i].dif);
//if (((dV_dcell && (dVc * j_0e > 0)) ||
// (dV_dcell > 0 && (cell_data[i].potV + dVc) > (cell_data[count_cells + 1].potV)) ||
// (dV_dcell < 0 && (cell_data[i].potV + dVc) < (cell_data[count_cells + 1].potV))))
if ((dV_dcell && (dVc * j_0e > 0)) ||
((dV_dcell > 0) && ((cell_data[i].potV + dVc) > cell_data[count_cells + 1].potV)) ||
((dV_dcell < 0) && ((cell_data[i].potV + dVc) < cell_data[count_cells + 1].potV)))
{
dVc = (cell_data[count_cells + 1].potV - cell_data[i].potV) / ((double)count_cells + 1 - (double)i);
}
cell_data[i + 1].potV = cell_data[i].potV + dVc;
}
//if (!dV_dcell || fix_current)
//{
// dVc = current_cells[i].R * (current_x - current_cells[i].dif);
// cell_data[i + 1].potV = cell_data[i].potV + dVc;
//}
for (cp = 0; cp < comp; cp++)
{
if (!ct[ifirst].v_m[cp].z)
continue;
for (i = ifirst; i <= ilast + stagnant; i++)
{
if (i < ilast + 1)
{
dVc = (cell_data[i + 1].potV - cell_data[i].potV) * F_Re3 / l_tk_x2[i];
ct[i].J_ij[cp].tot1 -= mixf[i][cp] * dVc;
std::map<int, std::map<std::string, class J_ij_save> >::iterator
cell_iter = cell_J_ij.find(i);
std::map< std::string, class J_ij_save >::iterator
s_iter = cell_iter->second.find(sol_D[1].spec[cp].name);
s_iter->second.flux_t = ct[i].J_ij[cp].tot1;
}
if (stagnant && ct[i].Dz2c_stag)
{
ct[i].J_ij[cp].tot_stag += (mixf_stag[i][cp] * ct[i].J_ij_sum / ct[i].Dz2c_stag);
}
}
}
current_A = current_x / DDt * F_C_MOL;
for (i = ifirst; i <= ilast + stagnant + (bcon_last == 2 ? 1 : 0); i++)
{
if (i <= ilast + 1)
{
// preserve the potentials...
sptr1 = Utilities::Rxn_find(Rxn_solution_map, i);
sptr1->Set_potV(cell_data[i].potV);
}
// Translate transport of the solute species into master species...
ct[i].count_m_s = count_m_s;
if (ct[i].m_s_size == 0 && ct[i].m_s != NULL)
ct[i].m_s = (class M_S *) free_check_null(ct[i].m_s);
if (ct[i].m_s == NULL)
{
ct[i].m_s = (class M_S *) PHRQ_malloc((count_m_s + 5) * sizeof(class M_S));
ct[i].m_s_size = count_m_s + 5;
}
else if (count_m_s > ct[i].m_s_size)
{
ct[i].m_s = (class M_S *) PHRQ_realloc(ct[i].m_s, (count_m_s + 5) * sizeof(class M_S));
ct[i].m_s_size = count_m_s + 5;
}
if (ct[i].m_s == NULL)
malloc_error();
std::set <std::string> ::iterator it = dif_els_names.begin();
for (i1 = 0; i1 < count_m_s; i1++)
{
ct[i].m_s[i1].tot1 = ct[i].m_s[i1].tot2 = ct[i].m_s[i1].tot_stag = 0.0;
ct[i].m_s[i1].charge = 0.0;
ct[i].m_s[i1].name = (*it).c_str();
it++;
}
fill_m_s(ct[i].J_ij, ct[i].J_ij_count_spec, i, stagnant);
}
/*
* 3. find the solutions in the column, add or subtract the moles...
*/
cxxNameDouble::iterator kit;
int if1, il1, incr;
for (cp = 0; cp < count_m_s; cp++)
{
if (!dV_dcell || dV_dcell * ct[1].m_s[cp].charge <= 0)
{
if1 = ifirst; il1 = ilast + 1; incr = 1;
}
else
{
if1 = ilast; il1 = ifirst - 1; incr = -1;
}
for (icell = if1; icell != il1; icell += incr)
{
min_mol = min_dif_M * ct[icell].kgw;
if (min_mol < 1e-13)
min_mol = 1e-13;
dum1 = dum2 = 0;
sptr1 = Utilities::Rxn_find(Rxn_solution_map, icell);
sptr2 = Utilities::Rxn_find(Rxn_solution_map, icell + 1);
if (stagnant && mixf_stag[icell][cp])
{
i1 = (icell == 0 ? c1 + 1 : icell == c1 ? cc1 : icell + c1);
sptr_stag = Utilities::Rxn_find(Rxn_solution_map, i1);
}
else
sptr_stag = NULL;
//if (!cp)
//{
// ct[icell].J_ij_sum = ct[icell + 1].J_ij_sum = 0.0;
// if (sptr_stag)
// ct[i1].J_ij_sum = 0.0;
//}
if (!strcmp(ct[icell].m_s[cp].name, "H"))
{
dummy = ct[icell].m_s[cp].tot1;
if (dV_dcell || fix_current || (icell > 0 && icell <= ilast))
sptr1->Set_total_h(sptr1->Get_total_h() - dummy);
if (dV_dcell || fix_current || (icell >= 0 && icell < ilast) || (icell == ilast && bcon_last == 2))
sptr2->Set_total_h(sptr2->Get_total_h() + dummy);
if (sptr_stag)
{
dummy = ct[icell].m_s[cp].tot_stag;
if (dV_dcell || fix_current || (icell > 0 && icell <= ilast))
sptr1->Set_total_h(sptr1->Get_total_h() - dummy);
if (icell == c)
{
// mix the boundary solution with the stagnant column end-cell
dummy += ct[icell + 1].m_s[cp].tot_stag;
if (dV_dcell || fix_current)
sptr2->Set_total_h(sptr2->Get_total_h() - ct[icell + 1].m_s[cp].tot_stag);
}
sptr_stag->Set_total_h(sptr_stag->Get_total_h() + dummy);
}
continue;
}
if (!strcmp(ct[icell].m_s[cp].name, "O"))
{
dummy = ct[icell].m_s[cp].tot1;
if (dV_dcell || fix_current || (icell > 0 && icell <= ilast))
sptr1->Set_total_o(sptr1->Get_total_o() - dummy);
if (dV_dcell || fix_current || (icell >= 0 && icell < ilast) || (icell == ilast && bcon_last == 2))
sptr2->Set_total_o(sptr2->Get_total_o() + dummy);
if (sptr_stag)
{
dummy = ct[icell].m_s[cp].tot_stag;
if (dV_dcell || fix_current || (icell > 0 && icell <= ilast))
sptr1->Set_total_o(sptr1->Get_total_o() - dummy);
if (icell == c)
{
dummy += ct[icell + 1].m_s[cp].tot_stag;
if (dV_dcell || fix_current)
sptr2->Set_total_o(sptr2->Get_total_o() - ct[icell + 1].m_s[cp].tot_stag);
}
sptr_stag->Set_total_o(sptr_stag->Get_total_o() + dummy);
}
//if (cp == count_m_s - 1) // transport the charge imbalance
//{
// sptr1->Set_cb(sptr1->Get_cb() + ct[icell].J_ij_sum);
// sptr2->Set_cb(sptr2->Get_cb() + ct[icell + 1].J_ij_sum);
// if (sptr_stag)
// sptr_stag->Set_cb(sptr_stag->Get_cb() + ct[i1].J_ij_sum);
//}
continue;
}
dum1 = sptr1->Get_totals()[ct[icell].m_s[cp].name];
dum2 = sptr2->Get_totals()[ct[icell].m_s[cp].name];
if (sptr_stag)
dum_stag = sptr_stag->Get_totals()[ct[icell].m_s[cp].name];
// check for negative moles, add moles from other redox states and the donnan layer when necessary and available...
if (dum1 - ct[icell].m_s[cp].tot1 - ct[icell].m_s[cp].tot_stag < min_mol &&
(dV_dcell || fix_current || (icell > 0 && icell <= ilast)))
{
dum1 = moles_from_redox_states(sptr1, ct[icell].m_s[cp].name);
if (ct[icell].dl_s > 1e-8)
{
cxxSurface * s_ptr = Utilities::Rxn_find(Rxn_surface_map, icell);
if (s_ptr)
{
dum1 += moles_from_donnan_layer(s_ptr, ct[icell].m_s[cp].name, ct[icell].m_s[cp].tot1 + ct[icell].m_s[cp].tot_stag - dum1 + min_mol);
}
}
sptr1->Get_totals()[ct[icell].m_s[cp].name] = dum1;
if (dum1 - ct[icell].m_s[cp].tot1 - ct[icell].m_s[cp].tot_stag < min_mol)
ct[icell].m_s[cp].tot1 = dum1 - ct[icell].m_s[cp].tot_stag - min_mol;
}
// check for negative moles, in the other cell...
ct[icell].m_s[cp].tot2 = ct[icell].m_s[cp].tot1;
dum = 0;
if (icell == c && sptr_stag && ct[c1].m_s[cp].tot_stag)
dum = ct[c1].m_s[cp].tot_stag;
if (dum2 + ct[icell].m_s[cp].tot2 - dum < min_mol &&
(dV_dcell || fix_current || (icell >= 0 && icell < ilast) || (icell == ilast && bcon_last == 2)))
{
dum2 = moles_from_redox_states(sptr2, ct[icell].m_s[cp].name);
if (ct[icell + 1].dl_s > 1e-8)
{
cxxSurface * s_ptr = Utilities::Rxn_find(Rxn_surface_map, icell + 1);
if (s_ptr)
{
dum2 += moles_from_donnan_layer(s_ptr, ct[icell].m_s[cp].name, -(ct[icell].m_s[cp].tot2 + dum2 - min_mol));
}
}
sptr2->Get_totals()[ct[icell].m_s[cp].name] = dum2;
if (dum2 + ct[icell].m_s[cp].tot2 - dum < min_mol)
ct[icell].m_s[cp].tot2 = -dum2 + min_mol + dum;
}
if (fabs(ct[icell].m_s[cp].tot2) < fabs(ct[icell].m_s[cp].tot1))
ct[icell].m_s[cp].tot1 = ct[icell].m_s[cp].tot2;
if (dV_dcell || fix_current || (icell > 0 && icell <= ilast))
{
dum = ct[icell].m_s[cp].tot1;
if (stagnant)
dum += ct[icell].m_s[cp].tot_stag;
dum1 -= dum;
sptr1->Get_totals()[ct[icell].m_s[cp].name] = (dum1 > 0 ? dum1 : min_mol);
if (dum1 < 0)
{
dum += dum1 - min_mol;
if ((it1 = neg_moles.find(icell)) != neg_moles.end()
&& (it2 = (els = it1->second).find(ct[icell].m_s[cp].name)) != els.end())
dum1 += it2->second;
els.clear();
els.insert(std::make_pair(ct[icell].m_s[cp].name, dum1));
neg_moles.erase(icell);
neg_moles.insert(std::make_pair(icell, els));
}
//ct[icell].J_ij_sum -= dum * ct[icell].m_s[cp].charge;
}
if (dV_dcell || fix_current || (icell >= 0 && icell < ilast) || (icell == ilast && bcon_last == 2))
{
dum = ct[icell].m_s[cp].tot1;
if (stagnant && icell == c && (dV_dcell || fix_current))
dum -= ct[c1].m_s[cp].tot_stag;
dum2 += dum;
sptr2->Get_totals()[ct[icell].m_s[cp].name] = (dum2 > 0 ? dum2 : min_mol);
if (dum2 < 0)
{
dum -= dum2 - min_mol;
if ((it1 = neg_moles.find(icell + 1)) != neg_moles.end()
&& (it2 = (els = it1->second).find(ct[icell].m_s[cp].name)) != els.end())
dum2 += it2->second;
els.clear();
els.insert(std::make_pair(ct[icell].m_s[cp].name, dum2));
neg_moles.erase(icell + 1);
neg_moles.insert(std::make_pair(icell + 1, els));
}
//ct[icell + 1].J_ij_sum += dum * ct[icell].m_s[cp].charge;
}
if (sptr_stag)
{
dum = ct[icell].m_s[cp].tot_stag;
if (icell == c)
dum += ct[c1].m_s[cp].tot_stag;
if (dum_stag + dum < 0)
{
dum_stag = moles_from_redox_states(sptr_stag, ct[icell].m_s[cp].name);
if (ct[i1].dl_s)
{
cxxSurface * s_ptr = Utilities::Rxn_find(Rxn_surface_map, i1);
if (s_ptr)
{
dum_stag += moles_from_donnan_layer(s_ptr, ct[icell].m_s[cp].name, -(dum + dum_stag - min_mol));
}
sptr_stag->Get_totals()[ct[icell].m_s[cp].name] = dum_stag;
}
}
dum_stag += dum;
sptr_stag->Get_totals()[ct[icell].m_s[cp].name] = (dum_stag > 0 ? dum_stag : min_mol);
if (dum_stag < 0)
{
dum -= dum_stag - min_mol;
if ((it1 = neg_moles.find(i1)) != neg_moles.end()
&& (it2 = (els = it1->second).find(ct[icell].m_s[cp].name)) != els.end())
dum_stag += it2->second;
els.clear();
els.insert(std::make_pair(ct[icell].m_s[cp].name, dum_stag));
neg_moles.erase(i1);
neg_moles.insert(std::make_pair(i1, els));
}
//ct[i1].J_ij_sum += dum * ct[icell].m_s[cp].charge;
}
// reduce oscillations in the column-boundary cells, but not for H and O, and current_A is not adjusted...
if ((dV_dcell/* || fix_current*/) && icell == il1 - incr && dV_dcell * ct[0].m_s[cp].charge < 0 && strcmp(ct[0].m_s[cp].name, "H") && strcmp(ct[0].m_s[cp].name, "O") && c > 3 && mixrun > 1)
{
dummy = Utilities::Rxn_find(Rxn_solution_map, 0)->Get_totals()[ct[0].m_s[cp].name] / ct[0].kgw * (1 - ct[0].dl_s);
if (dummy > 1e-6)
{
sptr1 = Utilities::Rxn_find(Rxn_solution_map, 1);
sptr2 = Utilities::Rxn_find(Rxn_solution_map, 2);
dum1 = sptr1->Get_totals()[ct[0].m_s[cp].name] / ct[1].kgw * (1 - ct[1].dl_s) - dummy;
dum2 = sptr2->Get_totals()[ct[0].m_s[cp].name] / ct[2].kgw * (1 - ct[2].dl_s) - dummy;
if (dum1 / dum2 < 0 || dum1 / dum2 > 1)
{
dum = cell_data[1].mid_cell_x / cell_data[2].mid_cell_x;
//ct[1].J_ij_sum -= sptr1->Get_totals()[ct[0].m_s[cp].name] * ct[1].m_s[cp].charge;
dum1 = (dummy + dum * dum2) * ct[1].kgw / (1 - ct[1].dl_s);
sptr1->Get_totals()[ct[0].m_s[cp].name] = dum1;
//ct[1].J_ij_sum += dum1 * ct[1].m_s[cp].charge;
}
}
dummy = Utilities::Rxn_find(Rxn_solution_map, c1)->Get_totals()[ct[0].m_s[cp].name] / ct[c1].kgw * (1 - ct[c1].dl_s);
if (dummy > 1e-6)
{
sptr1 = Utilities::Rxn_find(Rxn_solution_map, c);
sptr2 = Utilities::Rxn_find(Rxn_solution_map, c_1);
dum1 = sptr1->Get_totals()[ct[0].m_s[cp].name] / ct[c].kgw * (1 - ct[c].dl_s) - dummy;
dum2 = sptr2->Get_totals()[ct[0].m_s[cp].name] / ct[c_1].kgw * (1 - ct[c_1].dl_s) - dummy;
if (dum1 / dum2 < 0 || dum1 / dum2 > 1)
{
dum = (cell_data[c].mid_cell_x - cell_data[c_1].mid_cell_x) /
(cell_data[c1].mid_cell_x - cell_data[c_1].mid_cell_x);
//ct[c].J_ij_sum -= sptr1->Get_totals()[ct[0].m_s[cp].name] * ct[c].m_s[cp].charge;
dum1 = (dummy + (1 - dum) * dum2) * ct[c].kgw / (1 - ct[c].dl_s);
sptr1->Get_totals()[ct[0].m_s[cp].name] = dum1;
//ct[c].J_ij_sum += dum1 * ct[c].m_s[cp].charge;
}
}
}
//if (cp == count_m_s - 1)
//{
// sptr1->Set_cb(sptr1->Get_cb() + ct[icell].J_ij_sum);
// sptr2->Set_cb(sptr2->Get_cb() + ct[icell + 1].J_ij_sum);
// if (sptr_stag)
// sptr_stag->Set_cb(sptr_stag->Get_cb() + ct[i1].J_ij_sum);
//}
}
}
return;
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
moles_from_redox_states(cxxSolution *sptr, const char *name)
/* ---------------------------------------------------------------------- */
{
int length, length2;
cxxNameDouble::iterator kit;
LDBLE dum = 0;
length = (int)strlen(name);
for (kit = sptr->Get_totals().begin(); kit != sptr->Get_totals().end(); kit++)
{
length2 = (int)(size_t)strcspn(kit->first.c_str(), "(");
if (length == length2 && !strncmp(name, kit->first.c_str(), length2))
{
dum += kit->second; kit->second = 0;
}
}
return(dum);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
add_MCD_moles(LDBLE moles, LDBLE min_mol, int i, cxxSolution *sptr, const char *name)
/* ---------------------------------------------------------------------- */
{
cxxNameDouble::iterator kit;
std::map<int, std::map<std::string, double> > ::iterator it1;
std::map<std::string, double> ::iterator it2;
LDBLE dum = sptr->Get_totals()[name];
if (!dum)
dum = moles_from_redox_states(sptr, name);
if ((it1 = neg_moles.find(i)) != neg_moles.end()
&& (it2 = (els = it1->second).find(name)) != els.end())
{
dum += it2->second;
neg_moles.erase(it1);
els.erase(it2);
neg_moles.insert(std::make_pair(i, els));
}
dum += moles;
if (dum < -min_mol)
{
if (ct[i].dl_s)
{
cxxSurface *su_ptr = Utilities::Rxn_find(Rxn_surface_map, i);
if (su_ptr != NULL)
dum += moles_from_donnan_layer(su_ptr, name, -dum + min_mol);
}
}
sptr->Get_totals()[name] = (dum > 0 ? dum : 0e-16);
if (dum < -min_mol)
{
els.insert(std::make_pair(name, dum));
if ((it1 = neg_moles.find(i)) != neg_moles.end())
neg_moles.erase(it1);
neg_moles.insert(std::make_pair(i, els));
}
return(dum);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
moles_from_donnan_layer(cxxSurface *sptr, const char *name, LDBLE moles_needed)
/* ---------------------------------------------------------------------- */
{
cxxNameDouble::iterator kit;
LDBLE dum = 0;
cxxSurfaceCharge * charge_ptr = NULL;
for (size_t j = 0; j < sptr->Get_surface_charges().size(); j++)
{
if (sptr->Get_dl_type() == cxxSurface::DONNAN_DL)
{
charge_ptr = &(sptr->Get_surface_charges()[j]);
for (kit = charge_ptr->Get_diffuse_layer_totals().begin(); kit != charge_ptr->Get_diffuse_layer_totals().end(); kit++)
{
if (strcmp(kit->first.c_str(), "H") == 0 || strcmp(kit->first.c_str(), "O") == 0)
continue;
if (strcmp(kit->first.c_str(), name) == 0)
{
if (kit->second > moles_needed)
{
dum += moles_needed; kit->second -= moles_needed;
}
else
{
dum += kit->second; kit->second = 0;
}
}
}
}
}
return(dum);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
multi_D(LDBLE DDt, int mobile_cell, int stagnant)
/* ---------------------------------------------------------------------- */
{
/*
* 1. determine mole transfer (mol/s) of solute species for the interface between 2 cells.
* 2. sum up as mole transfer of master_species
* 3. add moles of master_species to the 2 cells
* NOTE. Define the water content of stagnant cells relative to the
* mobile cell (with, for example, 1 kg water)
* Define properties of each interface only 1 time with MIX.
* If an electrical field is applied (dV_dcell != 0), the currents j_i = current_cells[i].ele + dif (C * s)
are calculated for all cells. Then the ele part from cell 0 -> 1 is calculated:
current_x = (j_0d + j_0e) = j_0 = j_1 = ... = j_i
j_0e * (R0 + R1 + ...) + (j_0d - j_1d) * R1 + ... + (j_0d - j_id) * Ri = Vtot
or
j_0e * Sum_R + Sum_Rd = Vtot.
Ri = dV_dcell / j_ie, the relative cell resistance.
Solve j_0e, find (V1 - V0) = j_0e * R0. j_1e = current_x - j_1d, find (V2 - V1) = j_1e * R1, etc.
*/
int icell, jcell, i, l, n, length, length2, il_calcs;
int i1, loop_f_c;
int first_c, last_c, last_c2 = 0;
char token[MAX_LENGTH];
LDBLE mixf, temp;
LDBLE dVtemp = 0.0;
if (dV_dcell && stagnant)
{
dVtemp = dV_dcell;
dV_dcell = 0;
}
cell_J_ij.clear();
icell = jcell = -1;
first_c = last_c = -1;
il_calcs = -1;
current_x = sum_R = sum_Rd = 0.0;
if (dV_dcell)
find_current = loop_f_c = 1; // calculate J_ij once for all cells, loop to dV_dcell2.
else
find_current = loop_f_c = 0;
for (int f_c = 0; f_c <= loop_f_c; f_c++)
{
for (n = 0; n <= (stagnant ? stag_data.count_stag : 0); n++) // allow for stagnant cell mixing with higher cells in the layer
{
icell = mobile_cell + 1 + n * count_cells;
if (stagnant)
{
if (n == 0)
icell -= 1;
else if (mobile_cell == 0 || mobile_cell == count_cells + 1)
continue;
/*
* find the mix ptr for icell and go along the cells that mix with it
*/
use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, icell));
if (use.Get_mix_ptr() == NULL)
{
if (first_c < 0)
first_c = icell;
if (last_c2 < icell)
last_c2 = icell;
continue;
}
first_c = 0;
last_c = (int) (use.Get_mix_ptr()->Get_mixComps().size() - 1);
}
else
{ /* regular column... */
if (bcon_first == 1)
first_c = 0;
else
first_c = 1;
if (bcon_last == 1)
last_c = count_cells;
else
last_c = count_cells - 1;
last_c2 = (dV_dcell ? count_cells + 1: count_cells);
}
for (i = first_c; i <= last_c; i++)
{
if (stagnant)
{
std::vector<int> n_solution;
std::vector<LDBLE> fraction;
(use.Get_mix_ptr())->Vectorize(n_solution, fraction);
jcell = n_solution[i];
if (jcell <= icell)
continue;
if (jcell >= all_cells || jcell < 0)
{
error_string = sformatf(
"Multi_D asked for diffusion from cell %d to %d, %d is beyond the number of cells", icell, jcell, jcell);
error_msg(error_string, CONTINUE);
}
if (jcell > last_c2)
last_c2 = jcell;
mixf = (nmix ? fraction[i] / nmix : fraction[i]);
}
else
{ /* regular column... */
icell = i;
jcell = i + 1;
mixf = 1.0;
}
if (dV_dcell)
{
tk_x2 = (sol_D[icell].tk_x + sol_D[jcell].tk_x) / 2;
}
/*
* 1. obtain J_ij...
*/
il_calcs = (int) find_J(icell, jcell, mixf, DDt, stagnant);
if (find_current)
{
if (i < last_c)
continue;
else
{
LDBLE dVc, j_0e;
// distribute dV_dcell according to relative resistance, calculate current_x if not fixed
j_0e = (dV_dcell * count_cells - sum_Rd) / sum_R;
current_x = j_0e + current_cells[0].dif;
if (fix_current)
{
int sign = (current_x >= 0 ? 1 : -1);
current_x = sign * fix_current / F_C_MOL;
j_0e = current_x - current_cells[0].dif;
}
dVc = j_0e * current_cells[0].R;
cell_data[1].potV = cell_data[0].potV + dVc;
for (i1 = 1; i1 < count_cells; i1++)
{
dVc = current_cells[i1].R * (current_x - current_cells[i1].dif);
cell_data[i1 + 1].potV = cell_data[i1].potV + dVc;
}
if (fix_current)
{
dVc = current_cells[i1].R * (current_x - current_cells[i1].dif);
cell_data[i1 + 1].potV = cell_data[i1].potV + dVc;
}
find_current = 0;
continue;
}
}
if (!ct[icell].J_ij_count_spec)
continue;
/*
* 2. sum up the primary or secondary master_species
*/
if (!il_calcs)
{
tot1_h = tot1_o = tot2_h = tot2_o = 0.0;
m_s = (class M_S *) free_check_null(m_s);
count_m_s = (ct[icell].J_ij_count_spec < count_moles_added ?
ct[icell].J_ij_count_spec : count_moles_added);
m_s = (class M_S *) PHRQ_malloc((size_t) count_m_s *
sizeof(class M_S));
if (m_s == NULL)
malloc_error();
for (i1 = 0; i1 < count_m_s; i1++)
{
m_s[i1].name = NULL;
m_s[i1].tot1 = 0;
m_s[i1].tot2 = 0;
}
count_m_s = 0;
}
fill_m_s(ct[icell].J_ij, ct[icell].J_ij_count_spec, icell, stagnant);
/*
* 3. find the solutions, add or subtract the moles...
*/
if (dV_dcell || (icell != 0 && icell != count_cells + 1))
{
use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, icell));
use.Get_solution_ptr()->Set_total_h(use.Get_solution_ptr()->Get_total_h() - tot1_h);
use.Get_solution_ptr()->Set_total_o(use.Get_solution_ptr()->Get_total_o() - tot1_o);
if (dV_dcell && (icell > 0 || fix_current))
{
use.Get_solution_ptr()->Set_potV(cell_data[icell].potV);
}
for (l = 0; l < count_m_s; l++)
{
length = (int) strlen(m_s[l].name);
cxxNameDouble::iterator it;
for (it = use.Get_solution_ptr()->Get_totals().begin();
it != use.Get_solution_ptr()->Get_totals().end(); it++)
{
length2 =
(int) (size_t) strcspn(it->first.c_str(), "(");
if (strncmp(m_s[l].name, it->first.c_str(), length) == 0 && length == length2)
{
it->second -= m_s[l].tot1;
break;
}
}
if (it == use.Get_solution_ptr()->Get_totals().end())
{
use.Get_solution_ptr()->Get_totals()[m_s[l].name] = -m_s[l].tot1;
}
}
}
if (dV_dcell || jcell != count_cells + 1)
{
use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, jcell));
dummy = use.Get_solution_ptr()->Get_total_h();
use.Get_solution_ptr()->Set_total_h(dummy + tot2_h);
dummy = use.Get_solution_ptr()->Get_total_o();
use.Get_solution_ptr()->Set_total_o(dummy + tot2_o);
if (icell == count_cells && fix_current && !stagnant)
{
use.Get_solution_ptr()->Set_potV(cell_data[jcell].potV);
}
for (l = 0; l < count_m_s; l++)
{
length = (int) strlen(m_s[l].name);
cxxNameDouble::iterator it;
for (it = use.Get_solution_ptr()->Get_totals().begin();
it != use.Get_solution_ptr()->Get_totals().end(); it++)
{
length2 = (int) (size_t) strcspn(it->first.c_str(), "(");
if (strncmp(m_s[l].name, it->first.c_str(), length) == 0 && length == length2)
{
it->second += m_s[l].tot2;
break;
}
}
if (it == use.Get_solution_ptr()->Get_totals().end())
{
use.Get_solution_ptr()->Get_totals()[m_s[l].name] = m_s[l].tot2;
}
}
}
}
}
}
// check for negative conc's...
//if (stagnant)
// first_c = mobile_cell; // allow for stagnant cell mixing with boundary cell 0
for (i = first_c; i <= last_c2; i++)
{
//if (stagnant && i > first_c && i <= count_cells + first_c)
// continue;
use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i));
if (!use.Get_solution_ptr())
continue;
cxxNameDouble::iterator it;
for (it = use.Get_solution_ptr()->Get_totals().begin();
it != use.Get_solution_ptr()->Get_totals().end(); it++)
{
if (strcmp(it->first.c_str(), "H(0)") == 0)
continue;
if (strcmp(it->first.c_str(), "O(0)") == 0)
continue;
LDBLE moles = it->second;
if (moles < 0 && ct[i].dl_s)
{
use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, i));
cxxSurface * s_ptr = use.Get_surface_ptr();
cxxSurfaceCharge * charge_ptr = NULL;
cxxNameDouble::iterator jit;
for (size_t j = 0; j < s_ptr->Get_surface_charges().size(); j++)
{
if (s_ptr->Get_dl_type() == cxxSurface::DONNAN_DL)
{
charge_ptr = &(s_ptr->Get_surface_charges()[j]);
for (jit = charge_ptr->Get_diffuse_layer_totals().begin(); jit != charge_ptr->Get_diffuse_layer_totals().end(); jit++)
{
if (strcmp(jit->first.c_str(), "H") == 0 || strcmp(jit->first.c_str(), "O") == 0)
continue;
if (strcmp(jit->first.c_str(), it->first.c_str()) == 0)
{
moles += jit->second;
it->second += jit->second;
jit->second = 0;
}
}
}
}
}
if (moles < 0)
{
temp = moles;
it->second = 0;
/* see if other redox states have more moles... */
length = (int) strlen(it->first.c_str());
cxxNameDouble::iterator kit;
for (kit = use.Get_solution_ptr()->Get_totals().begin();
kit != use.Get_solution_ptr()->Get_totals().end(); kit++)
{
length2 = (int) (size_t) strcspn(kit->first.c_str(), "(");
if (!strncmp(it->first.c_str(), kit->first.c_str(), length2))
{
temp += kit->second;
if (temp < 0)
{
kit->second = 0;
}
else
{
kit->second = temp;
break;
}
}
}
if (temp < -1e-12)
{
snprintf(token, sizeof(token),
"Negative concentration in MCD: added %.4e moles %s in cell %d",
(double)-temp, it->first.c_str(), i);
warning_msg(token);
for (i1 = 0; i1 < count_moles_added; i1++)
{
if (moles_added[i1].name && !strcmp(moles_added[i1].name, it->first.c_str()))
{
moles_added[i1].moles -= temp;
break;
}
else if (!moles_added[i1].moles)
{
moles_added[i1].name = string_duplicate(it->first.c_str());
moles_added[i1].moles -= temp;
break;
}
}
}
}
}
}
m_s = (class M_S *) free_check_null(m_s);
for (i = first_c; i < last_c2; i++)
{
if (stagnant && i > first_c && i <= count_cells + first_c)
continue;
ct[i].J_ij = (class J_ij *) free_check_null(ct[i].J_ij);
if (il_calcs)
ct[i].J_ij_il = (class J_ij *) free_check_null(ct[i].J_ij_il);
ct[i].v_m = (struct V_M *) free_check_null(ct[i].v_m);
}
if (dVtemp && stagnant)
{
dV_dcell = dVtemp;
}
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
fill_m_s(class J_ij *l_J_ij, int l_J_ij_count_spec, int icell, int stagnant)
/* ---------------------------------------------------------------------- */
{
/* sum up the primary or secondary master_species from solute species
* H and O go in tot1&2_h and tot1&2_o
*/
int j, k, l;
LDBLE fraction;
const char* cptr;
for (j = 0; j < l_J_ij_count_spec; j++)
{
{
char * temp_name = string_duplicate(l_J_ij[j].name);
cptr = temp_name;
count_elts = 0;
get_elts_in_species(&cptr, 1);
free_check_null(temp_name);
}
if (implicit && stagnant < 2)
{
for (k = 0; k < count_elts; k++)
{
for (l = 0; l < count_m_s; l++)
{
if (strcmp(ct[icell].m_s[l].name, elt_list[k].elt->name) == 0)
{
fraction = fabs((double)elt_list[k].coef * l_J_ij[j].tot1) + fabs(ct[icell].m_s[l].tot1);
if (fraction)
fraction = fabs((double)elt_list[k].coef * l_J_ij[j].tot1) / fraction;
else
fraction = 1;
ct[icell].m_s[l].tot1 += elt_list[k].coef * l_J_ij[j].tot1;
ct[icell].m_s[l].charge *= (1 - fraction);
ct[icell].m_s[l].charge += fraction * l_J_ij[j].charge;
if (stagnant)
ct[icell].m_s[l].tot_stag += elt_list[k].coef * l_J_ij[j].tot_stag;
break;
}
}
}
}
else
{
for (k = 0; k < count_elts; k++)
{
if (strcmp(elt_list[k].elt->name, "X") == 0)
continue;
if (strcmp(elt_list[k].elt->name, "H") == 0)
{
tot1_h += elt_list[k].coef * l_J_ij[j].tot1;
tot2_h += elt_list[k].coef * l_J_ij[j].tot2;
}
else if (strcmp(elt_list[k].elt->name, "O") == 0)
{
tot1_o += elt_list[k].coef * l_J_ij[j].tot1;
tot2_o += elt_list[k].coef * l_J_ij[j].tot2;
}
else
{
for (l = 0; l < count_m_s; l++)
{
if (strcmp(m_s[l].name, elt_list[k].elt->name) == 0)
{
m_s[l].tot1 += elt_list[k].coef * l_J_ij[j].tot1;
m_s[l].tot2 += elt_list[k].coef * l_J_ij[j].tot2;
break;
}
}
if (l == count_m_s)
{
m_s[l].name = elt_list[k].elt->name;
m_s[l].tot1 = elt_list[k].coef * l_J_ij[j].tot1;
m_s[l].tot2 = elt_list[k].coef * l_J_ij[j].tot2;
count_m_s++;
}
}
}
}
}
return (OK);
}
/* ---------------------------------------------------------------------- */
void Phreeqc::
calc_b_ij(int icell, int jcell, int k, LDBLE b_i, LDBLE b_j, LDBLE g_i, LDBLE g_j, LDBLE free_i, LDBLE free_j, int stagnant)
/* ---------------------------------------------------------------------- */
{
ct[icell].v_m[k].b_ij = b_i * (free_i + g_i) * b_j * (free_j + g_j) / (b_i * (free_i + g_i) + b_j * (free_j + g_j));
// At filterends, concentrations of ions change step-wise to the DL.
// We take the harmonic mean for f_free, the average for the DL.
if (ct[icell].v_m[k].z)
{
if (!g_i && g_j)
{
ct[icell].v_m[k].b_ij = free_j * b_i * b_j / (b_i + b_j) +
b_i * (1 - free_j) / 4 + b_j * g_j / 4;
}
else if (g_i && !g_j)
ct[icell].v_m[k].b_ij = free_i * b_i * b_j / (b_i + b_j) +
b_j * (1 - free_i) / 4 + b_i * g_i / 4;
}
// for boundary cells...
if (stagnant > 1)
{ /* for a diffusion experiment with well-mixed reservoir in cell 3 and the last stagnant cell,
and with the mixf * 2 for the boundary cells in the input... */
if (icell == 3 && !g_i && g_j)
ct[icell].v_m[k].b_ij = b_j * (free_j + g_j) / 2;
else if (jcell == all_cells - 1 && !g_j && g_i)
ct[icell].v_m[k].b_ij = b_i * (free_i + g_i) / 2;
}
else
{
if (icell == 0 || (icell == count_cells + 1 && jcell == count_cells + count_cells + 1))
ct[icell].v_m[k].b_ij = b_j * (free_j + g_j);
else if (icell == count_cells && jcell == count_cells + 1)
ct[icell].v_m[k].b_ij = b_i * (free_i + g_i);
}
if (ct[icell].v_m[k].z)
ct[icell].Dz2c += ct[icell].v_m[k].b_ij * ct[icell].v_m[k].zc * ct[icell].v_m[k].z;
return;
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
find_J(int icell, int jcell, LDBLE mixf, LDBLE DDt, int stagnant)
/* ---------------------------------------------------------------------- */
{
/* mole transfer of the individual master_species:
* Eqn 1:
* J_ij = DDt * (A_ij / lav) * (-D_i*grad(c) + D_i*z_i*c_i * SUM(D_i*z_i*grad(c)) / SUM(D_i*(z_i)^2*c_i))
* regular column, stagnant FALSE:
* D_i = temperature-corrected Dw
* A_ij = A_icell * A_jcell
* A_icell = (L porewater in i_cell / length_icell) / tort_f_icell /
* (length_icell / 2)
* lav = A_icell + A_jcell
* grad(c) is concentration difference in icell and jcell (dx is in lav),
for activity corrections see Appelo & Wersin, 2007.
** dec. 28, 2015**
included aq_dl in the harmonic mean:
J_ij = - b_i * b_j / (b_i + b_j) * (c_j - c_i) in mol/s (see ex 21 in the manual 3).
b_i = A1 / (G_i * h_i / 2) * Dw for a pore without EDL. A1 = aq1 / h_i (m^2).
with EDL (no aq_il_i in A1, for now):
t_aq1 = aq1 + aq_dl_i. A1 = t_aq1 / h_i. f_free_i = aq1 / t_aq1.
b_i_cat = A1 / (G_i * h_i / 2) * Dw * {f_free + (1 - f_free) * Bm}. Bm = Boltzmann enrichment in EDL = g_dl.
b_i_ani = A1 / (G_i * h_i / 2) * Dw * {f_free + (1 - f_free) / Bm)}.
22/2/18: now calculates diffusion through EDL's of multiple, differently charged surfaces
* stagnant TRUE:
* same eqn for J_ij, but multplies with 2 * mixf. (times 2, because mixf = A / (G_i * h_i))
* mixf_ij = mixf / (Dw / init_tort_f) / new_tort_f * new_por / init_por
* mixf is defined in MIX; Dw is default multicomponent diffusion coefficient;
* init_tort_f equals multi_Dpor^(-multi_Dn); new_pf = new tortuosity factor.
* Interlayer diffusion (IL) takes the gradient in the equivalent concentrations on X-.
surface area A for IL:
stagnant: ct[icell].mixf_il is mixf * por_il / por.
por_il = interlayer porosity, from -interlayer_D true 'por_il'.
por = free + DL porewater porosity, from -multi_D true 'multi_Dpor'.
in regular column, A is calc'd from (free + DL porewater) and cell-length.
for IL: A * por_il / por.
por_il should be entered for the cell with the maximal cec.
IL water is related to X-, thus the cec (eq/L IL water) is the same for all cells if X is difined.
IL-water = (free + DL porewater) * por_il / por.
for IL: A * aq_il / t_aq.
*/
int i, i_max, j, j_max, k, k_il, only_counter, il_calcs;
int i1;
LDBLE A1 = 0.0, A2 = 0.0, ddlm, aq1, aq2, t_aq1, t_aq2, f_free_i, f_free_j;
LDBLE dl_aq1, dl_aq2, dum, dum1, dum2, tort1, tort2, b_i, b_j;
LDBLE Sum_zM, aq_il1, aq_il2;
LDBLE por_il1, por_il2, por_il12 = 0.0;
LDBLE cec1, cec2, cec12 = 0.0, rc1 = 0.0, rc2 = 0.0;
LDBLE dV, c1, c2;
LDBLE max_b = 0.0;
il_calcs = (interlayer_Dflag ? 1 : 0);
cxxSurface *s_ptr1, *s_ptr2;
LDBLE g_i, g_j;
std::vector<cxxSurfaceCharge> s_charge_p;
std::vector<cxxSurfaceCharge> s_charge_p1;
std::vector<cxxSurfaceCharge> s_charge_p2;
std::vector<cxxSurfaceCharge>::iterator it_sc;
std::vector<cxxSurfaceComp> s_com_p;
ct[icell].dl_s = ct[jcell].dl_s = dl_aq1 = dl_aq2 = 0.0;
if (dV_dcell && !find_current)
goto dV_dcell2;
/* check for immediate return and interlayer diffusion calcs... */
//ct[icell].J_ij_sum = 0.0;
ct[icell].J_ij_count_spec = 0;
if (!il_calcs)
{
if (stagnant)
{
if (cell_data[icell].por < multi_Dpor_lim || cell_data[jcell].por < multi_Dpor_lim)
{
if (!implicit)
return (il_calcs);
}
}
else
{ /* regular column... */
if ((icell == 0 && cell_data[1].por < multi_Dpor_lim)
|| (icell == count_cells && cell_data[count_cells].por < multi_Dpor_lim)
|| (icell != 0 && icell != count_cells && (cell_data[icell].por < multi_Dpor_lim
|| cell_data[jcell].por < multi_Dpor_lim)))
{
if (dV_dcell)
{
current_cells[icell].R = -1e15;
current_cells[icell].ele = dV_dcell / current_cells[icell].R;
current_cells[icell].dif = 0;
sum_R += current_cells[icell].R;
sum_Rd += current_cells[0].dif * current_cells[icell].R;
}
if (!implicit)
return (il_calcs);
}
}
}
/* do the calcs */
aq1 = ct[icell].kgw = Utilities::Rxn_find(Rxn_solution_map, icell)->Get_mass_water();
aq2 = ct[jcell].kgw = Utilities::Rxn_find(Rxn_solution_map, jcell)->Get_mass_water();
/*
* check if DL calculations must be made, find amounts of water...
*/
s_ptr1 = s_ptr2 = NULL;
ct[icell].visc1 = ct[icell].visc2 = 1.0;
only_counter = FALSE;
s_ptr1 = Utilities::Rxn_find(Rxn_surface_map, icell);
if (s_ptr1 != NULL)
{
if (s_ptr1->Get_dl_type() != cxxSurface::NO_DL)
{
s_charge_p.assign(s_ptr1->Get_surface_charges().begin(), s_ptr1->Get_surface_charges().end());
s_com_p.assign(s_ptr1->Get_surface_comps().begin(), s_ptr1->Get_surface_comps().end());
if (s_ptr1->Get_only_counter_ions())
only_counter = TRUE;
ct[icell].visc1 = s_ptr1->Get_DDL_viscosity();
/* find the immobile surface charges with DL... */
for (i = 0; i < (int)s_charge_p.size(); i++)
{
for (i1 = 0; i1 < (int)s_com_p.size(); i1++)
{
if (!(s_charge_p[i].Get_name().compare(s_com_p[i1].Get_charge_name())) && !s_com_p[i1].Get_Dw())
{
dl_aq1 += s_charge_p[i].Get_mass_water();
s_charge_p1.push_back(s_charge_p[i]);
break;
}
}
}
}
}
s_ptr2 = Utilities::Rxn_find(Rxn_surface_map, jcell);
if (s_ptr2 != NULL)
{
if (s_ptr2->Get_dl_type() != cxxSurface::NO_DL)
{
s_charge_p.assign(s_ptr2->Get_surface_charges().begin(), s_ptr2->Get_surface_charges().end());
s_com_p.assign(s_ptr2->Get_surface_comps().begin(), s_ptr2->Get_surface_comps().end());
if (s_ptr2->Get_only_counter_ions())
only_counter = TRUE;
ct[icell].visc2 = s_ptr2->Get_DDL_viscosity();
for (i = 0; i < (int)s_charge_p.size(); i++)
{
for (i1 = 0; i1 < (int)s_com_p.size(); i1++)
{
if (!(s_charge_p[i].Get_name().compare(s_com_p[i1].Get_charge_name())) && !s_com_p[i1].Get_Dw())
{
dl_aq2 += s_charge_p[i].Get_mass_water();
s_charge_p2.push_back(s_charge_p[i]);
break;
}
}
}
}
}
if (!stagnant)
{
if (icell == 0)
ct[icell].visc1 = ct[icell].visc2;
else if (icell == count_cells)
ct[icell].visc2 = ct[icell].visc1;
}
/* in each cell: DL surface = mass_water_DL / (cell_length)
free pore surface = mass_water_free / (cell_length)
determine DL surface as a fraction of the total pore surface... */
t_aq1 = aq1 + dl_aq1;
t_aq2 = aq2 + dl_aq2;
f_free_i = aq1 / t_aq1;
f_free_j = aq2 / t_aq2;
if (dl_aq1 > 0)
ct[icell].dl_s = dl_aq1 / t_aq1;
if (dl_aq2 > 0)
{
ct[jcell].dl_s = dl_aq2 / t_aq2;
if (!ct[icell].dl_s)
ct[icell].dl_s = 1e-8; // used in implicit appt
}
if (il_calcs)
{
/* find interlayer porosity por_il,
make it relative to exchange capacity (mol X/L), highest X in sol_D[1].x_max (mol X / L).
Find amounts of IL water and cec. */
por_il1 = por_il2 = por_il12 = 0.0;
cec1 = cec2 = cec12 = rc1 = rc2 = 0.0;
if (icell == 0)
{
por_il1 = sol_D[0].exch_total / aq1 / sol_D[1].x_max *
cell_data[1].por_il;
por_il2 = sol_D[1].exch_total / aq2 / sol_D[1].x_max *
cell_data[1].por_il;
if (sol_D[0].exch_total > 3e-10 && sol_D[1].exch_total > 3e-10)
/* take the harmonic mean... */
por_il12 = 2 * por_il1 * por_il2 / (por_il1 + por_il2);
else
/* at column ends, take the clay... */
por_il12 = (por_il1 >= por_il2 ? por_il1 : por_il2);
aq_il2 = t_aq2 * por_il12 / cell_data[1].por;
aq_il1 = t_aq2 * por_il12 / cell_data[1].por;
}
else if (icell == count_cells)
{
por_il1 = sol_D[count_cells].exch_total / aq1 / sol_D[1].x_max *
cell_data[count_cells].por_il;
por_il2 = sol_D[count_cells + 1].exch_total / aq2 / sol_D[1].x_max *
cell_data[count_cells].por_il;
if (sol_D[count_cells].exch_total > 3e-10 && sol_D[count_cells + 1].exch_total > 3e-10)
por_il12 = 2 * por_il1 * por_il2 / (por_il1 + por_il2);
else
por_il12 = (por_il1 >= por_il2 ? por_il1 : por_il2);
aq_il1 = t_aq1 * por_il12 / cell_data[count_cells].por;
aq_il2 = t_aq2 * por_il12 / cell_data[count_cells].por;
}
else
{
por_il1 = sol_D[icell].exch_total / aq1 / sol_D[1].x_max *
cell_data[icell].por_il;
por_il2 = sol_D[jcell].exch_total / aq2 / sol_D[1].x_max *
cell_data[jcell].por_il;
if (sol_D[icell].exch_total > 3e-10 && sol_D[jcell].exch_total > 3e-10)
por_il12 = 2 * por_il1 * por_il2 / (por_il1 + por_il2);
else
por_il12 = (por_il1 >= por_il2 ? por_il1 : por_il2);
aq_il1 = t_aq1 * por_il12 / cell_data[icell].por;
aq_il2 = t_aq2 * por_il12 / cell_data[jcell].por;
}
if (por_il12 < interlayer_Dpor_lim)
il_calcs = 0;
else
{
dum = sol_D[icell].exch_total;
dum2 = sol_D[jcell].exch_total;
// the rc's are for distributing the mole transfer (later on) over X and solution.
rc1 = (dum2 > dum ? dum / dum2 : 1);
rc2 = (dum > dum2 ? dum2 / dum : 1);
if (sol_D[icell].exch_total > 3e-10)
cec1 = dum / aq_il1;
else
cec1 = 2e-10;
if (sol_D[jcell].exch_total > 3e-10)
cec2 = dum2 / aq_il2;
else
cec2 = 2e-10;
/* and the largest for calculating the mass transfer since it is usually at the boundary... */
cec12 = (cec1 > cec2 ? cec1 : cec2);
}
}
/* Find ct[icell].mixf_il for IL diffusion.
In stagnant calc's, correct mixf by default values, A = por / tort.
In regular column, A = surface area / (0.5 * x * tort)*/
tort1 = tort2 = 1.0;
ct[icell].A_ij_il = ct[icell].mixf_il = 0.0;
if (stagnant)
{
mixf /= (default_Dw * pow(multi_Dpor, multi_Dn) * multi_Dpor);
if (il_calcs)
ct[icell].mixf_il = mixf * por_il12 / interlayer_tortf;
}
if (icell == 0)
{
tort1 = tort2 = pow(cell_data[1].por, -multi_Dn);
if (stagnant)
A2 = cell_data[1].por / tort2;
else
{
A2 = t_aq2 / (cell_data[1].length * 0.5 * cell_data[1].length);
if (il_calcs && !stagnant)
ct[icell].A_ij_il = A2 * por_il12 / (cell_data[1].por * interlayer_tortf);
A2 /= tort2;
}
A1 = A2;
}
else if (icell == count_cells)
{
tort1 = tort2 = pow(cell_data[count_cells].por, -multi_Dn);
if (stagnant)
A1 = cell_data[count_cells].por / tort1;
else
{
A1 = t_aq1 / (cell_data[count_cells].length * 0.5 * cell_data[count_cells].length);
if (il_calcs && !stagnant)
ct[icell].A_ij_il = A1 * por_il12 / (cell_data[count_cells].por * interlayer_tortf);
A1 /= tort1;
}
A2 = A1;
}
else
{
tort1 = pow(cell_data[icell].por, -multi_Dn);
tort2 = pow(cell_data[jcell].por, -multi_Dn);
if (stagnant)
{
A1 = cell_data[icell].por / tort1;
A2 = cell_data[jcell].por / tort2;
}
else
{
A1 = t_aq1 / (cell_data[icell].length * 0.5 * cell_data[icell].length);
A2 = t_aq2 / (cell_data[jcell].length * 0.5 * cell_data[jcell].length);
if (il_calcs && !stagnant)
{
dum = A1 * por_il12 / (cell_data[icell].por * interlayer_tortf);
dum2 = A2 * por_il12 / (cell_data[jcell].por * interlayer_tortf);
ct[icell].A_ij_il = dum * dum2 / (dum + dum2);
}
A1 /= tort1;
A2 /= tort2;
}
}
/* diffuse... */
/*
* malloc sufficient space...
*/
k = sol_D[icell].count_spec + sol_D[jcell].count_spec;
if (ct[icell].J_ij == NULL)
{
ct[icell].J_ij = (class J_ij *) PHRQ_malloc((size_t)(k) * sizeof(class J_ij));
ct[icell].J_ij_size = k;
}
else if (k > ct[icell].J_ij_size)
{
ct[icell].J_ij = (class J_ij *) PHRQ_realloc(ct[icell].J_ij, (size_t)(k) * sizeof(class J_ij));
ct[icell].J_ij_size = k;
}
if (ct[icell].J_ij == NULL)
malloc_error();
if (ct[icell].v_m == NULL)
{
ct[icell].v_m = (struct V_M *) PHRQ_malloc((size_t)(k) * sizeof(struct V_M));
ct[icell].v_m_size = k;
}
else if (k > ct[icell].v_m_size)
{
ct[icell].v_m = (struct V_M *) PHRQ_realloc(ct[icell].v_m, (size_t)(k) * sizeof(struct V_M));
ct[icell].v_m_size = k;
}
if (ct[icell].v_m == NULL)
malloc_error();
for (i = 0; i < k; i++)
{
ct[icell].J_ij[i].tot1 = ct[icell].J_ij[i].tot_stag = 0.0;
ct[icell].v_m[i].grad = 0.0;
ct[icell].v_m[i].D = 1.0; // used for gamma correction
ct[icell].v_m[i].z = 0.0;
ct[icell].v_m[i].c = 0.0;
ct[icell].v_m[i].zc = 0.0;
//ct[icell].v_m[i].Dz = 0.0;
//ct[icell].v_m[i].Dzc = 0.0;
ct[icell].v_m[i].b_ij = 0.0;
}
ct[icell].Dz2c = ct[icell].Dz2c_il = 0.0;
if (il_calcs)
{
/* also for interlayer cations */
k = sol_D[icell].count_exch_spec + sol_D[jcell].count_exch_spec;
ct[icell].J_ij_il = (class J_ij *) free_check_null(ct[icell].J_ij_il);
ct[icell].J_ij_il = (class J_ij *) PHRQ_malloc((size_t) k * sizeof(class J_ij));
if (ct[icell].J_ij_il == NULL)
malloc_error();
ct[icell].v_m_il = (struct V_M *) free_check_null(ct[icell].v_m_il);
ct[icell].v_m_il = (struct V_M *) PHRQ_malloc((size_t) k * sizeof(struct V_M));
if (ct[icell].v_m_il == NULL)
malloc_error();
for (i = 0; i < k; i++)
{
ct[icell].J_ij_il[i].tot1 = 0.0;
ct[icell].v_m_il[i].grad = 0.0;
ct[icell].v_m_il[i].D = 0.0;
ct[icell].v_m_il[i].z = 0.0;
ct[icell].v_m_il[i].c = 0.0;
ct[icell].v_m_il[i].zc = 0.0;
ct[icell].v_m_il[i].Dz = 0.0;
ct[icell].v_m_il[i].Dzc = 0.0;
ct[icell].v_m_il[i].b_ij = 0.0;
}
}
/*
* coefficients in Eqn (1)...
*/
i = j = k = k_il = 0;
i_max = sol_D[icell].count_spec;
j_max = sol_D[jcell].count_spec;
while (i < i_max || j < j_max)
{
if (j == j_max
|| (i < i_max && strcmp(sol_D[icell].spec[i].name, sol_D[jcell].spec[j].name) < 0))
{
/* species 'name' is only in icell */
if (il_calcs && sol_D[icell].spec[i].type == EX)
{
ct[icell].J_ij_il[k_il].name = sol_D[icell].spec[i].name;
ct[icell].v_m_il[k_il].D = sol_D[icell].spec[i].Dwt;
ct[icell].v_m_il[k_il].z = sol_D[icell].spec[i].z;
ct[icell].v_m_il[k_il].Dz = ct[icell].v_m_il[k_il].D * ct[icell].v_m_il[k_il].z;
dum = sol_D[icell].spec[i].c * cec12 / (2 * ct[icell].v_m_il[k_il].z);
ct[icell].v_m_il[k_il].Dzc = ct[icell].v_m_il[k_il].Dz * dum;
ct[icell].Dz2c_il += ct[icell].v_m_il[k_il].Dzc * ct[icell].v_m_il[k_il].z;
ct[icell].v_m_il[k_il].grad = -sol_D[icell].spec[i].c * cec12 / ct[icell].v_m_il[k_il].z; /* use equivalent fraction */
k_il++;
}
else
{
ct[icell].J_ij[k].name = sol_D[icell].spec[i].name;
ct[icell].v_m[k].z = sol_D[icell].spec[i].z;
ct[icell].v_m[k].grad = -sol_D[icell].spec[i].c; /* assume d log(gamma) / d log(c) = 0 */
c1 = sol_D[icell].spec[i].c / 2;
ct[icell].v_m[k].c = c1;
if (ct[icell].v_m[k].z)
ct[icell].v_m[k].zc = ct[icell].v_m[k].z * c1;
if (dV_dcell && ct[icell].v_m[k].z && !fix_current)
{
// compare diffusive and electromotive forces
dum = ct[icell].v_m[k].grad;
if (icell == 0)
dum2 = (cell_data[1].potV - cell_data[0].potV) / (cell_data[1].length / 2);
else if (icell == count_cells)
dum2 = (cell_data[count_cells + 1].potV - cell_data[count_cells].potV) / (cell_data[count_cells].length / 2);
else
dum2 = (cell_data[jcell].potV - cell_data[icell].potV) / ((cell_data[jcell].length + cell_data[icell].length) / 2);
dum2 *= F_Re3 / tk_x2 * ct[icell].v_m[k].z * c1;
if (dum + dum2 > 0)
{
// step out: no transport against the dV_dcell gradient if c = 0 in jcell...
if (i < i_max)
i++;
continue;
}
}
g_i = g_j = 0;
if (ct[icell].dl_s > 0)
{
if (dl_aq1)
{
for (it_sc = s_charge_p1.begin(); it_sc != s_charge_p1.end(); it_sc++)
{
g_i += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z];
}
g_i *= sol_D[icell].spec[i].erm_ddl;
}
if (dl_aq2)
{
for (it_sc = s_charge_p2.begin(); it_sc != s_charge_p2.end(); it_sc++)
{
if (ct[icell].v_m[k].z == 0 || only_counter)
{
g_j += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z];
}
else
{
if (abs(ct[icell].v_m[k].z) == 1)
// there is always H+ and OH-...
g_j += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z];
else
{
dum1 = it_sc->Get_mass_water() / t_aq2;
dum2 = it_sc->Get_z_gMCD_map()[1] / dum1;
g_j += pow(dum2, ct[icell].v_m[k].z) * dum1;
}
}
}
g_j *= sol_D[icell].spec[i].erm_ddl;
}
}
b_i = A1 * sol_D[icell].spec[i].Dwt;
b_j = A2;
if (sol_D[icell].tk_x == sol_D[jcell].tk_x)
b_j *= sol_D[icell].spec[i].Dwt;
else
{
dum2 = sol_D[icell].spec[i].Dwt / sol_D[icell].viscos_f;
dum2 *= exp(sol_D[icell].spec[i].dw_t / sol_D[jcell].tk_x - sol_D[icell].spec[i].dw_t / sol_D[icell].tk_x);
dum2 *= sol_D[jcell].viscos_f;
b_j *= dum2;
}
calc_b_ij(icell, jcell, k, b_i, b_j, g_i, g_j, f_free_i, f_free_j, stagnant);
k++;
}
if (i < i_max)
i++;
}
else if (i == i_max ||
(j < j_max && strcmp(sol_D[icell].spec[i].name, sol_D[jcell].spec[j].name) > 0))
{
/* species 'name' is only in jcell */
if (il_calcs && sol_D[jcell].spec[j].type == EX)
{
ct[icell].J_ij_il[k_il].name = sol_D[jcell].spec[j].name;
ct[icell].v_m_il[k_il].D = sol_D[jcell].spec[j].Dwt;
ct[icell].v_m_il[k_il].z = sol_D[jcell].spec[j].z;
ct[icell].v_m_il[k_il].Dz = ct[icell].v_m_il[k_il].D * ct[icell].v_m_il[k_il].z;
ct[icell].v_m_il[k_il].Dzc = ct[icell].v_m_il[k_il].Dz * sol_D[jcell].spec[j].c *
cec12 / (2 * ct[icell].v_m_il[k_il].z);
ct[icell].Dz2c_il += ct[icell].v_m_il[k_il].Dzc * ct[icell].v_m_il[k_il].z;
ct[icell].v_m_il[k_il].grad = sol_D[jcell].spec[j].c * cec12 / ct[icell].v_m_il[k_il].z; /* use equivalent fraction */
k_il++;
}
else
{
ct[icell].J_ij[k].name = sol_D[jcell].spec[j].name;
ct[icell].v_m[k].z = sol_D[jcell].spec[j].z;
ct[icell].v_m[k].grad = sol_D[jcell].spec[j].c; /* assume d log(gamma) / d log(c) = 0 */
c2 = sol_D[jcell].spec[j].c / 2;
ct[icell].v_m[k].c = c2;
if (ct[icell].v_m[k].z)
ct[icell].v_m[k].zc = ct[icell].v_m[k].z * c2;
if (dV_dcell && ct[icell].v_m[k].z && !fix_current)
{
// compare diffuse and electromotive forces
dum = ct[icell].v_m[k].grad;
if (icell == 0)
dum2 = (cell_data[1].potV - cell_data[0].potV) / (cell_data[1].length / 2);
else if (icell == count_cells)
dum2 = (cell_data[count_cells + 1].potV - cell_data[count_cells].potV) / (cell_data[count_cells].length / 2);
else
dum2 = (cell_data[jcell].potV - cell_data[icell].potV) / ((cell_data[jcell].length + cell_data[icell].length) / 2);
dum2 *= F_Re3 / tk_x2 * ct[icell].v_m[k].z * c2;
// don't transport unavailable moles against the gradient
if (dum + dum2 < 0)
{
// step out...
if (j < j_max)
j++;
continue;
}
}
g_i = g_j = 0;
if (ct[icell].dl_s > 0)
{
if (dl_aq1)
{
for (it_sc = s_charge_p1.begin(); it_sc != s_charge_p1.end(); it_sc++)
{
if (ct[icell].v_m[k].z == 0 || only_counter)
{
g_i += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z];
}
else
{
if (abs(ct[icell].v_m[k].z) == 1)
// there is always H+ and OH-...
g_i += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z];
else
{
dum1 = it_sc->Get_mass_water() / t_aq1;
dum2 = it_sc->Get_z_gMCD_map()[1] / dum1;
g_i += pow(dum2, ct[icell].v_m[k].z) * dum1;
}
}
}
g_i *= sol_D[jcell].spec[j].erm_ddl;
}
if (dl_aq2)
{
for (it_sc = s_charge_p2.begin(); it_sc != s_charge_p2.end(); it_sc++)
{
g_j += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z];
}
g_j *= sol_D[jcell].spec[j].erm_ddl;
}
}
b_i = A1;
b_j = A2 * sol_D[jcell].spec[j].Dwt;
if (sol_D[icell].tk_x == sol_D[jcell].tk_x)
b_i *= sol_D[jcell].spec[j].Dwt;
else
{
dum2 = sol_D[jcell].spec[j].Dwt / sol_D[jcell].viscos_f;
dum2 *= exp(sol_D[jcell].spec[j].dw_t / sol_D[icell].tk_x - sol_D[jcell].spec[j].dw_t / sol_D[jcell].tk_x);
dum2 *= sol_D[icell].viscos_f;
b_i *= dum2;
}
calc_b_ij(icell, jcell, k, b_i, b_j, g_i, g_j, f_free_i, f_free_j, stagnant);
k++;
}
if (j < j_max)
j++;
}
else if (strcmp(sol_D[icell].spec[i].name, sol_D[jcell].spec[j].name) == 0)
{
/* species 'name' is in both cells */
if (il_calcs && sol_D[icell].spec[i].type == EX)
{
ct[icell].J_ij_il[k_il].name = sol_D[icell].spec[i].name;
if (sol_D[icell].spec[i].Dwt == 0 || sol_D[jcell].spec[j].Dwt == 0)
ct[icell].v_m_il[k_il].D = 0.0;
else
ct[icell].v_m_il[k_il].D =
(sol_D[icell].spec[i].Dwt + sol_D[jcell].spec[j].Dwt) / 2;
ct[icell].v_m_il[k_il].z = sol_D[icell].spec[i].z;
ct[icell].v_m_il[k_il].Dz = ct[icell].v_m_il[k_il].D * ct[icell].v_m_il[k_il].z;
ct[icell].v_m_il[k_il].Dzc = ct[icell].v_m_il[k_il].Dz * (sol_D[icell].spec[i].c +
sol_D[jcell].spec[j].c) * cec12 / ct[icell].v_m_il[k_il].z;
ct[icell].Dz2c_il += ct[icell].v_m_il[k_il].Dzc * ct[icell].v_m_il[k_il].z;
ct[icell].v_m_il[k_il].grad = (sol_D[jcell].spec[j].c - sol_D[icell].spec[i].c) *
cec12 / ct[icell].v_m_il[k_il].z; /* use equivalent fraction */
k_il++;
}
else
{
ct[icell].J_ij[k].name = sol_D[icell].spec[i].name;
ct[icell].v_m[k].z = sol_D[icell].spec[i].z;
ct[icell].v_m[k].grad = (sol_D[jcell].spec[j].c - sol_D[icell].spec[i].c);
c1 = sol_D[icell].spec[i].c / 2;
c2 = sol_D[jcell].spec[j].c / 2;
ct[icell].v_m[k].c = c1 + c2;
if (ct[icell].v_m[k].z)
ct[icell].v_m[k].zc = ct[icell].v_m[k].z * ct[icell].v_m[k].c;
if (dV_dcell && ct[icell].v_m[k].z && !fix_current && !implicit)
{
// compare diffuse and electromotive forces
dum = ct[icell].v_m[k].grad;
if (icell == 0)
dum2 = (cell_data[1].potV - cell_data[0].potV) / (cell_data[1].length / 2);
else if (icell == count_cells)
dum2 = (cell_data[count_cells + 1].potV - cell_data[count_cells].potV) / (cell_data[count_cells].length / 2);
else
dum2 = (cell_data[jcell].potV - cell_data[icell].potV) / ((cell_data[jcell].length + cell_data[icell].length) / 2);
dum2 *= F_Re3 / tk_x2 * ct[icell].v_m[k].z * (c1 + c2);
// don't transport unavailable moles against the gradient
if (fabs(dum) < fabs(dum2) &&
((dum2 >= 0 && sol_D[jcell].spec[j].c * aq2 < 1e-12) ||
(dum2 <= 0 && sol_D[icell].spec[i].c * aq1 < 1e-12)))
{
// step out:
if (i < i_max)
i++;
if (j < j_max)
j++;
continue;
}
}
g_i = g_j = 0;
if (ct[icell].dl_s > 0)
{
if (dl_aq1)
{
for (it_sc = s_charge_p1.begin(); it_sc != s_charge_p1.end(); it_sc++)
{
g_i += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z];
}
g_i *= sol_D[icell].spec[i].erm_ddl;
}
if (dl_aq2)
{
for (it_sc = s_charge_p2.begin(); it_sc != s_charge_p2.end(); it_sc++)
{
g_j += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z];
}
g_j *= sol_D[jcell].spec[j].erm_ddl;
}
}
b_i = A1 * sol_D[icell].spec[i].Dwt;
b_j = A2 * sol_D[jcell].spec[j].Dwt;
calc_b_ij(icell, jcell, k, b_i, b_j, g_i, g_j, f_free_i, f_free_j, stagnant);
//ddlm = sol_D[jcell].spec[j].lm - sol_D[icell].spec[i].lm; // appt: this could give an incorrect large factor for implicit
//if (fabs(ddlm) > 1e-10)
// ct[icell].v_m[k].grad *= (1 + (sol_D[jcell].spec[j].lg - sol_D[icell].spec[i].lg) / ddlm);
ddlm = sol_D[jcell].spec[j].lm - sol_D[icell].spec[i].lm;
dum1 = sol_D[jcell].spec[j].lg - sol_D[icell].spec[i].lg;
dum = 1;
if (ddlm)
dum += dum1 / ddlm;
if (dum > 0.2 && dum < 2.25)
{
ct[icell].v_m[k].grad *= dum;
if (implicit)
ct[icell].v_m[k].D = dum;
}
//if (ct[icell].v_m[k].b_ij > max_b)
// max_b = ct[icell].v_m[k].b_ij;
k++;
}
if (i < i_max)
i++;
if (j < j_max)
j++;
}
}
if (!dV_dcell && !ct[icell].Dz2c)
k = 0;
ct[icell].J_ij_count_spec = i_max = k;
ct[icell].J_ij_il_count_spec = k_il;
if (icell == count_cells)
ct[jcell].J_ij_count_spec = ct[icell].J_ij_count_spec;
if (implicit && stagnant < 2)
return(max_b);
/*
* fill in J_ij...
*/
if (dV_dcell)
{
current_cells[icell].ele = current_cells[icell].dif = 0;
for (i = 0; i < ct[icell].J_ij_count_spec; i++)
{
if (!ct[icell].v_m[i].z)
continue;
current_cells[icell].ele -= ct[icell].v_m[i].b_ij * ct[icell].v_m[i].z *
ct[icell].v_m[i].zc;
current_cells[icell].dif -= ct[icell].v_m[i].b_ij * ct[icell].v_m[i].z *
ct[icell].v_m[i].grad;
}
current_cells[icell].R = tk_x2 / (current_cells[icell].ele * F_Re3);
sum_R += current_cells[icell].R;
sum_Rd += (current_cells[0].dif - current_cells[icell].dif) * current_cells[icell].R;
return(il_calcs);
}
// dV_dcell, 2nd pass, current_x has been calculated, and
// voltage was adapted to give equal current in the cells.
dV_dcell2:
//ct[icell].J_ij_sum = 0;
Sum_zM = 0.0;
for (i = 0; i < ct[icell].J_ij_count_spec; i++)
{
if (ct[icell].v_m[i].z)
Sum_zM += ct[icell].v_m[i].b_ij * ct[icell].v_m[i].z * ct[icell].v_m[i].grad;
}
std::map<std::string, class J_ij_save> J_map;
for (i = 0; i < ct[icell].J_ij_count_spec; i++)
{
class J_ij_save J_save;
ct[icell].J_ij[i].tot1 = -ct[icell].v_m[i].grad;
J_save.flux_c = ct[icell].J_ij[i].tot1;
ct[icell].J_ij[i].charge = ct[icell].v_m[i].z;
if (!dV_dcell && ct[icell].v_m[i].z && ct[icell].Dz2c > 0)
{
ct[icell].J_ij[i].tot1 += Sum_zM * ct[icell].v_m[i].zc / ct[icell].Dz2c;
}
J_save.flux_t = ct[icell].J_ij[i].tot1;
if (stagnant)
{
ct[icell].J_ij[i].tot1 *= ct[icell].v_m[i].b_ij * 2 * mixf;
J_save.flux_c *= ct[icell].v_m[i].b_ij * 2 * mixf;
J_save.flux_t *= ct[icell].v_m[i].b_ij * 2 * mixf;
}
else
{
ct[icell].J_ij[i].tot1 *= ct[icell].v_m[i].b_ij * DDt;
J_save.flux_c *= ct[icell].v_m[i].b_ij * DDt;
J_save.flux_t *= ct[icell].v_m[i].b_ij * DDt;
}
J_map[ct[icell].J_ij[i].name] = J_save;
ct[icell].J_ij[i].tot2 = ct[icell].J_ij[i].tot1;
//ct[icell].J_ij_sum += ct[icell].v_m[i].z * ct[icell].J_ij[i].tot1;
}
cell_J_ij[icell] = J_map;
// assure that icell and jcell have dl water when checking negative conc's in MCD
ct[icell].dl_s = dl_aq1;
ct[jcell].dl_s = dl_aq2;
if (dV_dcell)
{
//ct[icell].J_ij_sum = 0;
dV = cell_data[jcell].potV - cell_data[icell].potV;
dum = dV * F_Re3 / tk_x2;
// perhaps adapt dV for getting equal current...
//current_cells[icell].ele = current_cells[icell].dif = 0;
//for (i = 0; i < ct[icell].J_ij_count_spec; i++)
//{
// if (!ct[icell].v_m[i].z)
// continue;
// current_cells[icell].ele -= ct[icell].v_m[i].b_ij * ct[icell].v_m[i].z *
// ct[icell].v_m[i].zc * dum;
// current_cells[icell].dif -= ct[icell].v_m[i].b_ij * ct[icell].v_m[i].z *
// ct[icell].v_m[i].grad;
//}
//dum1 = (current_x - current_cells[icell].dif) / current_cells[icell].ele;
//if (isnan(dum1))
// dum1 = 1;
//dum *= dum1;
for (i = 0; i < ct[icell].J_ij_count_spec; i++)
{
if (!ct[icell].v_m[i].z)
continue;
ct[icell].J_ij[i].tot1 -= ct[icell].v_m[i].b_ij *
ct[icell].v_m[i].zc * dum * DDt;
ct[icell].J_ij[i].tot2 = ct[icell].J_ij[i].tot1;
//ct[icell].J_ij_sum += ct[icell].v_m[i].z * ct[icell].J_ij[i].tot1;
}
current_A = current_x * F_C_MOL;
}
/*
* calculate interlayer mass transfer...
*/
if (il_calcs && ct[icell].Dz2c_il != 0 && ct[icell].J_ij_il_count_spec > 0)
{
cxxExchange *ex_ptr1 = Utilities::Rxn_find(Rxn_exchange_map, icell);
cxxExchange *ex_ptr2 = Utilities::Rxn_find(Rxn_exchange_map, jcell);
Sum_zM = 0.0;
i_max = k_il = ct[icell].J_ij_il_count_spec;
for (i = 0; i < i_max; i++)
Sum_zM += ct[icell].v_m_il[i].Dz * ct[icell].v_m_il[i].grad;
for (i = 0; i < i_max; i++)
{
ct[icell].J_ij_il[i].tot1 = -ct[icell].v_m_il[i].D * ct[icell].v_m_il[i].grad +
Sum_zM * ct[icell].v_m_il[i].Dzc / ct[icell].Dz2c_il;
if (stagnant)
ct[icell].J_ij_il[i].tot1 *= ct[icell].mixf_il;
else
ct[icell].J_ij_il[i].tot1 *= ct[icell].A_ij_il * DDt;
//ct[icell].J_ij_sum += ct[icell].v_m_il[i].z * ct[icell].J_ij_il[i].tot1;
ct[icell].J_ij_il[i].tot2 = ct[icell].J_ij_il[i].tot1;
// ct[icell].J_ij_il[i].charge = ct[icell].v_m_il[i].z; // appt not used now
}
/* express the transfer in elemental moles... */
tot1_h = tot1_o = tot2_h = tot2_o = 0.0;
m_s = (class M_S *) free_check_null(m_s);
m_s = (class M_S *) PHRQ_malloc((size_t) count_moles_added *
sizeof(class M_S));
if (m_s == NULL)
malloc_error();
for (i1 = 0; i1 < count_moles_added; i1++)
{
m_s[i1].charge = 0;
m_s[i1].name = NULL;
m_s[i1].tot1 = 0;
m_s[i1].tot2 = 0;
}
count_m_s = 0;
fill_m_s(ct[icell].J_ij_il, k_il, icell, stagnant);
/* do the mass transfer... */
if (icell > 0 || stagnant)
{
size_t k;
for (k = 0; k < ex_ptr1->Get_exchange_comps().size(); k++)
{
cxxNameDouble nd(ex_ptr1->Get_exchange_comps()[k].Get_totals());
cxxNameDouble::iterator it = nd.begin();
i_max = 0;
for (; it != nd.end(); it++)
{
if (strcmp("X", it->first.c_str()) == 0)
i_max = 1;
}
if (i_max)
break;
}
if (k < ex_ptr1->Get_exchange_comps().size())
{
cxxExchComp &comp_ref = ex_ptr1->Get_exchange_comps()[k];
cxxNameDouble nd(comp_ref.Get_totals());
cxxNameDouble::iterator it = nd.begin();
/* transfer O and H... */
for (; it != nd.end(); it++)
{
class element *elt_ptr = element_store(it->first.c_str());
LDBLE coef = it->second;
if (strcmp("H", elt_ptr->name) == 0)
{
if (coef < rc1 * tot1_h)
{
tot1_h -= coef;
comp_ref.Get_totals().insert("H", 0);
}
else
{
comp_ref.Get_totals().insert("H", coef - rc1 * tot1_h);
tot1_h *= (1 - rc1);
}
}
else if (strcmp("O", elt_ptr->name) == 0)
{
if (coef < rc1 * tot1_o)
{
tot1_o -= coef;
comp_ref.Get_totals().insert("O", 0);
}
else
{
comp_ref.Get_totals().insert("O", coef - rc1 * tot1_o);
tot1_o *= (1 - rc1);
}
}
}
/* transfer other elements... */
j_max = 0; /* if j_max turns true, reallocate the exchange structure */
for (j = 0; j < count_m_s; j++)
{
// Make sure list includes each element
comp_ref.Get_totals().add(m_s[j].name, 0);
cxxNameDouble nd(comp_ref.Get_totals());
cxxNameDouble::iterator it = nd.begin();
for (; it != nd.end(); it++)
{
class element *elt_ptr = element_store(it->first.c_str());
LDBLE coef = it->second;
if (strcmp(m_s[j].name, elt_ptr->name) != 0)
continue;
/* rc1 part goes to exchange species... */
if (coef < rc1 * m_s[j].tot1)
{
m_s[j].tot1 -= coef;
comp_ref.Get_totals().insert(m_s[j].name, 0);
}
else
{
comp_ref.Get_totals().insert(m_s[j].name, coef - rc1 * m_s[j].tot1);
m_s[j].tot1 *= (1 - rc1);
}
}
}
}
}
if (icell < count_cells || stagnant)
{
size_t k;
for (k = 0; k < ex_ptr2->Get_exchange_comps().size(); k++)
{
cxxExchComp &comp_ref = ex_ptr2->Get_exchange_comps()[k];
cxxNameDouble nd(comp_ref.Get_totals());
cxxNameDouble::iterator it = nd.begin();
i_max = 0;
for (; it != nd.end(); it++)
{
if (strcmp("X", it->first.c_str()) == 0)
i_max = 1;
}
if (i_max)
break;
}
if (k < ex_ptr2->Get_exchange_comps().size())
{
cxxExchComp &comp_ref = ex_ptr2->Get_exchange_comps()[k];
cxxNameDouble nd(comp_ref.Get_totals());
cxxNameDouble::iterator it = nd.begin();
/* transfer O and H... */
for (; it != nd.end(); it++)
{
class element *elt_ptr = element_store(it->first.c_str());
LDBLE coef = it->second;
if (strcmp("H", elt_ptr->name) == 0)
{
if (coef < -rc2 * tot2_h)
{
tot2_h += coef;
comp_ref.Get_totals().insert("H", 0);
}
else
{
comp_ref.Get_totals().insert("H", coef + rc2 * tot2_h);
tot2_h *= (1 - rc2);
}
}
else if (strcmp("O", elt_ptr->name) == 0)
{
if (coef < -rc2 * tot2_o)
{
tot2_o += coef;
comp_ref.Get_totals().insert("O", 0);
}
else
{
comp_ref.Get_totals().insert("O", coef + rc2 * tot2_o);
tot2_o *= (1 - rc2);
}
}
}
/* transfer other elements... */
for (j = 0; j < count_m_s; j++)
{
// Make sure list includes each element
comp_ref.Get_totals().add(m_s[j].name, 0);
cxxNameDouble nd(comp_ref.Get_totals());
cxxNameDouble::iterator it = nd.begin();
for (; it != nd.end(); it++)
{
class element *elt_ptr = element_store(it->first.c_str());
LDBLE coef = it->second;
if (strcmp(m_s[j].name, elt_ptr->name) != 0)
continue;
/* rc2 part goes to exchange species... */
if (coef < -rc2 * m_s[j].tot2)
{
m_s[j].tot2 += coef;
comp_ref.Get_totals().insert(m_s[j].name, 0);
}
else
{
comp_ref.Get_totals().insert(m_s[j].name, coef + rc2 * m_s[j].tot2);
m_s[j].tot2 *= (1 - rc2);
}
}
}
}
}
}
/* do not transport charge imbalance */
//ct[icell].J_ij_sum = 0;
//V_M = (struct V_M *) free_check_null(V_M);
if (il_calcs)
ct[icell].v_m_il = (struct V_M *) free_check_null(ct[icell].v_m_il);
return (il_calcs);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
disp_surf(LDBLE DDt)
/* ---------------------------------------------------------------------- */
/*
* Disperse/diffuse surfaces:
* surface[n1] = SS(mixf * surface[n2]) + (1 - SS(mixf) * surface[i1])
* where SS is Sum of, f2 is a mixing factor.
* Implementation:
* Mobile surface comps and charges are mixed face by face and 1 by 1 in surface[n1]:
Step (from cell 1 to count_cells + 1):
* 1. Surface n1 is made a copy of cell[i1]'s surface, if it exists, or else
* b. a copy of the first encountered mobile surface[n2] from cell i2 = i1 - 1.
* 2 a. Column surfaces are mixed by dispersion/diffusion
* b. Column surfaces are mixed by MCD (if multi_Dflag is true)
* 3. Surfaces n1 and n2 are stored in a temporary surface, with n_user = i1 or i2.
* 4. When done for all cells, new surfaces are copied into the cells.
* NOTE... The surfaces' diffuse_layer, edl and phases/kinetics relations must be identical,
* but only mobile surface_comp's (Dw > 0) and their surface_charge's are transported.
*/
{
int i, i1, i2, k, k1/*, n1, n2*/;
int charge_done, surf1, surf2;
LDBLE f1, f2, mixf, mixf_store, mcd_mixf;
LDBLE lav, A_ij, por, Dp1, Dp2;
cxxMix * mix_ptr;
cxxSurface *surface_ptr1, *surface_ptr2;
LDBLE viscos_f;
/*
* temperature and viscosity correction for MCD coefficient, D_T = D_298 * viscos_298 / viscos
*/
viscos_f = viscos_0;
viscos_f = viscos_0_25 / viscos_f;
//n1 = 0;
//n2 = n1 + 1;
cxxSurface surface_n1, surface_n2;
cxxSurface *surface_n2_ptr;
std::map<int, cxxSurface> Rxn_temp_surface_map;
for (i1 = 1; i1 <= count_cells + 1; i1++)
{
if (i1 <= count_cells && cell_data[i1].por < multi_Dpor_lim)
continue;
if (i1 == 1 && bcon_first != 1)
continue;
if (i1 == count_cells + 1 && bcon_last != 1)
continue;
i2 = i1 - 1;
if (i2 > 0 && cell_data[i2].por < multi_Dpor_lim)
continue;
/*
* step 1. define surface n1 from cell i1, if it exists...
*/
surface_n1.Set_n_user(-99);
surface_n2.Set_n_user(-99);
surface_ptr1 = Utilities::Rxn_find(Rxn_surface_map, i1);
if (surface_ptr1 != NULL)
{
surface_n1 = *surface_ptr1;
}
surface_ptr2 = Utilities::Rxn_find(Rxn_surface_map, i2);
surf1 = surf2 = 0;
if (surface_ptr2 != NULL)
{
if (surface_ptr2->Get_transport())
surf2 = 1;
}
if (surface_ptr1 != NULL)
{
if (surface_ptr1->Get_transport())
surf1 = 1;
}
mixf = mixf_store = 0;
if (surf1 || surf2)
{
/*
* step 2a. Dispersive mixing of mobile surfaces from column cells i2 < i1...
*/
if (i1 <= count_cells)
mix_ptr = Utilities::Rxn_find(Dispersion_mix_map, i1);
else
mix_ptr = Utilities::Rxn_find(Dispersion_mix_map, count_cells);
std::vector<int> num;
std::vector<LDBLE> frac;
mix_ptr->Vectorize(num, frac);
for (size_t i3 = 0; i3 < num.size(); i3++)
{
if (i1 <= count_cells)
{
if (num[i3] == i2)
{
mixf = mixf_store = frac[i3];
break;
}
}
else if (num[i3] == i1)
{
mixf = mixf_store = frac[i3];
break;
}
}
/* step 1b. If cell i1 has no surface, get the mobile comps from surface i2... */
if (surface_n1.Get_n_user() == -99)
{
surface_n1 = mobile_surface_copy(surface_ptr2, i1, false);
/* limit charges to 1... */
if (surface_n1.Get_surface_charges().size() > 1)
{
std::string charge_name = surface_n1.Get_surface_charges()[0].Get_name();
surface_n1 = sum_surface_comp(&surface_n1, 0,
&surface_n1, charge_name, 1, surface_n1.Get_surface_comps()[0].Get_Dw());
}
f1 = 0;
}
else
f1 = 1;
/* find the (possibly modified) surface in the previous cell i2... */
f2 = 1;
if (i2 > 0 || bcon_first == 1)
{
surface_n2_ptr = Utilities::Rxn_find(Rxn_temp_surface_map, i2);
if (surface_n2_ptr != NULL)
{
surface_n2 = *surface_n2_ptr;
}
/* if not found... */
else
{
/* copy it from surface_ptr2... */
if (surface_ptr2 != NULL)
{
surface_n2 = *surface_ptr2;
surface_n2.Set_n_user_both(i2);
}
else
{
/* or make it a mobile copy of the surface in cell i1... */
surface_n2 = mobile_surface_copy(surface_ptr1, i2, false);
/* limit charges to 1... */
if (surface_n2.Get_surface_charges().size() > 1)
{
std::string charge_name = surface_n2.Get_surface_charges()[0].Get_name();
surface_n2 = sum_surface_comp(&surface_n2, 0, &surface_n2, charge_name, 1,
surface_n2.Get_surface_comps()[0].Get_Dw());
}
f2 = 0;
}
}
}
/*
* For MCD, step 2b. Find physical dimensions and porosity of the cells...
*/
if (i1 == 1)
{
por = cell_data[1].por;
lav = cell_data[1].length / 2;
A_ij = Utilities::Rxn_find(Rxn_solution_map, 1)->Get_mass_water() / cell_data[1].length;
}
else if (i1 == count_cells + 1)
{
por = cell_data[count_cells].por;
lav = cell_data[count_cells].length / 2;
A_ij =
Utilities::Rxn_find(Rxn_solution_map, count_cells)->Get_mass_water() /
cell_data[count_cells].length;
}
else
{
por = cell_data[i2].por;
lav =
(cell_data[i1].length + cell_data[i2].length) / 2;
A_ij =
Utilities::Rxn_find(Rxn_solution_map, i1)->Get_mass_water() /
(cell_data[i1].length * cell_data[i1].por);
A_ij +=
Utilities::Rxn_find(Rxn_solution_map, i2)->Get_mass_water() /
(cell_data[i2].length * cell_data[i2].por);
A_ij /= 2;
A_ij *=
(cell_data[i1].por <
cell_data[i2].por ? cell_data[i1].por : cell_data[i2].por);
}
/* mix in comps with the same charge structure... */
if (surf2)
{
for (k = 0; k < (int) surface_ptr2->Get_surface_comps().size(); k++)
{
cxxSurfaceComp *comp_k_ptr = &(surface_ptr2->Get_surface_comps()[k]);
std::string charge_name = comp_k_ptr->Get_charge_name();
if (comp_k_ptr->Get_Dw() == 0)
continue;
charge_done = FALSE;
for (k1 = 0; k1 < k; k1++)
{
cxxSurfaceComp *comp_k1_ptr = &(surface_ptr2->Get_surface_comps()[k1]);
if (comp_k_ptr->Get_charge_name() ==
comp_k1_ptr->Get_charge_name())
{
charge_done = TRUE;
break;
}
}
if (charge_done)
continue;
/* Define the MCD diffusion coefficient... */
mcd_mixf = 0;
if (multi_Dflag)
{
Dp2 = comp_k_ptr->Get_Dw() * pow(por, multi_Dn);
Dp1 = 0;
if (surface_ptr1 != NULL && surface_ptr1->Get_transport())
{
for (k1 = 0; k1 < (int) surface_ptr1->Get_surface_comps().size(); k1++)
{
cxxSurfaceComp *comp_k1_ptr = &(surface_ptr1->Get_surface_comps()[k1]);
if (strcmp(comp_k1_ptr->Get_formula().c_str(),
comp_k_ptr->Get_formula().c_str()) != 0)
continue;
Dp1 =
comp_k1_ptr->Get_Dw() *
pow(cell_data[i1].por, multi_Dn);
break;
}
}
if (Dp1 > 0)
Dp2 = (Dp2 + Dp1) / 2;
/* and the mixing factor... */
mcd_mixf = Dp2 * viscos_f * A_ij * DDt / lav;
}
mixf = mixf_store + mcd_mixf;
if (mixf < 1e-8)
mixf = 0;
if (mixf > 0.99999999)
mixf = 0.99999999;
if (i1 <= count_cells)
{
surface_n1 = sum_surface_comp(&surface_n1, f1, surface_ptr2, charge_name, mixf,
surface_ptr2->Get_surface_comps()[k].Get_Dw());
f1 = 1;
}
if (i2 > 0)
{
surface_n2 = sum_surface_comp(&surface_n2, f2, surface_ptr2, charge_name, -mixf,
surface_ptr2->Get_surface_comps()[k].Get_Dw());
f2 = 1;
}
surface_n1.Set_n_user_both(i1);
}
}
if (surf1)
{
for (k = 0; k < (int) surface_ptr1->Get_surface_comps().size(); k++)
{
cxxSurfaceComp * comp_k_ptr = &(surface_ptr1->Get_surface_comps()[k]);
std::string charge_name = comp_k_ptr->Get_charge_name();
if (comp_k_ptr->Get_Dw() == 0)
continue;
charge_done = FALSE;
for (k1 = 0; k1 < k; k1++)
{
cxxSurfaceComp * comp_k1_ptr = &(surface_ptr1->Get_surface_comps()[k1]);
if (comp_k_ptr->Get_charge_name() ==
comp_k1_ptr->Get_charge_name())
{
charge_done = TRUE;
break;
}
}
if (charge_done)
continue;
/* Define the MCD diffusion coefficient... */
mcd_mixf = 0;
if (multi_Dflag)
{
if (i1 <= count_cells)
{
Dp1 =
comp_k_ptr->Get_Dw() *
pow(cell_data[i1].por, multi_Dn);
}
else
{
Dp1 = comp_k_ptr->Get_Dw() * pow(por, multi_Dn);
}
Dp2 = 0;
if (surface_ptr2 != NULL && surface_ptr2->Get_transport())
{
for (k1 = 0; k1 < (int) surface_ptr2->Get_surface_comps().size(); k1++)
{
cxxSurfaceComp * comp_k1_ptr = &(surface_ptr2->Get_surface_comps()[k1]);
if (strcmp(comp_k1_ptr->Get_formula().c_str(),
comp_k_ptr->Get_formula().c_str()) != 0)
continue;
Dp2 = comp_k1_ptr->Get_Dw() * pow(por, multi_Dn);
break;
}
}
if (Dp2 > 0)
Dp1 = (Dp1 + Dp2) / 2;
/* and the mixing factor... */
mcd_mixf = Dp1 * viscos_f * A_ij * DDt / lav;
}
mixf = mixf_store + mcd_mixf;
if (mixf < 1e-8)
mixf = 0;
if (mixf > 0.99999999)
mixf = 0.99999999;
if (i2 > 0)
{
surface_n2 = sum_surface_comp
(&surface_n2, f2, surface_ptr1, charge_name, mixf,
surface_ptr1->Get_surface_comps()[k].Get_Dw());
f2 = 1;
}
if (i1 <= count_cells)
{
surface_n1 = sum_surface_comp
(&surface_n1, f1, surface_ptr1, charge_name, -mixf,
surface_ptr1->Get_surface_comps()[k].Get_Dw());
f1 = 1;
}
surface_n2.Set_n_user_both(i2);
}
}
}
/*
* Step 3. copy surface[n1] and [n2] in a new temporary surface...
*/
if (surface_n1.Get_n_user() == -99)
continue;
Rxn_temp_surface_map[i1] = surface_n1;
{
cxxSurface t;
surface_n1 = t;
}
surface_n1.Set_n_user_both(-99);
if (surface_n2.Get_n_user() == i2)
{
surface_n2.Set_n_user_both(i2);
Rxn_temp_surface_map[i2] = surface_n2;
{
cxxSurface t;
surface_n2 = t;
}
surface_n2.Set_n_user_both(-99);
}
}
/*
* Step 4. Dispersion/diffusion is done. New surfaces can be copied in the cell's surface...
*/
//n2 = 0;
std::map<int, cxxSurface>::iterator jit = Rxn_temp_surface_map.begin();
for (; jit != Rxn_temp_surface_map.end(); jit++)
{
i = jit->first;
assert(i == jit->second.Get_n_user());
if ((i == 0 && bcon_first == 1) || (i == count_cells + 1 && bcon_last == 1))
{
continue;
}
if (i >= 0 && i <= 1 + count_cells * (1 + stag_data.count_stag))
{
surface_ptr1 = Utilities::Rxn_find(Rxn_surface_map, i);
if (surface_ptr1 != NULL)
{
Rxn_surface_map[i] = jit->second;
}
else
{
//Add to map
Rxn_surface_map[i] = jit->second;
}
}
}
return (OK);
}
/* ---------------------------------------------------------------------- */
cxxSurface Phreeqc::
sum_surface_comp(cxxSurface *source1, LDBLE f1, cxxSurface *source2,
std::string charge_name, LDBLE f2, LDBLE new_Dw)
/* ---------------------------------------------------------------------- */
{
/*
* Takes fraction f1 of the 1st surface, adds fraction f2 of the 2nd surface's comps[k] and its charge.
* The result goes in target
*/
int new_n_user;
cxxSurface *surface_ptr1, *surface_ptr2;
std::string token;
/*
* Find surfaces
*/
surface_ptr1 = source1;
if (surface_ptr1 == NULL)
{
error_string = sformatf("Null pointer for surface 1 in sum_surface.");
error_msg(error_string, STOP);
input_error++;
return (ERROR);
}
surface_ptr2 = source2;
/*
* Store data for structure surface
*/
new_n_user = surface_ptr1->Get_n_user();
cxxSurface temp_surface(*surface_ptr1);
temp_surface.Set_n_user_both(new_n_user);
temp_surface.Set_description("Copy");
temp_surface.Set_solution_equilibria(false);
temp_surface.Set_n_solution(-99);
/*
* Multiply component compositions by f1
*/
temp_surface.multiply(f1);
/*
* Add in surface_ptr2
*/
// Only components with same charge as component k
cxxSurface addee(*surface_ptr2);
addee.Get_surface_comps().clear();
addee.Get_surface_charges().clear();
for (std::vector<cxxSurfaceComp>::iterator it = surface_ptr2->Get_surface_comps().begin();
it != surface_ptr2->Get_surface_comps().end(); it++)
{
if (it->Get_charge_name() == charge_name)
{
addee.Get_surface_comps().push_back(*it);
}
}
for (std::vector<cxxSurfaceCharge>::iterator it = surface_ptr2->Get_surface_charges().begin();
it != surface_ptr2->Get_surface_charges().end(); it++)
{
if (it->Get_name() == charge_name)
{
addee.Get_surface_charges().push_back(*it);
}
}
if (f2 == 0)
f2 = 1e-30;
temp_surface.add(addee, f2);
temp_surface.Set_transport(false);
for (size_t i = 0; i < temp_surface.Get_surface_comps().size(); i++)
{
if (temp_surface.Get_surface_comps()[i].Get_charge_name() == charge_name)
{
temp_surface.Get_surface_comps()[i].Set_Dw(new_Dw);
}
if (temp_surface.Get_surface_comps()[i].Get_Dw() > 0)
{
temp_surface.Set_transport(true);
}
}
/*
* Finish up
*/
temp_surface.Sort_comps();
return (temp_surface);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
check_surfaces(cxxSurface *surface_ptr1, cxxSurface *surface_ptr2)
/* ---------------------------------------------------------------------- */
{
/* checks if surfaces can be mixed...
*/
int n_user1, n_user2, return_code;
return_code = OK;
n_user1 = surface_ptr1->Get_n_user();
n_user2 = surface_ptr2->Get_n_user();
if (surface_ptr1->Get_dl_type() != surface_ptr2->Get_dl_type())
{
error_string = sformatf(
"Surfaces %d and %d differ in definition of diffuse layer. Cannot mix.",
n_user1, n_user2);
error_msg(error_string, STOP);
return_code = ERROR;
input_error++;
}
if (surface_ptr1->Get_type() != surface_ptr2->Get_type())
{
error_string = sformatf(
"Surfaces %d and %d differ in use of electrical double layer. Cannot mix.",
n_user1, n_user2);
error_msg(error_string, STOP);
return_code = ERROR;
input_error++;
}
if (surface_ptr1->Get_only_counter_ions() != surface_ptr2->Get_only_counter_ions())
{
error_string = sformatf(
"Surfaces %d and %d differ in use of only counter ions in the diffuse layer. Cannot mix.",
n_user1, n_user2);
error_msg(error_string, STOP);
return_code = ERROR;
input_error++;
}
if (surface_ptr1->Get_related_phases() != surface_ptr2->Get_related_phases())
{
error_string = sformatf(
"Surfaces %d and %d differ in use of related phases (sites proportional to moles of an equilibrium phase). Cannot mix.",
n_user1, n_user2);
error_msg(error_string, STOP);
return_code = ERROR;
input_error++;
}
if (surface_ptr1->Get_related_rate() != surface_ptr2->Get_related_rate())
{
error_string = sformatf(
"Surfaces %d and %d differ in use of related rate (sites proportional to moles of a kinetic reactant). Cannot mix.",
n_user1, n_user2);
error_msg(error_string, STOP);
return_code = ERROR;
input_error++;
}
return (return_code);
}
/* ---------------------------------------------------------------------- */
cxxSurface Phreeqc::
mobile_surface_copy(cxxSurface *surface_old_ptr,
int n_user_new, bool move_old)
/* ---------------------------------------------------------------------- */
{
/*
* Copies mobile comps from surface_old_ptr to surf_ptr1,
* comps and charges with Dw > 0 are moved if move_old == TRUE, else copied.
* NOTE... when all comps are moved, the old surface is deleted and surfaces are sorted again,
* which will modify pointers and surface numbers.
* User number of new surface structure is n_user_new, structure is freed when n_user_new is already defined
*/
cxxSurface temp_surface(*surface_old_ptr);
/*
* Store moving surface's properties in temp_surface
*/
temp_surface.Set_n_user_both(n_user_new);
std::ostringstream desc;
desc << "Surface defined in simulation " << simulation << ".";
temp_surface.Set_description(desc.str().c_str());
temp_surface.Set_solution_equilibria(false);
temp_surface.Set_transport(true);
size_t count_comps = surface_old_ptr->Get_surface_comps().size();
int i1, i2;
i1 = i2 = 0;
temp_surface.Get_surface_comps().clear();
temp_surface.Get_surface_charges().clear();
/* see if comps must be moved, Dw > 0 */
for (size_t i = 0; i < count_comps; i++)
{
cxxSurfaceComp &comp_ref = surface_old_ptr->Get_surface_comps()[i];
if (comp_ref.Get_Dw() > 0)
{
i1++;
// copy comp
temp_surface.Get_surface_comps().push_back(comp_ref);
// copy charge, if needed
cxxSurfaceCharge *charge_ptr = temp_surface.Find_charge(comp_ref.Get_charge_name());
if (charge_ptr == NULL)
{
i2++;
cxxSurfaceCharge *old_charge_ptr = surface_old_ptr->Find_charge(comp_ref.Get_charge_name());
temp_surface.Get_surface_charges().push_back(*old_charge_ptr);
}
}
}
if (i1 > 0)
{
/* OK, store moved parts from old surface, but first:
get immobile surface comps from new surface... */
cxxSurface *surf_ptr = Utilities::Rxn_find(Rxn_surface_map, n_user_new);
if (surf_ptr != NULL)
{
for (size_t k = 0; k < surf_ptr->Get_surface_comps().size(); k++)
{
cxxSurfaceComp &comp_ref = surf_ptr->Get_surface_comps()[k];
if (comp_ref.Get_Dw() > 0)
continue;
bool charge_done(false);
for (size_t k1 = 0; k1 < k; k1++)
{
if (surf_ptr->Get_surface_comps()[k1].Get_charge_name() ==
comp_ref.Get_charge_name())
{
charge_done = true;
break;
}
}
if (charge_done)
continue;
temp_surface = sum_surface_comp(&temp_surface, 1, surf_ptr, comp_ref.Get_charge_name(), 1, 0);
}
}
// Return value is temp_surface
temp_surface.Set_n_user_both(n_user_new);
}
/* delete moved parts from old surface */
if (move_old && i1 > 0)
{
cxxSurface replace_old(temp_surface);
int n_user_old = surface_old_ptr->Get_n_user();
if ((size_t) i1 != count_comps)
{
/* redefine old surface with only unmovable comps (Dw = 0) */
/* other temp_surface members were set above */
replace_old.Set_n_user_both(n_user_old);
replace_old.Set_transport(false);
replace_old.Get_surface_comps().clear();
replace_old.Get_surface_charges().clear();
i1 = i2 = 0;
for (size_t i = 0; i < count_comps; i++)
{
if (surface_old_ptr->Get_surface_comps()[i].Get_Dw() == 0)
{
i1++;
// copy surface comp
cxxSurfaceComp & comp_ref = surface_old_ptr->Get_surface_comps()[i];
replace_old.Get_surface_comps().push_back(comp_ref);
cxxSurfaceCharge *charge_ptr = replace_old.Find_charge(comp_ref.Get_charge_name());
// copy surface charge if necessary
if (charge_ptr == NULL)
{
i2++;
cxxSurfaceCharge *old_charge_ptr = surface_old_ptr->Find_charge(comp_ref.Get_charge_name());
replace_old.Get_surface_charges().push_back(*old_charge_ptr);
}
}
}
if (replace_old.Get_surface_comps().size() == 0)
{
Rxn_surface_map.erase(surface_old_ptr->Get_n_user());
}
else
{
replace_old.Sort_comps();
Rxn_surface_map[surface_old_ptr->Get_n_user()] = replace_old;
}
}
}
temp_surface.Sort_comps();
return (temp_surface);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
diff_stag_surf(int mobile_cell)
/* ---------------------------------------------------------------------- */
/*
* Diffuse stagnant and mobile surfaces, following the steps of disp_surf.
* First the mobile/stagnant surfaces are mixed, then the stagnant surfaces
* when not already done.
* If mixing factors among the cells are defined expicitly, it is assumed that
* mixing with a lower numbered cell was done when that cell was processed:
* for any cell in MCD, need only include the mixing factors for higher numbered cells.
*/
{
int i, i1, i2, k, k1, /*n1, n2,*/ ns;
int charge_done, surf1, surf2;
LDBLE f1, f2, mixf, mixf_store;
LDBLE Dp1, Dp2;
cxxMix *mix_ptr;
cxxSurface *surface_ptr1, *surface_ptr2;
LDBLE viscos_f;
/*
* temperature and viscosity correction for MCD coefficient, D_T = D_298 * Tk * viscos_298 / (298 * viscos)
*/
viscos_f = viscos_0;
viscos_f = viscos_0_25 / viscos_f;
cxxSurface surface_n1, surface_n2;
cxxSurface *surface_n1_ptr = &surface_n1;
cxxSurface *surface_n2_ptr;
std::map<int, cxxSurface> Rxn_temp_surface_map;
for (ns = 0; ns < stag_data.count_stag; ns++)
{
i1 = mobile_cell + 1 + ns * count_cells;
if (ns == 0)
i1--;
if (cell_data[i1].por < multi_Dpor_lim)
continue;
surface_n1.Set_n_user_both(-99);
surface_n2.Set_n_user_both(-99);
surface_ptr1 = Utilities::Rxn_find(Rxn_surface_map, i1);
/*
* step 2a. mix surfaces...
*/
mix_ptr = Utilities::Rxn_find(Rxn_mix_map, i1);
if (mix_ptr == NULL)
continue;
std::vector<int> num;
std::vector<LDBLE> frac;
mix_ptr->Vectorize(num, frac);
for (size_t i3 = 0; i3 < num.size(); i3++)
{
if ((i2 = num[i3]) <= i1)
continue;
if (cell_data[i2].por < multi_Dpor_lim)
continue;
surface_ptr2 = Utilities::Rxn_find(Rxn_surface_map, i2);
surf1 = surf2 = 0;
if (surface_ptr2 != NULL)
{
if (surface_ptr2->Get_transport())
surf2 = 1;
}
if (surface_ptr1 != NULL)
{
if (surface_ptr1->Get_transport())
surf1 = 1;
}
if (!surf1 && !surf2)
continue;
mixf = mixf_store = frac[i3];;
/* find the (possibly modified) surface in cell i1... */
f1 = 1;
surface_n1_ptr = Utilities::Rxn_find(Rxn_temp_surface_map, i1);
if (surface_n1_ptr != NULL)
{
surface_n1 = *surface_n1_ptr;
//n1 = 0;
}
/* if not found... */
else
{
/* copy it from surface_ptr1... */
if (surface_ptr1 != NULL)
{
surface_n1 = *surface_ptr1;
}
else
{
/* or make it a mobile copy of the surface in cell i2... */
surface_n1 = mobile_surface_copy(surface_ptr2, i1, false);
/* limit comps to 1... */
if (surface_n1.Get_surface_charges().size() > 1)
{
std::string charge_name = surface_n1.Get_surface_charges()[0].Get_name();
surface_n1 = sum_surface_comp(&surface_n1, 0, &surface_n1, charge_name, 1,
surface_n1.Get_surface_comps()[0].Get_Dw());
}
f1 = 0;
}
}
/* find the (possibly modified) surface in cell i2... */
f2 = 1;
surface_n2_ptr = Utilities::Rxn_find(Rxn_temp_surface_map, i2);
if (surface_n2_ptr != NULL)
{
surface_n2 = *surface_n2_ptr;
}
/* if not found... */
else
{
//n2 = 1;
/* copy it from surface_ptr2... */
if (surface_ptr2 != NULL)
{
surface_n2 = *surface_ptr2;
}
else
{
/* or make it a mobile copy of the surface in cell i1... */
surface_n2 = mobile_surface_copy(surface_ptr1, i2, false);
/* limit comps to 1... */
if (surface_n2.Get_surface_charges().size() > 1)
{
std::string charge_name = surface_n2.Get_surface_charges()[0].Get_name();
surface_n2 = sum_surface_comp(&surface_n2, 0, &surface_n2, charge_name, 1,
surface_n2.Get_surface_comps()[0].Get_Dw());
}
f2 = 0;
}
}
/* For MCD, step 2b. Adapt mixf to default values... */
if (multi_Dflag)
{
mixf_store *=
(cell_data[i1].por <= cell_data[i2].por ? cell_data[i1].por : cell_data[i2].por);
mixf_store /= (default_Dw * pow(multi_Dpor, multi_Dn) * multi_Dpor);
}
/* mix in comps with the same charge structure... */
if (surf2)
{
for (k = 0; k < (int) surface_ptr2->Get_surface_comps().size(); k++)
{
cxxSurfaceComp *comp_k_ptr = &(surface_ptr2->Get_surface_comps()[k]);
std::string charge_name = comp_k_ptr->Get_charge_name();
if (comp_k_ptr->Get_Dw() == 0)
continue;
charge_done = FALSE;
for (k1 = 0; k1 < k; k1++)
{
cxxSurfaceComp *comp_k1_ptr = &(surface_ptr2->Get_surface_comps()[k1]);
if (comp_k_ptr->Get_charge_name() == comp_k1_ptr->Get_charge_name())
{
charge_done = TRUE;
break;
}
}
if (charge_done)
continue;
/* find diffusion coefficients of surfaces... */
if (multi_Dflag)
{
Dp2 = comp_k_ptr->Get_Dw() *
pow(cell_data[i2].por, multi_Dn) * viscos_f;
Dp1 = 0;
if (surf1)
{
for (k1 = 0; k1 < (int) surface_ptr1->Get_surface_comps().size(); k1++)
{
cxxSurfaceComp *comp_k1_ptr = &(surface_ptr1->Get_surface_comps()[k1]);
if (strcmp(comp_k1_ptr->Get_formula().c_str(),
comp_k_ptr->Get_formula().c_str()) != 0)
continue;
Dp1 = comp_k1_ptr->Get_Dw() * pow(cell_data[i1].por, multi_Dn) * viscos_f;
break;
}
}
if (Dp1 > 0)
Dp2 = (Dp2 + Dp1) / 2;
/* and adapt the mixing factor... */
mixf = mixf_store * Dp2;
mixf /= Utilities::Rxn_find(Rxn_solution_map, i2)->Get_mass_water();
}
if (mixf < 1e-8)
mixf = 0;
if (mixf > 0.99999999)
mixf = 0.99999999;
surface_n1 = sum_surface_comp(&surface_n1, f1, surface_ptr2, charge_name, mixf,
surface_ptr2->Get_surface_comps()[k].Get_Dw());
f1 = 1;
surface_n2 = sum_surface_comp(&surface_n2, f2, surface_ptr2, charge_name, -mixf,
surface_ptr2->Get_surface_comps()[k].Get_Dw());
}
}
if (surf1)
{
for (k = 0; k < (int) surface_ptr1->Get_surface_comps().size(); k++)
{
cxxSurfaceComp *comp_k_ptr = &(surface_ptr1->Get_surface_comps()[k]);
std::string charge_name = comp_k_ptr->Get_charge_name();
if (comp_k_ptr->Get_Dw() == 0)
continue;
charge_done = FALSE;
for (k1 = 0; k1 < k; k1++)
{
cxxSurfaceComp *comp_k1_ptr = &(surface_ptr1->Get_surface_comps()[k1]);
if (comp_k_ptr->Get_charge_name() == comp_k1_ptr->Get_charge_name())
{
charge_done = TRUE;
break;
}
}
if (charge_done)
continue;
/* find diffusion coefficients of surfaces... */
if (multi_Dflag)
{
Dp1 = comp_k_ptr->Get_Dw() * pow(cell_data[i1].por, multi_Dn) * viscos_f;
Dp2 = 0;
if (surf2)
{
for (k1 = 0; k1 < (int) surface_ptr2->Get_surface_comps().size(); k1++)
{
cxxSurfaceComp *comp_k1_ptr = &(surface_ptr2->Get_surface_comps()[k1]);
if (strcmp(comp_k1_ptr->Get_formula().c_str(),
comp_k_ptr->Get_formula().c_str()) != 0)
continue;
Dp2 = comp_k1_ptr->Get_Dw() * pow(cell_data[i2].por, multi_Dn) * viscos_f;
break;
}
}
if (Dp2 > 0)
Dp1 = (Dp1 + Dp2) / 2;
/* and adapt the mixing factor... */
mixf = mixf_store * Dp1;
mixf /= Utilities::Rxn_find(Rxn_solution_map, i1)->Get_mass_water();
}
if (mixf < 1e-8)
mixf = 0;
if (mixf > 0.99999999)
mixf = 0.99999999;
surface_n2 = sum_surface_comp(&surface_n2, f2, surface_ptr1, charge_name, mixf,
surface_ptr1->Get_surface_comps()[k].Get_Dw());
f2 = 1;
surface_n1 = sum_surface_comp(&surface_n1, f1, surface_ptr1, charge_name, -mixf,
surface_ptr1->Get_surface_comps()[k].Get_Dw());
}
}
/*
* Step 3. copy surface[n1] and [n2] in a new temporary surface...
*/
if (surface_n1.Get_n_user() == -99)
continue;
surface_n1.Set_n_user_both(i1);
Rxn_temp_surface_map[i1] = surface_n1;
assert(surface_n2.Get_n_user() != -99);
assert(surface_n2.Get_n_user() == i2);
surface_n2.Set_n_user_both(i2);
Rxn_temp_surface_map[i2] = surface_n2;
}
}
/*
* Step 4. Diffusion is done. New surfaces can be copied in the cells...
*/
//n2 = 0;
std::map<int, cxxSurface>::iterator jit = Rxn_temp_surface_map.begin();
for (; jit != Rxn_temp_surface_map.end(); jit++)
{
i = jit->first;
assert(i == jit->second.Get_n_user());
if ((i == 0 && bcon_first == 1) || (i == count_cells + 1 && bcon_last == 1))
{
continue;
}
if (i >= 0 && i <= 1 + count_cells * (1 + stag_data.count_stag))
{
surface_ptr1 = Utilities::Rxn_find(Rxn_surface_map, i);
//if (surface_ptr1 != NULL)
//{
// Rxn_surface_map[i] = jit->second;
//}
//else
{
//Add to map
Rxn_surface_map[i] = jit->second;
}
}
}
return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
reformat_surf(const char *comp_name, LDBLE fraction, const char *new_comp_name,
LDBLE new_Dw, int l_cell)
/* ---------------------------------------------------------------------- */
{
cxxSurface *surface_ptr;
if ((surface_ptr = Utilities::Rxn_find(Rxn_surface_map, l_cell)) == NULL)
return (OK);
if (surface_ptr->Find_charge(comp_name) == NULL)
return (OK);
// Assume comp_name is charge name
std::string old_charge_name = comp_name;
std::string new_charge_name = new_comp_name;
if (fraction > 0.99999999)
fraction = 0.99999999;
cxxSurface temp_surface(*surface_ptr);
cxxSurface change;
for (size_t i = 0; i < temp_surface.Get_surface_comps().size(); i++)
{
cxxSurfaceComp *comp_ptr = &(temp_surface.Get_surface_comps()[i]);
std::string charge_name = comp_ptr->Get_charge_name();
if (charge_name == old_charge_name)
{
cxxSurfaceComp comp(*comp_ptr);
comp.multiply(fraction);
std::string std_comp_name = comp_ptr->Get_formula();
Utilities::replace(comp_name, new_comp_name, std_comp_name);
comp.Set_formula(std_comp_name.c_str());
comp.Set_charge_name(new_comp_name);
cxxNameDouble nd;
cxxNameDouble::iterator it;
for (it = comp.Get_totals().begin(); it != comp.Get_totals().end(); it++)
{
std::string tot_name(it->first);
Utilities::replace(comp_name, new_comp_name, tot_name);
nd[tot_name] = it->second;
}
comp.Set_totals(nd);
change.Get_surface_comps().push_back(comp);
comp_ptr->multiply(1 - fraction);
}
}
for (size_t i = 0; i < temp_surface.Get_surface_charges().size(); i++)
{
cxxSurfaceCharge *charge_ptr = &(temp_surface.Get_surface_charges()[i]);
std::string charge_name = charge_ptr->Get_name();
if (charge_name == old_charge_name)
{
cxxSurfaceCharge charge(*charge_ptr);
charge.multiply(fraction);
std::string std_charge_name = charge_ptr->Get_name();
Utilities::replace(comp_name, new_comp_name, std_charge_name);
charge.Set_name(std_charge_name.c_str());
change.Get_surface_charges().push_back(charge);
charge_ptr->multiply(1 - fraction);
}
}
temp_surface.add(change, 1.0);
// change Dw
for (size_t i = 0; i < temp_surface.Get_surface_comps().size(); i++)
{
cxxSurfaceComp *comp_ptr = &(temp_surface.Get_surface_comps()[i]);
std::string charge_name = comp_ptr->Get_charge_name();
if (charge_name == new_charge_name)
{
comp_ptr->Set_Dw(new_Dw);
}
}
temp_surface.Set_transport(false);
for (size_t i = 0; i < temp_surface.Get_surface_comps().size(); i++)
{
if (temp_surface.Get_surface_comps()[i].Get_Dw() > 0)
{
temp_surface.Set_transport(true);
break;
}
}
temp_surface.Sort_comps();
Rxn_surface_map[l_cell] = temp_surface;
return OK;
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
viscosity(void)
/* ---------------------------------------------------------------------- */
{
/* from Atkins, 1994. Physical Chemistry, 5th ed. */
//viscos =
// pow((LDBLE) 10.,
// -(1.37023 * (tc_x - 20) +
// 0.000836 * (tc_x - 20) * (tc_x - 20)) / (109 + tc_x));
/* Huber et al., 2009, J. Phys. Chem. Ref. Data, Vol. 38, 101-125 */
LDBLE H[4] = { 1.67752, 2.20462, 0.6366564, -0.241605 };
LDBLE Tb = tk_x / 647.096, denom = H[0], mu0;
int i, j, i1;
for (i = 1; i < 4; i++)
denom += H[i] / pow(Tb, i);
mu0 = 100.0 * sqrt(Tb) / denom;
LDBLE H2[42] =
{ 5.20094e-1, 2.22531e-1, -2.81378e-1, 1.61913e-1, -3.25372e-2, 0, 0,
8.50895e-2, 9.99115e-1, -9.06851e-1, 2.57399e-1, 0, 0, 0,
-1.08374, 1.88797, -7.72479e-1, 0, 0, 0, 0,
-2.89555e-1, 1.26613, -4.89837e-1, 0, 6.98452e-2, 0, -4.35673e-3,
0, 0, -2.57040e-1, 0, 0, 8.72102e-3, 0,
0, 1.20573e-1, 0, 0, 0, 0, -5.93264e-4 };
LDBLE Rb = rho_0 / 0.322, Tb_1, Rb_1, S1 = 0.0, S2, mu1;
Tb_1 = 1.0 / Tb - 1.0; Rb_1 = Rb - 1.0;
for (i = 0; i < 6; i++)
{
S2 = 0.0;
for (j = 0; j < 7; j++)
{
i1 = 7 * i;
if (!H2[i1 + j])
continue;
if (j)
S2 += H2[i1 + j] * pow(Rb_1, j);
else
S2 += H2[i1];
}
if (i)
S1 += S2 * pow(Tb_1, i);
else
S1 += S2;
}
mu1 = exp(Rb * S1);
viscos_0 = viscos = mu0 * mu1 / 1e3;
viscos_0_25 = 0.8900239182946;
//#define OLD_VISCOSITY
#ifdef OLD_VISCOSITY
/* from Atkins, 1994. Physical Chemistry, 5th ed. */
viscos =
pow((LDBLE) 10.,
-(1.37023 * (tc_x - 20) +
0.000836 * (tc_x - 20) * (tc_x - 20)) / (109 + tc_x));
viscos_0_25 = 0.88862;
#endif
//return viscos;
if (!print_viscosity)
return viscos;
/* (modified) Jones-Dole eqn for viscosity:
viscos / viscos_0 =
1 + A * eq_tot^0.5 +
f_an * (Sum(B_i * m_i) +
Sum(D_i * m_i * ((1 + f_I) * mu_x^d3_i + (m_i * f_z)^d3_i) / (2 + f_I)))
A calculated from Falkenhagen-Dole
B_i = b0 + b1*exp(b2 * tc), b0..2 in Jones_Dole[0..2], read in SOLUTION_SPECIES
D_i = d1 * exp(d2 * tc), d1, 2 in Jones_Dole[3, 4]
d3_i in Jones_Dole[5]
Jones_Dole[6] contains the anion factor, 1 for Cl-, variable for other anions
f_z = (z * z + |z|) / 2, the contribution of the ion to mu_x, if z = 0: f_z = mu_x / m_i
f_I = variable, depends on d3_i > 1, or d3_i < 1.
tc is limited to 200 C.
A from Falkenhagen-Dole for a salt:
A = 4.3787e-14 * TK**1.5 / (eps_r**0.5)* (z1 + z2)**-0.5 / (D1 * D2) * psi
psi = (D1*z2 + D2*z1)/4 - z1*z2 * (D1-D2)**2 / ((D1*z1 + D2*z2)**0.5 + ((D1 + D2) * (z1 + z2))**0.5)**2
D1, z1 for the cation, D2, |z2| for the anion of the salt.
We use the harmonic mean of the Dw's, and the arithmetic mean of the z's,
both weighted by the equivalent concentration.
*/
LDBLE D1, D2, z1, z2, m_plus, m_min, eq_plus, eq_min, eq_dw_plus, eq_dw_min, t1, t2, t3, fan = 1;
LDBLE A, psi, Bc = 0, Dc = 0, Dw = 0.0, l_z, f_z, lm, V_an, m_an, V_Cl, tc;
m_plus = m_min = eq_plus = eq_min = eq_dw_plus = eq_dw_min = V_an = m_an = V_Cl = 0;
tc = (tc_x > 200) ? 200 : tc_x;
for (i = 0; i < (int)this->s_x.size(); i++)
{
if (s_x[i]->type != AQ && s_x[i]->type > HPLUS)
continue;
if ((lm = s_x[i]->lm) < -9)
continue;
if (s_x[i]->Jones_Dole[0] || s_x[i]->Jones_Dole[1] || s_x[i]->Jones_Dole[3])
{
s_x[i]->dw_t_visc = 0;
t1 = s_x[i]->moles / mass_water_aq_x;
l_z = fabs(s_x[i]->z);
if (l_z)
f_z = (l_z * l_z + l_z) / 2;
else
f_z = mu_x / t1;
//if data at tc's other than 25 are scarce, put the values found for 25 C in [7] and [8], optimize [1], [2], and [4]...
if (s_x[i]->Jones_Dole[7] || s_x[i]->Jones_Dole[8])
{
s_x[i]->Jones_Dole[0] = s_x[i]->Jones_Dole[7] -
s_x[i]->Jones_Dole[1] * exp(-s_x[i]->Jones_Dole[2] * 25.0);
s_x[i]->Jones_Dole[3] =
s_x[i]->Jones_Dole[8] / exp(-s_x[i]->Jones_Dole[4] * 25.0);
}
// find B * m and D * m * mu^d3
s_x[i]->dw_t_visc = (s_x[i]->Jones_Dole[0] +
s_x[i]->Jones_Dole[1] * exp(-s_x[i]->Jones_Dole[2] * tc)) * t1;
Bc += s_x[i]->dw_t_visc;
// define f_I from the exponent of the D * m^d3 term...
if (s_x[i]->Jones_Dole[5] >= 1)
t2 = mu_x / 3 / s_x[i]->Jones_Dole[5];
else if (s_x[i]->Jones_Dole[5] > 0.4)
t2 = -0.8 / s_x[i]->Jones_Dole[5];
else
t2 = -1;
t3 = (s_x[i]->Jones_Dole[3] * exp(-s_x[i]->Jones_Dole[4] * tc)) *
t1 * (pow(mu_x, s_x[i]->Jones_Dole[5])*(1 + t2) + pow(t1 * f_z, s_x[i]->Jones_Dole[5])) / (2 + t2);
s_x[i]->dw_t_visc += t3;
Dc += t3;
//output_msg(sformatf("\t%s\t%e\t%e\t%e\n", s_x[i]->name, t1, Bc, Dc ));
}
// parms for A...
if ((l_z = s_x[i]->z) == 0)
continue;
Dw = s_x[i]->dw;
if (Dw)
{
Dw *= (0.89 / viscos_0 * tk_x / 298.15);
if (s_x[i]->dw_t)
Dw *= exp(s_x[i]->dw_t / tk_x - s_x[i]->dw_t / 298.15);
}
if (l_z < 0)
{
if (!strcmp(s_x[i]->name, "Cl-"))
// volumina for f_an...
{
V_Cl = s_x[i]->logk[vm_tc];
V_an += V_Cl * s_x[i]->moles;
m_an += s_x[i]->moles;
}
else// if (s_x[i]->Jones_Dole[6])
{
V_an += s_x[i]->logk[vm_tc] * s_x[i]->Jones_Dole[6] * s_x[i]->moles;
m_an += s_x[i]->moles;
}
if (Dw)
{
// anions for A...
m_min += s_x[i]->moles;
t1 = s_x[i]->moles * l_z;
eq_min -= t1;
eq_dw_min -= t1 / Dw;
}
}
else if (Dw)
{
// cations for A...
m_plus += s_x[i]->moles;
t1 = s_x[i]->moles * l_z;
eq_plus += t1;
eq_dw_plus += t1 / Dw;
}
}
if (m_plus && m_min && eq_dw_plus && eq_dw_min)
{
z1 = eq_plus / m_plus; z2 = eq_min / m_min;
D1 = eq_plus / eq_dw_plus; D2 = eq_min / eq_dw_min;
t1 = (D1 - D2) / (sqrt(D1 * z1 + D2 * z2) + sqrt((D1 + D2) * (z1 + z2)));
psi = (D1 * z2 + D2 * z1) / 4.0 - z1 * z2 * t1 * t1;
// Here A is A * viscos_0, avoids multiplication later on...
A = 4.3787e-14 * pow(tk_x, 1.5) / (sqrt(eps_r * (z1 + z2) / ((z1 > z2) ? z1 : z2)) * (D1 * D2)) * psi;
}
else
A = 0;
viscos = viscos_0 + A * sqrt((eq_plus + eq_min) / 2 / mass_water_aq_x);
if (m_an)
V_an /= m_an;
if (!V_Cl)
V_Cl = calc_vm_Cl();
if (V_an)
fan = 2 - V_an / V_Cl;
//else
// fan = 1;
viscos += viscos_0 * fan * (Bc + Dc);
if (viscos < 0)
{
viscos = viscos_0;
warning_msg("viscosity < 0, reset to viscosity of pure water");
}
for (i = 0; i < (int)this->s_x.size(); i++)
{
s_x[i]->dw_t_visc /= (Bc + Dc);
}
return viscos;
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
calc_vm_Cl(void)
/* ---------------------------------------------------------------------- */
{
/*
* Calculate molar volume of Cl- with a Redlich type eqn:
Vm = Vm0(tc) + (Av / 2) * z^2 * I^0.5 + coef(tc) * I^(b4).
* Vm0(tc) is calc'd using supcrt parms, or from millero[0] + millero[1] * tc + millero[2] * tc^2
* for Av * z^2 * I^0.5, see Redlich and Meyer, Chem. Rev. 64, 221.
Av is in (cm3/mol)(mol/kg)^-0.5, = DH_Av.
If b_Av != 0, the extended DH formula is used: I^0.5 /(1 + b_Av * DH_B * I^0.5).
DH_Av and DH_B are from calc_dielectrics(tc, pa).
* coef(tc) = logk[vmi1] + logk[vmi2] / (TK - 228) + logk[vmi3] * (TK - 228).
* b4 = logk[vmi4], or
* coef(tc) = millero[3] + millero[4] * tc + millero[5] * tc^2
*/
LDBLE V_Cl = 0;
LDBLE pb_s = 2600. + patm_x * 1.01325, TK_s = tc_x + 45.15, sqrt_mu = sqrt(mu_x);
class species *s_ptr;
s_ptr = s_search("Cl-");
if (!s_ptr)
return V_Cl;
if (s_ptr->logk[vma1])
{
/* supcrt volume at I = 0... */
V_Cl = s_ptr->logk[vma1] + s_ptr->logk[vma2] / pb_s +
(s_ptr->logk[vma3] + s_ptr->logk[vma4] / pb_s) / TK_s -
s_ptr->logk[wref] * QBrn;
/* the ionic strength term * I^0.5... */
if (s_ptr->logk[b_Av] < 1e-5)
V_Cl += s_ptr->z * s_ptr->z * 0.5 * DH_Av * sqrt_mu;
else
{
/* limit the Debye-Hueckel slope by b... */
/* pitzer... */
//s_ptr->rxn_x.logk[vm_tc] += s_ptr->z * s_ptr->z * 0.5 * DH_Av *
// log(1 + s_ptr->logk[b_Av] * sqrt(mu_x)) / s_ptr->logk[b_Av];
/* extended DH... */
V_Cl += s_ptr->z * s_ptr->z * 0.5 * DH_Av *
sqrt_mu / (1 + s_ptr->logk[b_Av] * DH_B * sqrt_mu);
}
/* plus the volume terms * I... */
if (s_ptr->logk[vmi1] != 0.0 || s_ptr->logk[vmi2] != 0.0 || s_ptr->logk[vmi3] != 0.0)
{
LDBLE bi = s_ptr->logk[vmi1] + s_ptr->logk[vmi2] / TK_s + s_ptr->logk[vmi3] * TK_s;
if (s_ptr->logk[vmi4] == 1.0)
V_Cl += bi * mu_x;
else
V_Cl += bi * pow(mu_x, s_ptr->logk[vmi4]);
}
}
else if (s_ptr->millero[0])
{
/* Millero volume at I = 0... */
V_Cl = s_ptr->millero[0] + tc_x * (s_ptr->millero[1] + tc_x * s_ptr->millero[2]);
if (s_ptr->z)
{
/* the ionic strength terms... */
V_Cl += s_ptr->z * s_ptr->z * 0.5 * DH_Av * sqrt_mu +
(s_ptr->millero[3] + tc_x * (s_ptr->millero[4] + tc_x * s_ptr->millero[5])) * mu_x;
}
}
return V_Cl;
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
flux_mcd(const char* species_name, int option)
/* ---------------------------------------------------------------------- */
{
class species* s_ptr;
double f = 0.0;
if (state == TRANSPORT && multi_Dflag)
{
s_ptr = s_search(species_name);
if (s_ptr != NULL && s_ptr->in != FALSE && s_ptr->type < EMINUS)
{
int n = cell_no;
std::map<int, std::map<std::string, class J_ij_save> >::iterator
j_map_iter = cell_J_ij.find(n);
if (j_map_iter != cell_J_ij.end())
{
std::map<std::string, class J_ij_save>::iterator
j_save_iter = j_map_iter->second.find(species_name);
if (j_save_iter != j_map_iter->second.end())
{
if (option == 1)
{
f = j_save_iter->second.flux_t;
}
else if (option == 2)
{
f = j_save_iter->second.flux_c;
}
}
}
}
}
return (f);
}