PageRenderTime 117ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 2ms

/Software/PhreeqC/modelengine.cpp

#
C++ | 12907 lines | 9019 code | 2338 blank | 1550 comment | 3087 complexity | 3812a53d6a5af9c4679ab2ee6b2180c9 MD5 | raw file
  1. #include <math.h>
  2. #include <float.h>
  3. #include "modelengine.h"
  4. #include "conio.h"
  5. //===========================
  6. // Constructors & Destructors
  7. //===========================
  8. ModelEngine::ModelEngine(GlobalData *gd, ModelData *md):
  9. SSAssemblageModel(gd, md)
  10. {
  11. this->gd = gd;
  12. this->md = md;
  13. SetData(md);
  14. r_temp = NULL;
  15. r_temp = new Reaction;
  16. count_unknowns = 0;
  17. error = NULL;
  18. error_string[0] = '\0';
  19. }
  20. ModelEngine::~ModelEngine()
  21. {
  22. delete r_temp;
  23. }
  24. //===========================
  25. // Public functions
  26. //===========================
  27. bool ModelEngine::ConvertUnits(Solution *sol_p)
  28. {
  29. int i;
  30. LDBLE sum_solutes;
  31. Master *m;
  32. Conc *conc;
  33. sum_solutes = exp(-sol_p->ph * md->LOG_10);
  34. for (i = 0; i < sol_p->totals->Count(); i++)
  35. {
  36. conc = (*sol_p->totals)[i];
  37. conc->moles = 0.0;
  38. if (conc->name == "H(1)" || conc->name == "E")
  39. continue;
  40. if (conc->input_conc <= 0)
  41. continue;
  42. if (conc->gfw <= 0.0)
  43. {
  44. if (!conc->as.IsEmpty())
  45. {
  46. if (!ComputeGFW(conc->as, conc->gfw))
  47. return false;
  48. if (conc->name == "Alkalinity" && conc->as == "CaCO3")
  49. conc->gfw /= 2.0;
  50. }
  51. else
  52. {
  53. if ((m = gd->master_list.Search(&conc->name(0, " "), true)) != NULL)
  54. conc->gfw = m->gfw;
  55. else
  56. return false;
  57. }
  58. }
  59. conc->moles = conc->input_conc;
  60. if (Check_l_(sol_p->units))
  61. conc->moles *= (LDBLE)1.0 / (sol_p->density);
  62. conc->moles *= ConversionFactorForMoles(conc->units);
  63. if (Check_g_kgs_(conc->units) || Check_g_l_(conc->units))
  64. sum_solutes += conc->moles;
  65. else if (Check_mol_kgs_(conc->units) || Check_mol_l_(conc->units) || Check_eq_l_(conc->units))
  66. sum_solutes += (conc->moles * conc->gfw);
  67. if (Check_g_(conc->units) && conc->gfw != 0.0)
  68. conc->moles /= conc->gfw;
  69. }
  70. if (Check_kgs_(sol_p->units) || Check_l_(sol_p->units))
  71. {
  72. md->mass_water_aq_x = (LDBLE)1.0 - ((LDBLE)1e-3 * sum_solutes);
  73. for (i = 0; i < sol_p->totals->Count(); i++)
  74. (*sol_p->totals)[i]->moles /= md->mass_water_aq_x;
  75. }
  76. md->mass_water_aq_x = sol_p->mass_water;
  77. for (i = 0; i < sol_p->totals->Count(); i++)
  78. (*sol_p->totals)[i]->moles *= md->mass_water_aq_x;
  79. return true;
  80. }
  81. bool ModelEngine::Clear()
  82. {
  83. int i;
  84. Solution *sol_p = md->use.sol_p;
  85. // Clear species solution-dependent data
  86. for (i = 0; i < gd->species_list.Count(); i++)
  87. {
  88. gd->species_list[i]->in = false;
  89. //gd->species_list[i]->rewrite = false;
  90. }
  91. // Set pe structure
  92. sol_p->pe->CopyTo(md->pe_x);
  93. md->default_pe_x = sol_p->default_pe;
  94. // Clear master species solution-dependent data
  95. Master *m;
  96. PEData *ped;
  97. for (i = 0; i < gd->master_list.Count(); i++)
  98. {
  99. m = gd->master_list[i];
  100. m->in = false;
  101. m->rewrite = false;
  102. m->u = NULL;
  103. ped = (*md->pe_x)[sol_p->default_pe];
  104. m->pe_rxn = &(ped->rxn);
  105. // copy primary reaction to secondary reaction
  106. m->rxn_primary->CopyTo(m->rxn_secondary);
  107. }
  108. if (md->state == INITIAL_SOLUTION)
  109. {
  110. gd->s_h2o->secondary->in = true;
  111. gd->s_h2o->secondary->rewrite = false;
  112. gd->s_hplus->secondary->in = true;
  113. gd->s_hplus->secondary->rewrite = false;
  114. }
  115. else
  116. {
  117. gd->s_h2o->primary->in = true;
  118. gd->s_h2o->primary->rewrite = false;
  119. gd->s_hplus->primary->in = true;
  120. gd->s_hplus->primary->rewrite = false;
  121. }
  122. gd->s_eminus->primary->in = true;
  123. gd->s_eminus->primary->rewrite = false;
  124. // Set all unknown pointers to NULL
  125. md->mb_unknown = NULL;
  126. md->ah2o_unknown = NULL;
  127. md->mass_hydrogen_unknown = NULL;
  128. md->mass_oxygen_unknown = NULL;
  129. md->mu_unknown = NULL;
  130. md->alkalinity_unknown = NULL;
  131. md->carbon_unknown = NULL;
  132. md->ph_unknown = NULL;
  133. md->pe_unknown = NULL;
  134. md->charge_balance_unknown = NULL;
  135. md->solution_phase_boundary_unknown = NULL;
  136. md->pure_phase_unknown = NULL;
  137. md->exchange_unknown = NULL;
  138. md->surface_unknown = NULL;
  139. md->gas_unknown = NULL;
  140. md->s_s_unknown = NULL;
  141. // Free arrays used in model
  142. FreeModelAllocs(); //free_model_allocs ();
  143. return true;
  144. }
  145. //===========================
  146. // Private functions
  147. //===========================
  148. bool ModelEngine::GetMasterList(String &list, Master *m, ListOfPointers<Master> *copy)
  149. {
  150. String *token;
  151. Master *m_ptr, *m_1;
  152. int i;
  153. tl.SetString(list);
  154. copy->Clear();
  155. // Make list of master species pointers
  156. if (m == m->s->primary)
  157. {
  158. // First in list is primary species
  159. for (i = 0; i < gd->master_list.Count(); i++)
  160. {
  161. m_1 = gd->master_list[i];
  162. if (m_1 == m)
  163. break;
  164. }
  165. i++;
  166. if (i >= gd->master_list.Count())
  167. {
  168. // Element has only one valence
  169. copy->AddNew(m);
  170. }
  171. else
  172. {
  173. m_1 = gd->master_list[i];
  174. if (m != m_1->e->primary)
  175. {
  176. // Element has only one valence
  177. copy->AddNew(m);
  178. }
  179. else
  180. {
  181. // Element has multiple valences
  182. if (m->s->secondary == NULL)
  183. return false;
  184. copy->AddNew(m->s->secondary);
  185. while (i < gd->master_list.Count() && (m_ptr = gd->master_list[i])->e->primary == m)
  186. {
  187. if (m_ptr->s->primary == NULL)
  188. copy->AddNew(m_ptr);
  189. i++;
  190. }
  191. }
  192. }
  193. }
  194. else
  195. {
  196. // First in list is secondary species, Include all valences from input
  197. copy->AddNew(m);
  198. while (!tl.EOTL())
  199. {
  200. token = tl.NextToken();
  201. m_ptr = gd->master_list.Search(token, true);
  202. if (m_ptr != NULL)
  203. copy->AddNew(m_ptr);
  204. }
  205. }
  206. return true;
  207. }
  208. bool ModelEngine::RewriteMasterToSecondary(Master *m1, Master *m2)
  209. {
  210. //
  211. // Write equation for secondary master species in terms of another secondary master species
  212. // Store result in rxn_secondary of master_ptr.
  213. //
  214. LDBLE coef1, coef2;
  215. Master *m_p1, *m_p2;
  216. // Check that the two master species have the same primary master species
  217. m_p1 = m1->e->primary;
  218. m_p2 = m2->e->primary;
  219. if (m_p1 != m_p2 || m_p1 == NULL)
  220. return false;
  221. // Find coefficient of primary master in reaction
  222. if (!GetReactionCoef(m1->rxn_primary, m_p1->s->name, coef1) || !GetReactionCoef(m2->rxn_primary, m_p1->s->name, coef2))
  223. return false;
  224. if (Equal(coef1, (LDBLE)0.0, (LDBLE)TOLERANCE) || Equal(coef2, (LDBLE)0.0, (LDBLE)TOLERANCE))
  225. return false;
  226. // Rewrite equation to secondary master species
  227. r_temp->AddTRXN(m1->rxn_primary, 1.0, false);
  228. r_temp->AddTRXN(m2->rxn_primary, -coef1 / coef2, true);
  229. return false;
  230. }
  231. bool ModelEngine::GetReactionCoef(Reaction *r, String name, LDBLE &coef, int start)
  232. {
  233. //
  234. // Finds coefficient of token in reaction.
  235. // input: r, pointer to a reaction structure
  236. // name, string to find as reaction token
  237. // start, search start position in r (default = 1)
  238. //
  239. // Return: 0.0, if token not found
  240. // coefficient of token, if found.
  241. //
  242. coef = 0.0;
  243. ReactionToken *r_token;
  244. for (int i = start; i < r->token_list->Count(); i++)
  245. {
  246. r_token = r->token_list->Element(i);
  247. if (r_token->s->name == name)
  248. {
  249. coef = r_token->coef;
  250. break;
  251. }
  252. }
  253. return true;
  254. }
  255. bool ModelEngine::CheckIn()
  256. {
  257. //
  258. // Routine goes through trxn to determine if each master species is in this model.
  259. // Assumes equation is written in terms of primary and secondary species
  260. // Checks to see if in is TRUE or REWRITE for each species
  261. // Returns TRUE if in model FALSE if not
  262. //
  263. ReactionToken *t;
  264. for (int i = 1; i < r_temp->token_list->Count(); i++)
  265. {
  266. t = r_temp->token_list->Element(i);
  267. // Check primary master species in
  268. if (t->s->primary != NULL && t->s->primary->in)
  269. continue;
  270. // Check secondary master species
  271. if (t->s->secondary != NULL && (t->s->secondary->in || t->s->secondary->rewrite))
  272. continue;
  273. // Must be primary master species that is out
  274. return false;
  275. }
  276. return true;
  277. }
  278. bool ModelEngine::WriteMassActionEqnX()
  279. {
  280. //
  281. // Reduce mass-action equation to the master species that are in the model
  282. //
  283. LDBLE coef_e;
  284. int count;
  285. int i, count_rxn_orig;
  286. // Rewrite any secondary master species flagged REWRITE. Replace pe if necessary
  287. count = 0;
  288. bool repeat = true;
  289. while (repeat)
  290. {
  291. count++;
  292. if (count > MAX_ADD_EQUATIONS)
  293. return false;
  294. repeat = false;
  295. ReactionToken *r;
  296. count_rxn_orig = r_temp->token_list->Count();
  297. for (i = 1; i < count_rxn_orig; i++)
  298. {
  299. r = r_temp->token_list->Element(i);
  300. if (r->s->secondary == NULL)
  301. continue;
  302. if (r->s->secondary->rewrite)
  303. {
  304. repeat = true;
  305. if (!GetReactionCoef(r->s->secondary->rxn_secondary, "e-", coef_e))
  306. return false;
  307. r_temp->AddTRXN(r->s->secondary->rxn_secondary, r->coef, false);
  308. if (!Equal(coef_e, (LDBLE)0.0, (LDBLE)TOLERANCE))
  309. r_temp->AddTRXN(*r->s->secondary->pe_rxn, r->coef * coef_e, false);
  310. }
  311. }
  312. r_temp->TRXNCombine();
  313. }
  314. return true;
  315. }
  316. bool ModelEngine::AddPotentialFactor()
  317. {
  318. //
  319. // Add the potential factor to surface mass-action equations.
  320. // Factor is essentially the activity coefficient, representing
  321. // the work required to bring charged ions to the surface
  322. //
  323. int i;
  324. LDBLE sum_z;
  325. Master *master_ptr;
  326. Unknown *unknown_ptr;
  327. if (md->use.sur_p->type != DDL)
  328. return true;
  329. sum_z = 0.0;
  330. master_ptr = NULL;
  331. // Find sum of charge of aqueous species and surface master species
  332. ReactionToken *r;
  333. for (i = 1; i < r_temp->token_list->Count(); i++)
  334. {
  335. r = r_temp->token_list->Element(i);
  336. if (r->s->type == AQ || r->s == gd->s_hplus || r->s == gd->s_eminus)
  337. sum_z += r->s->z * r->coef;
  338. if (r->s->type == SURF)
  339. master_ptr = r->s->primary;
  340. }
  341. if (master_ptr == NULL)
  342. return false;
  343. // Find potential unknown for surface species
  344. unknown_ptr = FindSurfaceChargeUnknown(master_ptr->name, SURF_PSI);
  345. if (unknown_ptr == NULL)
  346. return false;
  347. master_ptr = (*unknown_ptr->master)[0]; // potential for surface component
  348. // Include psi in mass action equation
  349. if (master_ptr != NULL)
  350. {
  351. r = r_temp->token_list->AddNew();
  352. r->name = master_ptr->s->name;
  353. r->s = master_ptr->s;
  354. r->coef = (LDBLE)-2.0 * sum_z;
  355. }
  356. else
  357. return false;
  358. return true;
  359. }
  360. bool ModelEngine::AddCDMusicFactors(int n)
  361. {
  362. //
  363. // Add the potential factors for cd_music to surface mass-action equations.
  364. // Factors are essentially the activity coefficient, representing
  365. // the work required to bring charged ions to the three charge layers
  366. // of the cd_music model
  367. //
  368. int i;
  369. String name;
  370. Master *master_ptr;
  371. Unknown *unknown_ptr;
  372. ReactionToken *r;
  373. if (md->use.sur_p->type != CD_MUSIC)
  374. return true;
  375. master_ptr = NULL;
  376. // Find sum of charge of aqueous species and surface master species
  377. for (i = 1; i < r_temp->token_list->Count(); i++)
  378. {
  379. r = r_temp->token_list->Element(i);
  380. if (r->s->type == SURF)
  381. master_ptr = r->s->primary;
  382. }
  383. if (master_ptr == NULL)
  384. return false;
  385. name = master_ptr->e->name;
  386. // Find potential unknown for surface species
  387. unknown_ptr = FindSurfaceChargeUnknown (name, SURF_PSI);
  388. if (unknown_ptr == NULL)
  389. return false;
  390. master_ptr = (*unknown_ptr->master)[0]; // potential for surface component
  391. // Include psi in mass action equation
  392. r = r_temp->token_list->AddNew();
  393. r->name = master_ptr->s->name;
  394. r->s = master_ptr->s;
  395. r->coef = gd->species_list[n]->dz[0];
  396. // Plane 1
  397. unknown_ptr = FindSurfaceChargeUnknown(name, SURF_PSI1);
  398. if (unknown_ptr == NULL)
  399. return false;
  400. master_ptr = (*unknown_ptr->master)[0]; // potential for surface component
  401. // Include psi in mass action equation
  402. r = r_temp->token_list->AddNew();
  403. r->name = master_ptr->s->name;
  404. r->s = master_ptr->s;
  405. r->coef = gd->species_list[n]->dz[1];
  406. // Plane 2
  407. unknown_ptr = FindSurfaceChargeUnknown(name, SURF_PSI2);
  408. if (unknown_ptr == NULL)
  409. return false;
  410. master_ptr = (*unknown_ptr->master)[0]; // potential for surface component
  411. // Include psi in mass action equation
  412. r = r_temp->token_list->AddNew();
  413. r->name = master_ptr->s->name;
  414. r->s = master_ptr->s;
  415. r->coef = gd->species_list[n]->dz[2];
  416. return true;
  417. }
  418. Unknown *ModelEngine::FindSurfaceChargeUnknown(String &name, int plane)
  419. {
  420. int i;
  421. String token;
  422. token = name;
  423. token.Replace ("_", " ");
  424. token = token(0, " ");
  425. if (plane == SURF_PSI)
  426. token += "_CB";
  427. else if (plane == SURF_PSI1)
  428. token += "_CBb";
  429. else if (plane == SURF_PSI2)
  430. token += "_CBd";
  431. for (i = 0; i < count_unknowns; i++)
  432. if (token == (*unknown_list)[i]->name)
  433. return (*unknown_list)[i];
  434. return NULL;
  435. }
  436. bool ModelEngine::WriteMBEqnX()
  437. {
  438. //
  439. // Rewrite any secondary master species flagged REWRITE
  440. // Don't add in any pe reactions
  441. //
  442. int count;
  443. bool repeat;
  444. int i, count_rxn_orig;
  445. int j, k;
  446. char *ptr, token[DEFAULT_STRING_LENGTH];
  447. Master *master_ptr;
  448. ReactionToken *rxn_token;
  449. count = 0;
  450. repeat = true;
  451. while (repeat)
  452. {
  453. count++;
  454. if (count > MAX_ADD_EQUATIONS)
  455. return false;
  456. repeat = false;
  457. count_rxn_orig = r_temp->token_list->Count();
  458. for (i = 1; i < count_rxn_orig; i++)
  459. {
  460. rxn_token = r_temp->token_list->Element(i);
  461. if (rxn_token->s->secondary == NULL)
  462. continue;
  463. if (rxn_token->s->secondary->rewrite)
  464. {
  465. repeat = true;
  466. r_temp->AddTRXN(rxn_token->s->secondary->rxn_secondary, rxn_token->coef, false);
  467. }
  468. }
  469. r_temp->TRXNCombine();
  470. }
  471. eos_list->Clear();
  472. parent_count = 0;
  473. ReactionToken *r;
  474. ElementOfSpecies *eos_p;
  475. for (i = 1; i < r_temp->token_list->Count(); i++)
  476. {
  477. r = r_temp->token_list->Element(i);
  478. j = eos_list->Count();
  479. r->s->name.Copy(token);
  480. ptr = &token[0];
  481. if (!GetElementsInSpecies(&ptr, r->coef))
  482. return false;
  483. for (k = j; k < eos_list->Count(); k++)
  484. {
  485. eos_p = (*eos_list)[k];
  486. if (r->s->secondary != NULL)
  487. master_ptr = r->s->secondary->e->primary;
  488. else
  489. master_ptr = r->s->primary;
  490. if (eos_p->e == master_ptr->e)
  491. {
  492. eos_p->coef = 0.0;
  493. break;
  494. }
  495. }
  496. if (r->s->secondary == NULL)
  497. r->s->primary->e->name.Copy(token);
  498. else
  499. r->s->secondary->e->name.Copy(token);
  500. ptr = &token[0];
  501. if (!GetSecondaryElementsInSpecies(&ptr, r->coef))
  502. return false;
  503. }
  504. CombineElements();
  505. return true;
  506. }
  507. bool ModelEngine::AddSurfaceChargeBalance()
  508. {
  509. //
  510. // Include charge balance in list for mass-balance equations
  511. //
  512. int i;
  513. char *ptr;
  514. char token[DEFAULT_STRING_LENGTH];
  515. Master *master_ptr;
  516. Unknown *unknown_ptr;
  517. ElementOfSpecies *eos_p;
  518. if (md->use.sur_p->type != DDL)
  519. return true;
  520. master_ptr = NULL;
  521. // Find master species
  522. for (i = 0; i < eos_list->Count(); i++)
  523. {
  524. eos_p = (*eos_list)[i];
  525. if (eos_p->e->primary->s->type == SURF)
  526. {
  527. master_ptr = eos_p->e->primary;
  528. break;
  529. }
  530. }
  531. if (i >= eos_list->Count())
  532. throw exception();
  533. // Find potential unknown for surface species
  534. unknown_ptr = FindSurfaceChargeUnknown(master_ptr->e->name, SURF_PSI);
  535. if (unknown_ptr == NULL)
  536. throw exception();
  537. master_ptr = (*unknown_ptr->master)[0]; // potential for surface component
  538. master_ptr->e->name.Copy(token);
  539. ptr = &token[0];
  540. // Include charge balance in list for mass-balance equations
  541. LDBLE coef = 1.0;
  542. if (!GetSecondaryElementsInSpecies(&ptr, coef))
  543. return false;
  544. return true;
  545. }
  546. bool ModelEngine::AddCDMusicChargeBalances(int n)
  547. {
  548. //
  549. // Add the potential factor to surface mass-action equations.
  550. // Factor is essentially the activity coefficient, representing
  551. // the work required to bring charged ions to the surface
  552. //
  553. int i;
  554. char *ptr;
  555. char token[DEFAULT_STRING_LENGTH];
  556. Master *master_ptr;
  557. Unknown *unknown_ptr;
  558. ElementOfSpecies *eos_p;
  559. if (md->use.sur_p->type != CD_MUSIC)
  560. return true;
  561. master_ptr = NULL;
  562. // Find master species
  563. for (i = 0; i < eos_list->Count(); i++)
  564. {
  565. eos_p = (*eos_list)[i];
  566. if (eos_p->e->primary->s->type == SURF)
  567. {
  568. master_ptr = eos_p->e->primary;
  569. break;
  570. }
  571. }
  572. if (i >= eos_list->Count())
  573. throw exception();
  574. // Find potential unknown for plane 0
  575. unknown_ptr = FindSurfaceChargeUnknown(master_ptr->e->name, SURF_PSI);
  576. master_ptr = (*unknown_ptr->master)[0]; // potential for surface component
  577. // Include charge balance in list for mass-balance equations
  578. master_ptr->e->name.Copy(token);
  579. ptr = &token[0];
  580. if (!GetSecondaryElementsInSpecies(&ptr, gd->species_list[n]->dz[0]))
  581. return false;
  582. // Find potential unknown for plane 1
  583. unknown_ptr = FindSurfaceChargeUnknown(master_ptr->e->name, SURF_PSI1);
  584. master_ptr = (*unknown_ptr->master)[0]; // potential for surface component
  585. // Include charge balance in list for mass-balance equations
  586. master_ptr->e->name.Copy(token);
  587. ptr = &token[0];
  588. if (!GetSecondaryElementsInSpecies(&ptr, gd->species_list[n]->dz[1]))
  589. return false;
  590. // Find potential unknown for plane 2
  591. unknown_ptr = FindSurfaceChargeUnknown(master_ptr->e->name, SURF_PSI2);
  592. master_ptr = (*unknown_ptr->master)[0]; // potential for surface component
  593. // Include charge balance in list for mass-balance equations
  594. master_ptr->e->name.Copy(token);
  595. ptr = &token[0];
  596. if (!GetSecondaryElementsInSpecies(&ptr, gd->species_list[n]->dz[2]))
  597. return false;
  598. return true;
  599. }
  600. bool ModelEngine::MBForSpeciesAQ(int n)
  601. {
  602. //
  603. // Make list of mass balance and charge balance equations in which
  604. // to insert species n.
  605. //
  606. // count_mb_unknowns - number of equations and summation relations
  607. // mb_unknowns.unknown - pointer to unknown which contains row number
  608. // mb_unknowns.source - pointer to the LDBLE number to be multiplied
  609. // by coef, usually moles.
  610. // mb_unknowns.coef - coefficient of s[n] in equation or relation
  611. int i, j;
  612. Species *s;
  613. Master *master_ptr;
  614. Unknown *unknown_ptr;
  615. md->mb_unknowns->Clear();
  616. s = gd->species_list[n];
  617. // e- does not appear in any mass balances
  618. if (s->type == EMINUS)
  619. return true;
  620. // Do not include diffuse layer in cb, alk, ah2o, mu
  621. if (md->charge_balance_unknown != NULL && s->type < H2O)
  622. StoreMBUnknowns(md->charge_balance_unknown, &s->moles, s->z, &s->dg);
  623. if (md->alkalinity_unknown != NULL && s->type < H2O)
  624. StoreMBUnknowns (md->alkalinity_unknown, &s->moles, s->alk, &s->dg);
  625. if (md->ah2o_unknown != NULL && s->type < H2O)
  626. StoreMBUnknowns (md->ah2o_unknown, &s->moles, 1.0, &s->dg);
  627. if (md->mu_unknown != NULL && s->type < H2O)
  628. StoreMBUnknowns (md->mu_unknown, &s->moles, s->z * s->z, &s->dg);
  629. // Include diffuse layer in hydrogen and oxygen mass balance
  630. if (md->mass_hydrogen_unknown != NULL)
  631. {
  632. if (md->dl_type_x != NO_DL && md->state >= REACTION)
  633. StoreMBUnknowns (md->mass_hydrogen_unknown, &s->tot_g_moles, s->h - 2 * s->o, &s->dg_total_g);
  634. else
  635. StoreMBUnknowns (md->mass_hydrogen_unknown, &s->moles, s->h - 2 * s->o, &s->dg);
  636. }
  637. if (md->mass_oxygen_unknown != NULL)
  638. {
  639. if (md->dl_type_x != NO_DL && md->state >= REACTION)
  640. StoreMBUnknowns (md->mass_oxygen_unknown, &s->tot_g_moles, s->o, &s->dg_total_g);
  641. else
  642. StoreMBUnknowns (md->mass_oxygen_unknown, &s->moles, s->o, &s->dg);
  643. }
  644. // Sum diffuse layer charge into (surface + DL) charge balance
  645. SpeciesDiffLayer *sdl_p;
  646. if (md->use.sur_p != NULL && s->type < H2O && md->dl_type_x != NO_DL)
  647. {
  648. j = 0;
  649. for (i = 0; i < count_unknowns; i++)
  650. {
  651. unknown_ptr = (*unknown_list)[i];
  652. if (unknown_ptr->type == SURFACE_CB)
  653. {
  654. if (md->use.sur_p->type == CD_MUSIC)
  655. unknown_ptr = (*unknown_list)[i + 2];
  656. sdl_p = (*s->diff_layer)[j];
  657. StoreMBUnknowns(unknown_ptr, &sdl_p->g_moles, s->z, &sdl_p->dg_g_moles);
  658. j++;
  659. }
  660. }
  661. }
  662. // Other mass balances
  663. ElementOfSpecies *eos;
  664. for (i = 0; i < eos_list->Count(); i++)
  665. {
  666. eos = (*eos_list)[i];
  667. if (eos->e->master->s->type > AQ && eos->e->master->s->type < SOLID)
  668. continue;
  669. master_ptr = eos->e->master;
  670. if (master_ptr->primary && master_ptr->s->secondary != NULL)
  671. master_ptr = master_ptr->s->secondary;
  672. if (master_ptr->u == md->ph_unknown)
  673. continue;
  674. else if (master_ptr->u == md->pe_unknown)
  675. continue;
  676. else if (master_ptr->u == md->charge_balance_unknown)
  677. continue;
  678. else if (master_ptr->u == md->alkalinity_unknown)
  679. continue;
  680. else if (master_ptr->u->type == SOLUTION_PHASE_BOUNDARY)
  681. continue;
  682. if (md->dl_type_x != NO_DL && md->state >= REACTION)
  683. StoreMBUnknowns(master_ptr->u, &s->tot_g_moles, eos->coef * master_ptr->coef, &s->dg_total_g);
  684. else
  685. StoreMBUnknowns(master_ptr->u, &s->moles, eos->coef * master_ptr->coef, &s->dg);
  686. }
  687. return true;
  688. }
  689. bool ModelEngine::StoreMBUnknowns(Unknown *unknown_ptr, LDBLE *LDBLE_ptr, LDBLE coef, LDBLE *gamma_ptr)
  690. {
  691. //
  692. // Takes an unknown pointer and a coefficient and puts in
  693. // list of mb_unknowns
  694. //
  695. if (Equal(coef, (LDBLE)0.0, (LDBLE)TOLERANCE))
  696. return true;
  697. UnknownInfo *m = md->mb_unknowns->AddNew();
  698. m->u = unknown_ptr;
  699. m->source = LDBLE_ptr;
  700. m->gamma_source = gamma_ptr;
  701. m->coef = coef;
  702. return true;
  703. }
  704. bool ModelEngine::MBForSpeciesEX(int n)
  705. {
  706. //
  707. // Make list of mass balance and charge balance equations in which
  708. // to insert exchange species n.
  709. //
  710. // count_mb_unknowns - number of equations and summation relations
  711. // mb_unknowns.source - pointer to the LDBLE number to be multiplied
  712. // by coef, usually moles.
  713. // mb_unknowns.unknown - pointer to unknown which contains row number
  714. // mb_unknowns.coef - coefficient of s[n] in equation or relation
  715. //
  716. int i;
  717. Master *master_ptr;
  718. Species *s;
  719. md->mb_unknowns->Clear();
  720. // Master species for exchange do not appear in any mass balances
  721. s = gd->species_list[n];
  722. if (s->type == EX && s->primary != NULL)
  723. return true;
  724. // Include diffuse layer in hydrogen and oxygen mass balance
  725. if (md->charge_balance_unknown != NULL)
  726. StoreMBUnknowns(md->charge_balance_unknown, &s->moles, s->z, &s->dg);
  727. if (md->mass_hydrogen_unknown != NULL)
  728. StoreMBUnknowns(md->mass_hydrogen_unknown, &s->moles, s->h - 2 * s->o, &s->dg);
  729. if (md->mass_oxygen_unknown != NULL)
  730. StoreMBUnknowns(md->mass_oxygen_unknown, &s->moles, s->o, &s->dg);
  731. // Other mass balances
  732. ElementOfSpecies *eos_p;
  733. for (i = 0; i < eos_list->Count(); i++)
  734. {
  735. eos_p = (*eos_list)[i];
  736. if (eos_p->e->master->s->type > AQ && eos_p->e->master->s->type < SOLID)
  737. continue;
  738. master_ptr = eos_p->e->master;
  739. if (master_ptr->primary && master_ptr->s->secondary != NULL)
  740. master_ptr = master_ptr->s->secondary;
  741. // Special for ph_unknown, pe_unknown, and alkalinity_unknown
  742. if (master_ptr->u == md->ph_unknown)
  743. continue;
  744. else if (master_ptr->u == md->pe_unknown)
  745. continue;
  746. else if (master_ptr->u == md->alkalinity_unknown)
  747. continue;
  748. // EX, sum exchange species only into EXCH mass balance in initial calculation
  749. // into all mass balances in reaction calculation
  750. if (md->state >= REACTION || master_ptr->s->type == EX)
  751. StoreMBUnknowns(master_ptr->u, &s->moles, eos_p->coef * master_ptr->coef, &s->dg);
  752. }
  753. return true;
  754. }
  755. bool ModelEngine::MBForSpeciesSURF(int n)
  756. {
  757. //
  758. // Make list of mass balance and charge balance equations in which
  759. // to insert species n.
  760. //
  761. // count_mb_unknowns - number of equations and summation relations
  762. // mb_unknowns.source - pointer to the LDBLE number to be multiplied
  763. // by coef, usually moles.
  764. // mb_unknowns.unknown - pointer to unknown which contains row number
  765. // mb_unknowns.coef - coefficient of s[n] in equation or relation
  766. //
  767. int i;
  768. Master *master_ptr;
  769. md->mb_unknowns->Clear();
  770. // Include in charge balance, if diffuse_layer_x == false
  771. Species *s = gd->species_list[n];
  772. if (md->charge_balance_unknown != NULL && md->dl_type_x == NO_DL)
  773. StoreMBUnknowns(md->charge_balance_unknown, &s->moles, s->z, &s->dg);
  774. // Include diffuse layer in hydrogen and oxygen mass balance
  775. if (md->mass_hydrogen_unknown != NULL)
  776. StoreMBUnknowns (md->mass_hydrogen_unknown, &s->moles, s->h - 2 * s->o, &s->dg);
  777. if (md->mass_oxygen_unknown != NULL)
  778. StoreMBUnknowns (md->mass_oxygen_unknown, &s->moles, s->o, &s->dg);
  779. // Other mass balances
  780. ElementOfSpecies *eos_p;
  781. for (i = 0; i < eos_list->Count(); i++)
  782. {
  783. eos_p = (*eos_list)[i];
  784. // Skip H+, e-, and H2O
  785. if (eos_p->e->master->s->type > AQ && eos_p->e->master->s->type < SOLID)
  786. continue;
  787. master_ptr = eos_p->e->master;
  788. if (master_ptr->primary && master_ptr->s->secondary != NULL)
  789. master_ptr = master_ptr->s->secondary;
  790. // SURF_PSI, sum surface species in (surface + DL) charge balance
  791. if (master_ptr->s->type == SURF_PSI && md->use.sur_p->type != CD_MUSIC)
  792. {
  793. StoreMBUnknowns (master_ptr->u, &s->moles, s->z, &s->dg);
  794. continue;
  795. }
  796. if (master_ptr->s->type == SURF_PSI && md->use.sur_p->type == CD_MUSIC)
  797. {
  798. StoreMBUnknowns (master_ptr->u, &s->moles, s->dz[0], &s->dg);
  799. continue;
  800. }
  801. if (master_ptr->s->type == SURF_PSI1)
  802. {
  803. StoreMBUnknowns (master_ptr->u, &s->moles, s->dz[1], &s->dg);
  804. continue;
  805. }
  806. if (master_ptr->s->type == SURF_PSI2)
  807. {
  808. StoreMBUnknowns (master_ptr->u, &s->moles, s->dz[2], &s->dg);
  809. continue;
  810. }
  811. // Special for ph_unknown, pe_unknown, and alkalinity_unknown
  812. if (master_ptr->u == md->ph_unknown)
  813. continue;
  814. else if (master_ptr->u == md->pe_unknown)
  815. continue;
  816. else if (master_ptr->u == md->alkalinity_unknown)
  817. continue;
  818. // SURF, sum surface species only into SURFACE mass balance in initial calculation
  819. // into all mass balances in reaction calculation
  820. if (md->state >= REACTION || master_ptr->s->type == SURF)
  821. {
  822. StoreMBUnknowns (master_ptr->u, &s->moles, eos_p->coef * master_ptr->coef, &s->dg);
  823. }
  824. }
  825. return true;
  826. }
  827. bool ModelEngine::BuildMBSums()
  828. {
  829. //
  830. // Function builds lists sum_mb1 and sum_mb2 that describe how to sum molalities
  831. // to calculate mass balance sums, including activity of water, ionic strength,
  832. // charge balance, and alkalinity.
  833. //
  834. int i;
  835. //LDBLE *target;
  836. UnknownInfo *ui;
  837. int count = md->mb_unknowns->Count();
  838. for (i = 0; i < count; i++)
  839. {
  840. ui = (*md->mb_unknowns)[i];
  841. StoreMB(ui->source, &(ui->u->f), ui->coef);
  842. #ifdef DEBUG_MOHID
  843. if (d.debug_status)
  844. fprintf(d.buildjacobiansums_f, "BuildMBSums-1) i:%d %d %s %20.20e\n",
  845. i,
  846. ui->u->number,
  847. ui->u->name.CharPtr(),
  848. ui->coef);
  849. #endif
  850. }
  851. return true;
  852. }
  853. bool ModelEngine::BuildJacobianSums(int k)
  854. {
  855. //
  856. // Function builds lists sum_jacob1 and sum_jacob2 that describe how to sum molalities
  857. // to form jacobian.
  858. //
  859. int i, j, kk;
  860. int index;
  861. int count_g;
  862. LDBLE coef;
  863. //LDBLE *source, *target;
  864. UnknownInfo *mb_unknown;
  865. SpeciesDiffLayer *sdl_p;
  866. Unknown *u, *uprior, *u_kk;
  867. Species *s = gd->species_list[k];
  868. // Calculate jacobian coefficients for each mass balance equation
  869. for (i = 0; i < md->mb_unknowns->Count(); i++)
  870. {
  871. mb_unknown = (*md->mb_unknowns)[i];
  872. // Store d(moles) for a mass balance equation
  873. // initial solution only
  874. if (mb_unknown->u->type == SOLUTION_PHASE_BOUNDARY)
  875. continue;
  876. coef = mb_unknown->coef;
  877. StoreDN(k, mb_unknown->source, mb_unknown->u->number, coef, mb_unknown->gamma_source);
  878. #ifdef DEBUG_MOHID
  879. if (d.debug_status)
  880. fprintf(d.buildjacobiansums_f, "1) k:%d i:%d u_number:%d: %20.20e\n",
  881. k,
  882. i,
  883. mb_unknown->u->number,
  884. coef);
  885. #endif
  886. // Add extra terms for change in dg/dx in diffuse layer model
  887. if (s->type >= H2O || md->dl_type_x == NO_DL)
  888. continue;
  889. else if ((mb_unknown->u->type == MB || mb_unknown->u->type == MH || mb_unknown->u->type == MH2O) && md->state >= REACTION)
  890. {
  891. if (md->mass_oxygen_unknown != NULL)
  892. {
  893. // term for water, sum of all surfaces
  894. index = mb_unknown->u->number * (count_unknowns + 1) + md->mass_oxygen_unknown->number;
  895. StoreJacob(&s->tot_dh2o_moles, &arr[index], coef);
  896. #ifdef DEBUG_MOHID
  897. if (d.debug_status)
  898. fprintf(d.buildjacobiansums_f, "2) index:%d s_number:%d u_number:%d: %d %20.20e\n",
  899. index,
  900. s->number,
  901. md->mass_oxygen_unknown->number,
  902. coef);
  903. #endif
  904. }
  905. // terms for psi, one for each surface
  906. count_g = 0;
  907. for (j = 0; j < count_unknowns; j++)
  908. {
  909. u = (*unknown_list)[j];
  910. sdl_p = (*s->diff_layer)[count_g];
  911. if (u->type != SURFACE_CB)
  912. continue;
  913. index = mb_unknown->u->number * (count_unknowns + 1) + u->number;
  914. StoreJacob(&sdl_p->dx_moles, &arr[index], coef);
  915. count_g++;
  916. #ifdef DEBUG_MOHID
  917. if (d.debug_status)
  918. fprintf(d.buildjacobiansums_f, "3) index:%d %d %d %d %20.20e\n",
  919. index,
  920. mb_unknown->u->number,
  921. u->number,
  922. count_g,
  923. coef);
  924. #endif
  925. if (count_g >= md->use.sur_p->charge->Count())
  926. break;
  927. }
  928. // terms for related phases
  929. count_g = 0;
  930. for (j = 0; j < count_unknowns; j++)
  931. {
  932. u = (*unknown_list)[j];
  933. sdl_p = (*s->diff_layer)[count_g];
  934. if (u->type != SURFACE_CB)
  935. continue;
  936. uprior = (*unknown_list)[j - 1];
  937. // has related phase
  938. if (uprior->surface_comp->phase_name.IsEmpty())
  939. continue;
  940. // now find the related phase
  941. for (kk = count_unknowns - 1; kk >= 0; kk--)
  942. {
  943. u_kk = (*unknown_list)[kk];
  944. if (u_kk->type != PP)
  945. continue;
  946. if (u_kk->p->name == uprior->surface_comp->phase_name)
  947. break;
  948. }
  949. if (kk >= 0)
  950. {
  951. index = mb_unknown->u->number * (count_unknowns + 1) + (*unknown_list)[kk]->number;
  952. StoreJacob(&sdl_p->drelated_moles, &arr[index], coef);
  953. #ifdef DEBUG_MOHID
  954. if (d.debug_status)
  955. fprintf(d.buildjacobiansums_f, "4) index:%d %d %d %20.20e\n",
  956. index,
  957. mb_unknown->u->number,
  958. (*unknown_list)[kk]->number,
  959. coef);
  960. #endif
  961. }
  962. count_g++;
  963. if (count_g >= md->use.sur_p->charge->Count())
  964. break;
  965. }
  966. }
  967. else if (mb_unknown->u->type == SURFACE_CB)
  968. {
  969. count_g = 0;
  970. for (j = 0; j < count_unknowns; j++)
  971. {
  972. u = (*unknown_list)[j];
  973. sdl_p = (*s->diff_layer)[count_g];
  974. if (u->type != SURFACE_CB)
  975. continue;
  976. if (mb_unknown->u->number == u->number)
  977. {
  978. index = mb_unknown->u->number * (count_unknowns + 1) + u->number;
  979. StoreJacob(&sdl_p->dx_moles, &arr[index], coef);
  980. uprior = (*unknown_list)[j - 1];
  981. // term for related phase
  982. // has related phase
  983. if (!uprior->surface_comp->phase_name.IsEmpty())
  984. {
  985. // now find the related phase
  986. for (kk = count_unknowns - 1; kk >= 0; kk--)
  987. {
  988. u_kk = (*unknown_list)[kk];
  989. if (u_kk->type != PP)
  990. continue;
  991. if (u_kk->p->name == uprior->surface_comp->phase_name)
  992. break;
  993. }
  994. if (kk >= 0)
  995. {
  996. index = mb_unknown->u->number * (count_unknowns + 1) + u_kk->number;
  997. StoreJacob(&sdl_p->drelated_moles, &arr[index], coef);
  998. }
  999. }
  1000. if (md->mass_oxygen_unknown != NULL)
  1001. {
  1002. // term for water, for same surfaces
  1003. index = mb_unknown->u->number * (count_unknowns + 1) + md->mass_oxygen_unknown->number;
  1004. StoreJacob(&sdl_p->dh2o_moles, &arr[index], coef);
  1005. }
  1006. break;
  1007. }
  1008. count_g++;
  1009. if (count_g >= md->use.sur_p->charge->Count())
  1010. break;
  1011. }
  1012. }
  1013. }
  1014. return true;
  1015. }
  1016. bool ModelEngine::WriteMBForSpeciesList(int n)
  1017. {
  1018. //
  1019. // Sets up data to add to species_list
  1020. // Original secondary redox states are retained
  1021. //
  1022. int i;
  1023. char *ptr, token[DEFAULT_STRING_LENGTH];
  1024. ElementOfSpecies *new_eos;
  1025. r_temp->Reset();
  1026. r_temp->AddTRXN(gd->species_list[n]->rxn_s, 1.0, false);
  1027. //Copy to elt_list
  1028. eos_list->Clear();
  1029. parent_count = 0;
  1030. ReactionToken *r;
  1031. for (i = 1; i < r_temp->token_list->Count(); i++)
  1032. {
  1033. r = r_temp->token_list->Element(i);
  1034. if (r->s->secondary == NULL)
  1035. {
  1036. r->s->primary->e->name.Copy(token);
  1037. ptr = &token[0];
  1038. GetSecondaryElementsInSpecies(&ptr, r->coef);
  1039. }
  1040. else
  1041. {
  1042. r->s->secondary->e->name.Copy(token);
  1043. ptr = &token[0];
  1044. GetSecondaryElementsInSpecies(&ptr, r->coef);
  1045. }
  1046. }
  1047. ElementOfSpecies *eos;
  1048. for (i = 0; i < eos_list->Count(); i++)
  1049. {
  1050. eos = (*eos_list)[i];
  1051. if (eos->e->name == "O(-2)")
  1052. {
  1053. new_eos = eos_list->AddNew();
  1054. new_eos->e = gd->e_h_one;
  1055. new_eos->coef = eos->coef * 2;
  1056. }
  1057. }
  1058. CombineElements();
  1059. eos_list->CopyTo(gd->species_list[n]->eos_sys_total);
  1060. return true;
  1061. }
  1062. bool ModelEngine::BuildSpeciesList(int n)
  1063. {
  1064. //
  1065. // Builds a list that includes an entry for each master species in each
  1066. // secondary reaction. Used for summing species of each element and
  1067. // printing results.
  1068. //
  1069. int j;
  1070. SpeciesInfo *new_s;
  1071. ElementOfSpecies *eos_p;
  1072. Species *s = gd->species_list[n];
  1073. // Treat species made only with H+, e-, and H2O specially
  1074. if (IsSpecial(s))
  1075. {
  1076. new_s = md->species_info_list->AddNew();
  1077. new_s->master_s = gd->s_hplus;
  1078. new_s->s = s;
  1079. new_s->coef = 0.0;
  1080. return true;
  1081. }
  1082. // Treat exchange species specially
  1083. if (s->type == EX)
  1084. {
  1085. if (s->primary != NULL)
  1086. return true; // master species has md->zero molality
  1087. for (j = 0; j < eos_list->Count(); j++)
  1088. {
  1089. eos_p = (*eos_list)[j];
  1090. if (eos_p->e->master->s->type != EX)
  1091. continue;
  1092. new_s = md->species_info_list->AddNew();
  1093. new_s->master_s = eos_p->e->master->s;
  1094. new_s->s = s;
  1095. new_s->coef = eos_p->e->master->coef * eos_p->coef;
  1096. }
  1097. return true;
  1098. }
  1099. // Treat surface species specially
  1100. if (s->type == SURF_PSI)
  1101. return true;
  1102. if (s->type == SURF)
  1103. {
  1104. for (j = 0; j < eos_list->Count(); j++)
  1105. {
  1106. eos_p = (*eos_list)[j];
  1107. if (eos_p->e->master->s->type != SURF)
  1108. continue;
  1109. new_s = md->species_info_list->AddNew();
  1110. new_s->master_s = eos_p->e->master->s;
  1111. new_s->s = s;
  1112. new_s->coef = eos_p->e->master->coef * eos_p->coef;
  1113. }
  1114. return true;
  1115. }
  1116. // Other aqueous species
  1117. Master *master_ptr;
  1118. for (j = 0; j < eos_list->Count(); j++)
  1119. {
  1120. eos_p = (*eos_list)[j];
  1121. if (IsSpecial(eos_p->e->master->s))
  1122. continue;
  1123. if (eos_p->e->master->s->secondary != NULL)
  1124. master_ptr = eos_p->e->master->s->secondary;
  1125. else
  1126. master_ptr = eos_p->e->master->s->primary;
  1127. new_s = md->species_info_list->AddNew();
  1128. new_s->master_s = master_ptr->s;
  1129. new_s->s = s;
  1130. new_s->coef = master_ptr->coef * eos_p->coef;
  1131. }
  1132. return true;
  1133. }
  1134. bool ModelEngine::StoreMB(LDBLE * source, LDBLE * target, LDBLE coef)
  1135. {
  1136. //
  1137. // Adds item to list sum_mb1 or sum_mb2
  1138. // If coef is 1.0, adds to sum_mb1, which does not require a multiply
  1139. // else, adds to sum_mb2, which will multiply by coef
  1140. //
  1141. STCoef *new_item;
  1142. if (Equal(coef, (LDBLE)1.0, (LDBLE)TOLERANCE))
  1143. {
  1144. new_item = md->sum_mb1->AddNew();
  1145. new_item->source = source;
  1146. new_item->target = target;
  1147. }
  1148. else
  1149. {
  1150. new_item = md->sum_mb2->AddNew();
  1151. new_item->source = source;
  1152. new_item->target = target;
  1153. new_item->coef = coef;
  1154. }
  1155. return true;
  1156. }
  1157. bool ModelEngine::WritePhaseSysTotal(int n)
  1158. {
  1159. //
  1160. // Sets up data to add to species_list
  1161. // Original secondary redox states are retained
  1162. //
  1163. int i;
  1164. ReactionToken *r;
  1165. char *ptr, token[DEFAULT_STRING_LENGTH];
  1166. Phase *p = gd->phase_list[n];
  1167. // Start with secondary reaction
  1168. r_temp->Reset();
  1169. r_temp->AddTRXN(p->rxn_s, 1.0, false);
  1170. // Copy to elt_list
  1171. eos_list->Clear();
  1172. parent_count = 0;
  1173. for (i = 1; i < r_temp->token_list->Count(); i++)
  1174. {
  1175. r = r_temp->token_list->Element(i);
  1176. if (r->s->secondary == NULL)
  1177. r->s->primary->name.Copy(token); //master and element have the same name (change made in the load of database)
  1178. else
  1179. r->s->secondary->name.Copy(token);
  1180. ptr = &token[0];
  1181. GetSecondaryElementsInSpecies(&ptr, r->coef);
  1182. }
  1183. ElementOfSpecies *eos, *new_eos;
  1184. for (i = 0; i < eos_list->Count(); i++)
  1185. {
  1186. eos = (*eos_list)[i];
  1187. if (eos->e->name == "O(-2)")
  1188. {
  1189. new_eos = eos_list->AddNew();
  1190. new_eos->e = gd->e_h_one;
  1191. new_eos->coef = eos->coef * 2;
  1192. }
  1193. }
  1194. CombineElements();
  1195. eos_list->CopyTo(p->eos_sys_total);
  1196. return true;
  1197. }
  1198. bool ModelEngine::BuildSolutionPhaseBoundaries()
  1199. {
  1200. //
  1201. // Build into sums the logic to calculate inverse saturation indices for
  1202. // solution phase boundaries
  1203. //
  1204. int i, j;
  1205. Master *master_ptr;
  1206. ReactionToken *rxn_ptr;
  1207. Unknown *x;
  1208. if (md->solution_phase_boundary_unknown == NULL)
  1209. return true;
  1210. // Calculate inverse saturation index
  1211. for (i = 0; i < count_unknowns; i++)
  1212. {
  1213. x = (*unknown_list)[i];
  1214. if (x->type != SOLUTION_PHASE_BOUNDARY)
  1215. continue;
  1216. StoreMB(&x->p->lk, &x->f, 1.0);
  1217. StoreMB(&x->si, &x->f, 1.0);
  1218. if (!x->p->in)
  1219. return false;
  1220. for (j = 1; j < x->p->rxn_x->token_list->Count(); j++)
  1221. {
  1222. rxn_ptr = x->p->rxn_x->token_list->Element(j);
  1223. StoreMB(&rxn_ptr->s->la, &x->f, -rxn_ptr->coef);
  1224. }
  1225. }
  1226. // Put coefficients into array
  1227. for (i = 0; i < count_unknowns; i++)
  1228. {
  1229. x = (*unknown_list)[i];
  1230. if (x->type != SOLUTION_PHASE_BOUNDARY)
  1231. continue;
  1232. for (j = 1; j < x->p->rxn_x->token_list->Count(); j++)
  1233. {
  1234. rxn_ptr = x->p->rxn_x->token_list->Element(j);
  1235. if (rxn_ptr->s->secondary != NULL && rxn_ptr->s->secondary->in)
  1236. master_ptr = rxn_ptr->s->secondary;
  1237. else
  1238. master_ptr = rxn_ptr->s->primary;
  1239. if (master_ptr->u == NULL)
  1240. continue;
  1241. StoreJacob0(x->number, master_ptr->u->number, rxn_ptr->coef);
  1242. }
  1243. }
  1244. return true;
  1245. }
  1246. bool ModelEngine::BuildMinExch()
  1247. {
  1248. //
  1249. // Defines proportionality factor between mineral and exchanger to jacob0
  1250. //
  1251. int i, j, k, jj;
  1252. int row;
  1253. ExchComp *comp_ptr;
  1254. Master *master_ptr, *m0;
  1255. Unknown *unknown_ptr, *u_j, *u_k;
  1256. ElementOfSpecies *eos_p;
  1257. LDBLE coef;
  1258. if (md->use.exc_p == NULL)
  1259. return true;
  1260. if (!md->use.exc_p->related_phases)
  1261. return true;
  1262. for (i = 0; i < md->use.exc_p->comps->Count(); i++)
  1263. {
  1264. comp_ptr = (*md->use.exc_p->comps)[i];
  1265. if (comp_ptr->phase_name.IsEmpty())
  1266. continue;
  1267. // find unknown number
  1268. for (j = count_unknowns - 1; j >= 0; j--)
  1269. {
  1270. u_j = (*unknown_list)[j];
  1271. m0 = (*u_j->master)[0];
  1272. if (u_j->type != EXCH)
  1273. continue;
  1274. if (m0 == comp_ptr->master)
  1275. break;
  1276. }
  1277. for (k = count_unknowns - 1; k >= 0; k--)
  1278. {
  1279. u_k = (*unknown_list)[k];
  1280. if (u_k->type != PP)
  1281. continue;
  1282. if (u_k->p->name == comp_ptr->phase_name)
  1283. break;
  1284. }
  1285. if (j == -1)
  1286. return false;
  1287. if (k == -1)
  1288. continue;
  1289. // Build jacobian
  1290. // charge balance
  1291. StoreJacob0(md->charge_balance_unknown->number, u_k->number, comp_ptr->formula_z * comp_ptr->phase_proportion);
  1292. StoreSumDeltas(&delta[k], &md->charge_balance_unknown->delta, -comp_ptr->formula_z * comp_ptr->phase_proportion);
  1293. // mole balance balance
  1294. eos_list->Clear();
  1295. parent_count = 0;
  1296. CopyToTempEOSList(comp_ptr->formula_totals, 1.0);
  1297. ChangeHydrogenInEOSList(0);
  1298. for (jj = 0; jj < eos_list->Count(); jj++)
  1299. {
  1300. eos_p = (*eos_list)[jj];
  1301. master_ptr = (*eos_list)[jj]->e->primary;
  1302. if (!master_ptr->in)
  1303. master_ptr = master_ptr->s->secondary;
  1304. if (master_ptr == NULL)
  1305. return false;
  1306. if (master_ptr->s->type == EX)
  1307. {
  1308. if (!Equal(u_j->moles, u_k->moles * eos_p->coef * comp_ptr->phase_proportion, (LDBLE)5.0 * md->convergence_tolerance))
  1309. u_j->moles = u_k->moles * eos_p->coef * comp_ptr->phase_proportion;
  1310. }
  1311. coef = eos_p->coef;
  1312. if (master_ptr->s == gd->s_hplus)
  1313. {
  1314. row = md->mass_hydrogen_unknown->number;
  1315. unknown_ptr = md->mass_hydrogen_unknown;
  1316. }
  1317. else if (master_ptr->s == gd->s_h2o)
  1318. {
  1319. row = md->mass_oxygen_unknown->number;
  1320. unknown_ptr = md->mass_oxygen_unknown;
  1321. }
  1322. else
  1323. {
  1324. row = master_ptr->u->number;
  1325. unknown_ptr = master_ptr->u;
  1326. }
  1327. StoreJacob0 (row, u_k->number, coef * comp_ptr->phase_proportion);
  1328. StoreSumDeltas(&delta[k], &unknown_ptr->delta, -coef * comp_ptr->phase_proportion);
  1329. }
  1330. }
  1331. return true;
  1332. }
  1333. bool ModelEngine::BuildMinSurface()
  1334. {
  1335. //
  1336. // Defines proportionality factor between mineral and surface to jacob0
  1337. //
  1338. int i, j, k, jj, row;
  1339. List<ElementOfSpecies> *next_elt;
  1340. ElementOfSpecies *eos_p;
  1341. SurfaceComp *comp_ptr, *sc_p;
  1342. Unknown *unknown_ptr, *u_j, *u_k, *u;
  1343. Master *master_ptr, *m;
  1344. LDBLE coef;
  1345. if (md->use.sur_p == NULL)
  1346. return true;
  1347. if (!md->use.sur_p->related_phases)
  1348. return true;
  1349. for (i = 0; i < md->use.sur_p->comps->Count(); i++)
  1350. {
  1351. sc_p = (*md->use.sur_p->comps)[i];
  1352. if (sc_p->phase_name.IsEmpty())
  1353. continue;
  1354. // find unknown number
  1355. for (j = count_unknowns - 1; j >= 0; j--)
  1356. {
  1357. u_j = (*unknown_list)[j];
  1358. m = (*u_j->master)[0];
  1359. if (u_j->type != SURFACE)
  1360. continue;
  1361. if (m == sc_p->master)
  1362. break;
  1363. }
  1364. for (k = count_unknowns - 1; k >= 0; k--)
  1365. {
  1366. u_k = (*unknown_list)[k];
  1367. if (u_k->type != PP)
  1368. continue;
  1369. if (u_k->p->name == sc_p->phase_name)
  1370. break;
  1371. }
  1372. if (j == -1)
  1373. return false;
  1374. if (k == -1)
  1375. continue;
  1376. comp_ptr = u_j->surface_comp;
  1377. if (md->use.sur_p->type == CD_MUSIC)
  1378. {
  1379. // Add formula for CD_MUSIC
  1380. next_elt = comp_ptr->formula_totals;
  1381. }
  1382. else
  1383. {
  1384. // Add master species for non CD_MUSIC
  1385. next_elt = (*u_j->master)[0]->s->eos_list;
  1386. }
  1387. // update grams == moles in this case
  1388. u = (*unknown_list)[j + 1];
  1389. if (j < count_unknowns - 1 && u->type == SURFACE_CB)
  1390. StoreSumDeltas (&delta[k], &u->related_moles, -1.0);
  1391. // charge balance
  1392. StoreJacob0 (md->charge_balance_unknown->number, u_k->number, comp_ptr->formula_z * comp_ptr->phase_proportion);
  1393. StoreSumDeltas (&delta[k], &md->charge_balance_unknown->delta, -comp_ptr->formula_z * comp_ptr->phase_proportion);
  1394. eos_list->Clear();
  1395. parent_count = 0;
  1396. CopyToTempEOSList(next_elt, 1.0);
  1397. ChangeHydrogenInEOSList(0);
  1398. for (jj = 0; jj < eos_list->Count(); jj++)
  1399. {
  1400. eos_p = (*eos_list)[jj];
  1401. master_ptr = eos_p->e->primary;
  1402. if (!master_ptr->in)
  1403. master_ptr = master_ptr->s->secondary;
  1404. if (master_ptr == NULL)
  1405. return false;
  1406. if (master_ptr->s->type == SURF)
  1407. {
  1408. if (!Equal(u_j->moles, u_k->moles * eos_p->coef * comp_ptr->phase_proportion, (LDBLE)5.0 * md->convergence_tolerance))
  1409. u_j->moles = u_k->moles * eos_p->coef * comp_ptr->phase_proportion;
  1410. }
  1411. coef = eos_p->coef;
  1412. if (master_ptr->s == gd->s_hplus)
  1413. {
  1414. row = md->mass_hydrogen_unknown->number;
  1415. unknown_ptr = md->mass_hydrogen_unknown;
  1416. }
  1417. else if (master_ptr->s == gd->s_h2o)
  1418. {
  1419. row = md->mass_oxygen_unknown->number;
  1420. unknown_ptr = md->mass_oxygen_unknown;
  1421. }
  1422. else
  1423. {
  1424. row = master_ptr->u->number;
  1425. unknown_ptr = master_ptr->u;
  1426. }
  1427. StoreJacob0(row, u_k->number, coef * comp_ptr->phase_proportion);
  1428. StoreSumDeltas(&delta[k], &unknown_ptr->delta, -coef * comp_ptr->phase_proportion);
  1429. }
  1430. }
  1431. return true;
  1432. }
  1433. bool ModelEngine::BuildGasPhase()
  1434. {
  1435. //
  1436. // Put coefficients into lists to sum iaps to test for equilibrium
  1437. // Put coefficients into lists to build jacobian for
  1438. // sum of partial pressures equation and
  1439. // mass balance equations for elements contained in gases
  1440. //
  1441. int i, j, k;
  1442. int row, col;
  1443. Master *master_ptr;
  1444. ReactionToken *rxn_ptr;
  1445. GasComp *gas_comp_ptr;
  1446. Phase *phase_ptr;
  1447. Unknown *unknown_ptr;
  1448. LDBLE coef, coef_elt;
  1449. ElementOfSpecies *eos_p;
  1450. if (md->gas_unknown == NULL)
  1451. return true;
  1452. for (i = 0; i < md->use.gas_p->comps->Count(); i++)
  1453. {
  1454. // Determine elements in gas component
  1455. eos_list->Clear();
  1456. parent_count = 0;
  1457. gas_comp_ptr = (*md->use.gas_p->comps)[i];
  1458. phase_ptr = gas_comp_ptr->phase;
  1459. if (phase_ptr->rxn_x->token_list->Count() <= 0)
  1460. continue;
  1461. CopyToTempEOSList(phase_ptr->eos_list, 1.0);
  1462. ChangeHydrogenInEOSList(0);
  1463. // Build mass balance sums for each element in gas
  1464. // All elements in gas
  1465. for (j = 0; j < eos_list->Count(); j++)
  1466. {
  1467. eos_p = (*eos_list)[j];
  1468. unknown_ptr = NULL;
  1469. if (eos_p->e->name == "H")
  1470. unknown_ptr = md->mass_hydrogen_unknown;
  1471. else if (eos_p->e->name == "O")
  1472. unknown_ptr = md->mass_oxygen_unknown;
  1473. else
  1474. {
  1475. if (eos_p->e->primary->in)
  1476. unknown_ptr = eos_p->e->primary->u;
  1477. else if (eos_p->e->primary->s->secondary != NULL)
  1478. unknown_ptr = eos_p->e->primary->s->secondary->u;
  1479. }
  1480. if (unknown_ptr != NULL)
  1481. {
  1482. coef = eos_p->coef;
  1483. StoreMB(&(gas_comp_ptr->phase->moles_x), &(unknown_ptr->f), coef);
  1484. }
  1485. }
  1486. if (md->use.gas_p->type == PRESSURE)
  1487. {
  1488. // Total pressure of gases
  1489. StoreMB(&(gas_comp_ptr->phase->p_soln_x), &(md->gas_unknown->f), 1.0);
  1490. }
  1491. // Build jacobian sums for mass balance equations
  1492. for (j = 0; j < eos_list->Count(); j++)
  1493. {
  1494. eos_p = (*eos_list)[j];
  1495. unknown_ptr = NULL;
  1496. if (eos_p->e->name == "H")
  1497. unknown_ptr = md->mass_hydrogen_unknown;
  1498. else if (eos_p->e->name == "O")
  1499. unknown_ptr = md->mass_oxygen_unknown;
  1500. else
  1501. {
  1502. if (eos_p->e->primary->in)
  1503. unknown_ptr = eos_p->e->primary->u;
  1504. else if (eos_p->e->primary->s->secondary != NULL)
  1505. unknown_ptr = eos_p->e->primary->s->secondary->u;
  1506. }
  1507. if (unknown_ptr == NULL)
  1508. continue;
  1509. row = unknown_ptr->number * (count_unknowns + 1);
  1510. coef_elt = eos_p->coef;
  1511. for (k = 1; k < phase_ptr->rxn_x->token_list->Count(); k++)
  1512. {
  1513. rxn_ptr = phase_ptr->rxn_x->token_list->Element(k);
  1514. if (rxn_ptr->s->secondary != NULL && rxn_ptr->s->secondary->in)
  1515. master_ptr = rxn_ptr->s->secondary;
  1516. else
  1517. master_ptr = rxn_ptr->s->primary;
  1518. if (master_ptr == NULL)
  1519. return false;
  1520. if (master_ptr->u == NULL)
  1521. continue;
  1522. if (!master_ptr->in)
  1523. return false;
  1524. col = master_ptr->u->number;
  1525. coef = coef_elt * rxn_ptr->coef;
  1526. StoreJacob (&(gas_comp_ptr->phase->moles_x), &(arr[row + col]), coef);
  1527. }
  1528. if (md->use.gas_p->type == PRESSURE)
  1529. {
  1530. // derivative wrt total moles of gas
  1531. StoreJacob(&(gas_comp_ptr->phase->fraction_x), &(arr[row + md->gas_unknown->number]), coef_elt);
  1532. }
  1533. }
  1534. // Build jacobian sums for sum of partial pressures equation
  1535. if (md->use.gas_p->type != PRESSURE)
  1536. continue;
  1537. unknown_ptr = md->gas_unknown;
  1538. row = unknown_ptr->number * (count_unknowns + 1);
  1539. for (k = 1; k < phase_ptr->rxn_x->token_list->Count(); k++)
  1540. {
  1541. rxn_ptr = phase_ptr->rxn_x->token_list->Element(k);
  1542. if (rxn_ptr->s->secondary != NULL && rxn_ptr->s->secondary->in)
  1543. master_ptr = rxn_ptr->s->secondary;
  1544. else
  1545. master_ptr = rxn_ptr->s->primary;
  1546. if (master_ptr->u == NULL)
  1547. continue;
  1548. if (!master_ptr->in)
  1549. return false;
  1550. col = master_ptr->u->number;
  1551. coef = rxn_ptr->coef;
  1552. StoreJacob (&(gas_comp_ptr->phase->p_soln_x), &(arr[row + col]), coef);
  1553. }
  1554. }
  1555. return true;
  1556. }
  1557. bool ModelEngine::BuildSSAssemblage()
  1558. {
  1559. //
  1560. // Put coefficients into lists to sum iaps to test for equilibrium
  1561. // Put coefficients into lists to build jacobian for
  1562. // mass action equation for component
  1563. // mass balance equations for elements contained in solid solutions
  1564. //
  1565. int i, j, k, l, m;
  1566. bool stop;
  1567. int row, col;
  1568. Master *master_ptr, *m_2, *m0;
  1569. ReactionToken *rxn_ptr;
  1570. Unknown *u, *u2;
  1571. SS *s_s_ptr, *s_s_ptr_old;
  1572. //SSComp *ssc_p;
  1573. ElementOfSpecies *eos_p;
  1574. char token[MAX_LENGTH];
  1575. char *ptr;
  1576. if (md->s_s_unknown == NULL)
  1577. return true;
  1578. s_s_ptr_old = NULL;
  1579. col = 0;
  1580. for (i = 0; i < count_unknowns; i++)
  1581. {
  1582. u = (*unknown_list)[i];
  1583. if (u->type != S_S_MOLES)
  1584. continue;
  1585. s_s_ptr = u->s_s;
  1586. if (s_s_ptr != s_s_ptr_old)
  1587. {
  1588. col = u->number;
  1589. s_s_ptr_old = s_s_ptr;
  1590. }
  1591. // Calculate function value (inverse saturation index)
  1592. if (u->p->rxn_x->token_list->Count() <= 0)
  1593. continue;
  1594. StoreMB(&(u->p->lk), &(u->f), 1.0);
  1595. #ifdef DEBUG_MOHID
  1596. if (d.debug_status)
  1597. fprintf(d.buildssassemblage_f, "1) u_number:%d ss:%s u_p:%s\n", u->number, s_s_ptr->name.CharPtr(), u->p->name.CharPtr());
  1598. #endif
  1599. for (m = 1; m < u->p->rxn_x->token_list->Count(); m++)
  1600. {
  1601. rxn_ptr = u->p->rxn_x->token_list->Element(m);
  1602. StoreMB(&(rxn_ptr->s->la), &(u->f), -rxn_ptr->coef);
  1603. #ifdef DEBUG_MOHID
  1604. if (d.debug_status)
  1605. fprintf(d.buildssassemblage_f, "2) s_number:%d %20.20e\n", rxn_ptr->s->number, rxn_ptr->coef);
  1606. #endif
  1607. }
  1608. // include mole fraction
  1609. StoreMB(&(u->p->log10_fraction_x), &(u->f), 1.0);
  1610. // include activity coeficient
  1611. StoreMB(&(u->p->log10_lambda), &(u->f), 1.0);
  1612. // Put coefficients into mass action equations
  1613. // first IAP terms
  1614. for (m = 1; m < u->p->rxn_x->token_list->Count(); m++)
  1615. {
  1616. rxn_ptr = u->p->rxn_x->token_list->Element(m);
  1617. if (rxn_ptr->s->secondary != NULL && rxn_ptr->s->secondary->in)
  1618. master_ptr = rxn_ptr->s->secondary;
  1619. else
  1620. master_ptr = rxn_ptr->s->primary;
  1621. if (master_ptr == NULL || master_ptr->u == NULL)
  1622. continue;
  1623. StoreJacob0 (u->number, master_ptr->u->number, rxn_ptr->coef);
  1624. #ifdef DEBUG_MOHID
  1625. if (d.debug_status)
  1626. fprintf(d.buildssassemblage_f, "3) m_number:%d m_u_number:%d %20.20e\n", master_ptr->number, master_ptr->u->number, rxn_ptr->coef);
  1627. #endif
  1628. }
  1629. if (s_s_ptr->a0 != 0.0 || s_s_ptr->a1 != 0.0)
  1630. {
  1631. // For binary solid solution
  1632. // next dnc terms
  1633. row = u->number * (count_unknowns + 1);
  1634. if (u->s_s_comp_number == 0)
  1635. col = u->number;
  1636. else
  1637. col = u->number - 1;
  1638. StoreJacob(&(u->p->dnc), &(arr[row + col]), -1);
  1639. // next dnb terms
  1640. col++;
  1641. StoreJacob(&(u->p->dnb), &(arr[row + col]), -1);
  1642. #ifdef DEBUG_MOHID
  1643. if (d.debug_status)
  1644. fprintf(d.buildssassemblage_f, "4) row:%d col1:%d col2:%d\n", row, col - 1, col);
  1645. #endif
  1646. }
  1647. else
  1648. {
  1649. // For ideal solid solution
  1650. row = u->number * (count_unknowns + 1);
  1651. for (j = 0; j < s_s_ptr->comps_list->Count(); j++)
  1652. {
  1653. if (j != u->s_s_comp_number)
  1654. StoreJacob(&(u->p->dn), &(arr[row + col + j]), -1.0);
  1655. else
  1656. StoreJacob(&(u->p->dnb), &(arr[row + col + j]), -1.0);
  1657. #ifdef DEBUG_MOHID
  1658. if (d.debug_status)
  1659. fprintf(d.buildssassemblage_f, "5) row:%d col:%d j:%d s_s_comp_number:%d\n", row, col, j, u->s_s_comp_number);
  1660. #endif
  1661. }
  1662. }
  1663. // Put coefficients into mass balance equations
  1664. eos_list->Clear();
  1665. parent_count = 0;
  1666. u->p->formula.Copy(token);
  1667. ptr = &token[0];
  1668. GetElementsInSpecies(&ptr, 1.0);
  1669. // Go through elements in phase
  1670. ChangeHydrogenInEOSList(0);
  1671. for (j = 0; j < eos_list->Count(); j++)
  1672. {
  1673. eos_p = (*eos_list)[j];
  1674. if (eos_p->e->name == "H" && md->mass_hydrogen_unknown != NULL)
  1675. {
  1676. StoreJacob0(md->mass_hydrogen_unknown->number, u->number, -eos_p->coef);
  1677. StoreSumDeltas (&(delta[i]), &md->mass_hydrogen_unknown->delta, eos_p->coef);
  1678. #ifdef DEBUG_MOHID
  1679. if (d.debug_status)
  1680. fprintf(d.buildssassemblage_f, "6) %d %d %20.20e\n",
  1681. i,
  1682. md->mass_hydrogen_unknown->number,
  1683. eos_p->coef);
  1684. #endif
  1685. }
  1686. else if (eos_p->e->name == "O" && md->mass_oxygen_unknown != NULL)
  1687. {
  1688. StoreJacob0(md->mass_oxygen_unknown->number, u->number, -eos_p->coef);
  1689. StoreSumDeltas(&(delta[i]), &md->mass_oxygen_unknown->delta, eos_p->coef);
  1690. #ifdef DEBUG_MOHID
  1691. if (d.debug_status)
  1692. fprintf(d.buildssassemblage_f, "7) %d %d %20.20e\n",
  1693. i,
  1694. md->mass_oxygen_unknown->number,
  1695. eos_p->coef);
  1696. #endif
  1697. }
  1698. else
  1699. {
  1700. master_ptr = eos_p->e->primary;
  1701. if (!master_ptr->in)
  1702. master_ptr = master_ptr->s->secondary;
  1703. if (master_ptr == NULL || !master_ptr->in)
  1704. return false;
  1705. else if (master_ptr->in)
  1706. {
  1707. // Master species is in model
  1708. StoreJacob0 (master_ptr->u->number, u->number, -eos_p->coef);
  1709. StoreSumDeltas (&delta[i], &master_ptr->u->delta, eos_p->coef);
  1710. #ifdef DEBUG_MOHID
  1711. if (d.debug_status)
  1712. fprintf(d.buildssassemblage_f, "8) %d %d %20.20e\n",
  1713. i,
  1714. master_ptr->u->number,
  1715. eos_p->coef);
  1716. #endif
  1717. }
  1718. else if (master_ptr->rewrite)
  1719. {
  1720. // Master species in equation needs to be rewritten
  1721. stop = false;
  1722. for (k = 0; k < count_unknowns; k++)
  1723. {
  1724. u2 = (*unknown_list)[k];
  1725. if (u2->type != MB)
  1726. continue;
  1727. for (l = 0; l < u2->master->Count() != NULL; l++)
  1728. {
  1729. m_2 = (*u2->master)[l];
  1730. if (m_2 == master_ptr)
  1731. {
  1732. m0 = (*u2->master)[0];
  1733. StoreJacob0(m0->u->number, u->number, -eos_p->coef);
  1734. StoreSumDeltas (&delta[i], &m0->u->delta, eos_p->coef);
  1735. #ifdef DEBUG_MOHID
  1736. if (d.debug_status)
  1737. fprintf(d.buildssassemblage_f, "9) %d %d %20.20e\n",
  1738. i,
  1739. m0->u->number,
  1740. eos_p->coef);
  1741. #endif
  1742. stop = true;
  1743. break;
  1744. }
  1745. }
  1746. if (stop)
  1747. break;
  1748. }
  1749. }
  1750. }
  1751. }
  1752. }
  1753. #ifdef DEBUG_MOHID
  1754. if (d.debug_status)
  1755. fprintf(d.buildssassemblage_f, "FIM)-------------\n");
  1756. #endif
  1757. return true;
  1758. }
  1759. bool ModelEngine::SaveModel()
  1760. {
  1761. return true;
  1762. }
  1763. bool ModelEngine::StoreDN(int k, LDBLE * source, int row, LDBLE coef_in, LDBLE * gamma_source)
  1764. {
  1765. //
  1766. // Stores the terms for d moles of species k in solution into row, multiplied
  1767. // by coef_in
  1768. //
  1769. int col, j;
  1770. LDBLE coef;
  1771. ReactionToken *rxn_ptr;
  1772. Master *master_ptr;
  1773. if (Equal(coef_in, (LDBLE)0.0, (LDBLE)TOLERANCE))
  1774. return true;
  1775. // Gamma term for d molality of species
  1776. // Note dg includes molality as a factor
  1777. row = row * (count_unknowns + 1);
  1778. Species *s = gd->species_list[k];
  1779. #ifdef DEBUG_MOHID
  1780. if (d.debug_status)
  1781. fprintf(d.buildjacobiansums_f, "StoreDN-1) %d %s\n",
  1782. row,
  1783. s->name.CharPtr());
  1784. #endif
  1785. if (s->type != SURF && s != gd->s_h2o && gamma_source != NULL)
  1786. {
  1787. if (gamma_source != NULL)
  1788. {
  1789. StoreJacob (gamma_source, &arr[row + md->mu_unknown->number], (LDBLE)-1.0 * coef_in);
  1790. #ifdef DEBUG_MOHID
  1791. if (d.debug_status)
  1792. fprintf(d.buildjacobiansums_f, "StoreDN-2) %d %20.20e\n",
  1793. row + md->mu_unknown->number,
  1794. coef_in);
  1795. #endif
  1796. }
  1797. }
  1798. // Mass of water factor
  1799. if (md->mass_oxygen_unknown != NULL && s->type != EX && s->type != SURF)
  1800. {
  1801. StoreJacob (source, &arr[row + md->mass_oxygen_unknown->number], coef_in);
  1802. #ifdef DEBUG_MOHID
  1803. if (d.debug_status)
  1804. fprintf(d.buildjacobiansums_f, "StoreDN-3) %d %20.20e\n",
  1805. row + md->mass_oxygen_unknown->number,
  1806. coef_in);
  1807. #endif
  1808. }
  1809. if (s == gd->s_h2o)
  1810. return true;
  1811. for (j = 1; j < s->rxn_x->token_list->Count(); j++)
  1812. {
  1813. rxn_ptr = s->rxn_x->token_list->Element(j);
  1814. if (rxn_ptr->s->secondary != NULL && rxn_ptr->s->secondary->in)
  1815. master_ptr = rxn_ptr->s->secondary;
  1816. else
  1817. master_ptr = rxn_ptr->s->primary;
  1818. if (master_ptr->u == NULL)
  1819. continue;
  1820. col = master_ptr->u->number;
  1821. coef = coef_in * rxn_ptr->coef;
  1822. StoreJacob (source, &arr[row + col], coef);
  1823. #ifdef DEBUG_MOHID
  1824. if (d.debug_status)
  1825. fprintf(d.buildjacobiansums_f, "StoreDN-4) %d %s\n",
  1826. row + col,
  1827. coef);
  1828. #endif
  1829. }
  1830. return true;
  1831. }
  1832. bool ModelEngine::StoreJacob(LDBLE * source, LDBLE * target, LDBLE coef)
  1833. {
  1834. // Adds a new item to either sum_jacob1 or sum_jacob2
  1835. // If coef is 1.0, adds to sum_jacob1, which does not require a multiply
  1836. // Otherwise, adds to sum_jacob2, which allows multiply by coef
  1837. STCoef *n_i;
  1838. if (Equal(coef, (LDBLE)1.0, (LDBLE)TOLERANCE))
  1839. {
  1840. n_i = md->sum_jacob1->AddNew();
  1841. n_i->source = source;
  1842. n_i->target = target;
  1843. }
  1844. else
  1845. {
  1846. n_i = md->sum_jacob2->AddNew();
  1847. n_i->source = source;
  1848. n_i->target = target;
  1849. n_i->coef = coef;
  1850. }
  1851. return true;
  1852. }
  1853. bool ModelEngine::IsSpecial(Species *spec)
  1854. {
  1855. //
  1856. // Checks to see if a species is composed of only H, O, and e-
  1857. // Returns TRUE if true
  1858. // FALSE if not
  1859. //
  1860. ReactionToken *token_ptr;
  1861. for (int i = 1; i < spec->rxn_s->token_list->Count(); i++)
  1862. {
  1863. token_ptr = spec->rxn_s->token_list->Element(i);
  1864. if (token_ptr->s != gd->s_hplus && token_ptr->s != gd->s_h2o && token_ptr->s != gd->s_eminus)
  1865. return false;
  1866. }
  1867. return true;
  1868. }
  1869. bool ModelEngine::StoreJacob0(int row, int column, LDBLE coef)
  1870. {
  1871. //
  1872. // Stores in list a constant coef which will be added into jacobian array
  1873. //
  1874. STCoef *n_i = md->sum_jacob0->AddNew();
  1875. n_i->target = &arr[row * (count_unknowns + 1) + column];
  1876. n_i->coef = coef;
  1877. return true;
  1878. }
  1879. bool ModelEngine::ChangeHydrogenInEOSList(LDBLE charge)
  1880. {
  1881. int j;
  1882. int found_h, found_o;
  1883. LDBLE coef_h, coef_o, coef;
  1884. found_h = -1;
  1885. found_o = -1;
  1886. coef_h = 0.0;
  1887. coef_o = 0.0;
  1888. CombineElements();
  1889. ElementOfSpecies *eos_p;
  1890. for (j = 0; j < eos_list->Count(); j++)
  1891. {
  1892. eos_p = (*eos_list)[j];
  1893. if (eos_p->e->name == "H")
  1894. {
  1895. found_h = j;
  1896. coef_h = eos_p->coef;
  1897. }
  1898. else if (eos_p->e->name == "O")
  1899. {
  1900. found_o = j;
  1901. coef_o = eos_p->coef;
  1902. }
  1903. }
  1904. coef = coef_h - 2 * coef_o - charge;
  1905. if (found_h < 0 && found_o < 0)
  1906. return true;
  1907. if (found_h >= 0 && found_o < 0)
  1908. return true;
  1909. if (found_h < 0 && found_o >= 0)
  1910. {
  1911. ElementOfSpecies *neos = eos_list->AddNew();
  1912. neos->name = gd->s_hplus->primary->e->name;
  1913. neos->e = gd->s_hplus->primary->e;
  1914. neos->coef = coef;
  1915. CombineElements();
  1916. return true;
  1917. }
  1918. eos_p = (*eos_list)[found_h];
  1919. eos_p->coef = coef;
  1920. return true;
  1921. }
  1922. bool ModelEngine::StoreSumDeltas(LDBLE * source, LDBLE * target, LDBLE coef)
  1923. {
  1924. //
  1925. // List sum_delta is summed to determine the change in the mass of
  1926. // each element due to mass transfers of minerals, changes show up
  1927. // in x[i]->delta. These may be multiplied by a factor under some
  1928. // situations where the entire calculated step is not taken
  1929. //
  1930. STCoef * nstc = md->sum_delta->AddNew();
  1931. nstc->source = source;
  1932. nstc->target = target;
  1933. nstc->coef = coef;
  1934. return true;
  1935. }
  1936. CONVERGE_RESULT ModelEngine::ExecuteModel()
  1937. {
  1938. //
  1939. // ExecuteModel is called after the equations have been set up by prep
  1940. // and initial guesses have been made in set.
  1941. //
  1942. // Here is the outline of the calculation sequence:
  1943. // residuals--residuals are calculated, if small we are done
  1944. // sum_jacobian--jacobian is calculated
  1945. // ineq--inequality solver is called
  1946. // reset--estimates of unknowns revised, if changes are small solution
  1947. // has been found, usually convergence is found in residuals.
  1948. // gammas--new activity coefficients
  1949. // molalities--calculate molalities
  1950. // mb_sums--calculate mass-balance sums
  1951. // mb_gases--decide if gas_phase exists
  1952. // mb_s_s--decide if solid_solutions exists
  1953. // switch_bases--check to see if new basis species is needed
  1954. // reprep--rewrite equations with new basis species if needed
  1955. // revise_guesses--revise unknowns to get initial mole balance
  1956. // check_residuals--check convergence one last time
  1957. // sum_species--calculate sums of elements from species concentrations
  1958. //
  1959. // An additional pass through may be needed if unstable phases still exist
  1960. // in the phase assemblage.
  1961. int kode, return_kode;
  1962. CONVERGE_RESULT r;
  1963. int count_infeasible, count_basis_change;
  1964. bool mass_water_switch_save, result;
  1965. String filename;
  1966. mass_water_switch_save = md->mass_water_switch;
  1967. if (!mass_water_switch_save && md->delay_mass_water) //delay_mass_water will be always FALSE (used to debug in original version)
  1968. md->mass_water_switch = true;
  1969. md->pe_step_size_now = md->pe_step_size;
  1970. md->step_size_now = md->step_size;
  1971. md->iterations = 0;
  1972. count_basis_change = 0;
  1973. count_infeasible = 0;
  1974. md->stop_program = false;
  1975. md->remove_unstable_phases = false;
  1976. for (int i = 0;; i++)
  1977. {
  1978. if (!MBGases())
  1979. return ERROR_CR;
  1980. if (!MBSS())
  1981. return ERROR_CR;
  1982. kode = 1;
  1983. while ((r = Residuals()) != CONVERGED_CR || md->remove_unstable_phases)
  1984. {
  1985. if (r == ERROR_CR)
  1986. return ERROR_CR;
  1987. md->iterations++;
  1988. if (md->iterations > md->itmax)
  1989. {
  1990. // Iterations exceeded
  1991. md->stop_program = true;
  1992. break;
  1993. }
  1994. if (!JacobianSums ())
  1995. return ERROR_CR;
  1996. if (!NumericalJacobian ())
  1997. return ERROR_CR;
  1998. // Full matrix with pure phases
  1999. if (r == OK_CR || md->remove_unstable_phases)
  2000. {
  2001. return_kode = Ineq(kode);
  2002. if (return_kode != OK)
  2003. count_infeasible++;
  2004. if (return_kode == 2)
  2005. {
  2006. Ineq(0);
  2007. }
  2008. Reset();
  2009. }
  2010. #ifdef DEBUG_MOHID
  2011. if (d.debug_status)
  2012. fprintf(d.gammas_f, "called_by_ExecuteModel-1)\n");
  2013. #endif
  2014. result = Gammas(md->mu_x);
  2015. if (!result)
  2016. return ERROR_CR;
  2017. #ifdef DEBUG_MOHID
  2018. if (d.debug_status)
  2019. fprintf(d.molalities_f, "called_by_ExecuteModel-1)\n");
  2020. #endif
  2021. if (!Molalities(false))
  2022. {
  2023. #ifdef DEBUG_MOHID
  2024. if (d.debug_status)
  2025. fprintf(d.reviseguesses_f, "called_by_ExecuteModel-1)\n");
  2026. #endif
  2027. if (!ReviseGuesses ())
  2028. return ERROR_CR;
  2029. }
  2030. if (md->use.sur_p != NULL && md->use.sur_p->dl_type != NO_DL && md->use.sur_p->related_phases)
  2031. {
  2032. if (!InitialSurfaceWater())
  2033. return ERROR_CR;
  2034. }
  2035. if (!MBSums())
  2036. return ERROR_CR;
  2037. if (!MBGases())
  2038. return ERROR_CR;
  2039. if (!MBSS())
  2040. return ERROR_CR;
  2041. // Switch bases if necessary
  2042. if (SwitchBases())
  2043. {
  2044. count_basis_change++;
  2045. if (!Reprep ())
  2046. return ERROR_CR;
  2047. result = Gammas(md->mu_x);
  2048. if (!result)
  2049. return ERROR_CR;
  2050. #ifdef DEBUG_MOHID
  2051. if (d.debug_status)
  2052. fprintf(d.molalities_f, "called_by_ExecuteModel-2)\n");
  2053. #endif
  2054. if (!Molalities (true))
  2055. return ERROR_CR;
  2056. if (md->use.sur_p != NULL && md->use.sur_p->dl_type != NO_DL && md->use.sur_p->related_phases)
  2057. if (!InitialSurfaceWater ())
  2058. return ERROR_CR;
  2059. #ifdef DEBUG_MOHID
  2060. if (d.debug_status)
  2061. fprintf(d.reviseguesses_f, "called_by_ExecuteModel-2)\n");
  2062. #endif
  2063. if (!ReviseGuesses ())
  2064. return ERROR_CR;
  2065. if (!MBSums())
  2066. return ERROR_CR;
  2067. if (!MBGases())
  2068. return ERROR_CR;
  2069. if (!MBSS())
  2070. return ERROR_CR;
  2071. }
  2072. if (md->stop_program)
  2073. break;
  2074. }
  2075. if (md->stop_program)
  2076. break;
  2077. if (!CheckResiduals())
  2078. {
  2079. md->stop_program = true;
  2080. break;
  2081. }
  2082. if (!md->remove_unstable_phases && !mass_water_switch_save && md->mass_water_switch)
  2083. {
  2084. md->mass_water_switch = false;
  2085. continue;
  2086. }
  2087. if (!md->remove_unstable_phases)
  2088. break;
  2089. }
  2090. //d.PrintLAToFile("model-26", count_unknowns, &gd->x);
  2091. //d.PrintSpeciesInfoToFile("ExecuteModel-26", md->species_info_list, md, gd);
  2092. if (md->stop_program)
  2093. return ERROR_CR;
  2094. return OK_CR;
  2095. }
  2096. bool ModelEngine::MBGases()
  2097. {
  2098. md->gas_in = false;
  2099. if (md->gas_unknown == NULL || md->use.gas_p == NULL)
  2100. return true;
  2101. if (md->use.gas_p->type == PRESSURE && (md->gas_unknown->f > (md->use.gas_p->total_p + 1e-7) || md->gas_unknown->moles > MIN_TOTAL))
  2102. md->gas_in = true;
  2103. return true;
  2104. }
  2105. bool ModelEngine::MBSS()
  2106. {
  2107. int i, j, k;
  2108. LDBLE lp, log10_iap, total_moles;
  2109. LDBLE iapc, iapb, kc, kb, lc, lb, xcaq, xbaq, xb, xc;
  2110. LDBLE sigmapi_aq, sigmapi_solid;
  2111. LDBLE total_p;
  2112. SS *s_s_ptr;
  2113. ReactionToken *rxn_ptr;
  2114. Phase *phase_ptr;
  2115. SSComp *ssc_p, *ssc_p0, *ssc_p1;
  2116. // Determines whether solid solution equation is needed
  2117. if (md->s_s_unknown == NULL || md->use.ssa_p == NULL)
  2118. return true;
  2119. for (i = 0; i < md->use.ssa_p->ss_list->Count(); i++)
  2120. {
  2121. s_s_ptr = (*md->use.ssa_p->ss_list)[i];
  2122. total_moles = 0;
  2123. for (j = 0; j < s_s_ptr->comps_list->Count(); j++)
  2124. {
  2125. ssc_p = (*s_s_ptr->comps_list)[j];
  2126. total_moles += ssc_p->moles;
  2127. }
  2128. #ifdef DEBUG_MOHID
  2129. if (d.debug_status)
  2130. fprintf(d.set_f, "MBSS-1) %20.20e\n", total_moles);
  2131. #endif
  2132. if (total_moles > 1e-13)
  2133. s_s_ptr->s_s_in = true;
  2134. else if (s_s_ptr->a0 != 0.0 || s_s_ptr->a1 != 0.0)
  2135. {
  2136. ssc_p0 = (*s_s_ptr->comps_list)[0];
  2137. ssc_p1 = (*s_s_ptr->comps_list)[1];
  2138. // Calculate IAPc and IAPb
  2139. if (ssc_p0->phase->rxn_x->token_list->Count() > 0)
  2140. {
  2141. log10_iap = 0;
  2142. //ToDo: Check if the "translation" of the original code above was done right
  2143. //for (rxn_ptr = ss0->phase->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++)
  2144. for (k = 1; k < ssc_p0->phase->rxn_x->token_list->Count(); k++)
  2145. {
  2146. rxn_ptr = ssc_p0->phase->rxn_x->token_list->Element(k);
  2147. log10_iap += rxn_ptr->s->la * rxn_ptr->coef;
  2148. }
  2149. iapc = exp (log10_iap * md->LOG_10);
  2150. }
  2151. else
  2152. {
  2153. iapc = (LDBLE)1e-99;
  2154. }
  2155. if (ssc_p1->phase->rxn_x->token_list->Count() > 0)
  2156. {
  2157. log10_iap = 0;
  2158. //ToDo: Check if the "translation" of the original code above was done right
  2159. //for (rxn_ptr = ss1->phase->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++)
  2160. for (k = 1; k < ssc_p1->phase->rxn_x->token_list->Count(); k++)
  2161. {
  2162. rxn_ptr = ssc_p1->phase->rxn_x->token_list->Element(k);
  2163. log10_iap += rxn_ptr->s->la * rxn_ptr->coef;
  2164. }
  2165. iapb = exp (log10_iap * md->LOG_10);
  2166. }
  2167. else
  2168. {
  2169. iapb = (LDBLE)1e-99;
  2170. }
  2171. // Calculate sigma pi, aq
  2172. sigmapi_aq = iapc + iapb;
  2173. // Calculate xc,aq and xb, aq
  2174. xcaq = iapc / (iapb + iapc);
  2175. xbaq = iapb / (iapb + iapc);
  2176. // Get Kc and Kb
  2177. kc = exp (ssc_p0->phase->lk * md->LOG_10);
  2178. kb = exp (ssc_p1->phase->lk * md->LOG_10);
  2179. // Solve for xb
  2180. xb = SSRoot(s_s_ptr->a0, s_s_ptr->a1, kc, kb, xcaq, xbaq);
  2181. // Calculate lambdac and lambdab
  2182. xc = 1 - xb;
  2183. lc = exp ((s_s_ptr->a0 - s_s_ptr->a1 * (-4 * xb + 3)) * xb * xb);
  2184. lb = exp ((s_s_ptr->a0 + s_s_ptr->a1 * (4 * xb - 1)) * xc * xc);
  2185. // Calculate sigma pi, solid
  2186. sigmapi_solid = xb * lb * kb + xc * lc * kc;
  2187. // If Sigma pi, solid < sigma pi, aq, then use eqns
  2188. if (sigmapi_solid < sigmapi_aq)
  2189. s_s_ptr->s_s_in = true;
  2190. else
  2191. s_s_ptr->s_s_in = false;
  2192. }
  2193. else
  2194. {
  2195. // Calculate total mole fraction from solution activities
  2196. total_p = 0;
  2197. for (j = 0; j < s_s_ptr->comps_list->Count(); j++)
  2198. {
  2199. phase_ptr = (*s_s_ptr->comps_list)[j]->phase;
  2200. if (phase_ptr->in)
  2201. {
  2202. lp = -phase_ptr->lk;
  2203. //ToDo: Check if the "translation" of the original code above was done right
  2204. //for (rxn_ptr = s_s_ptr->comps[j].phase->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++)
  2205. for (k = 1; k < phase_ptr->rxn_x->token_list->Count(); k++)
  2206. {
  2207. rxn_ptr = phase_ptr->rxn_x->token_list->Element(k);
  2208. lp += rxn_ptr->s->la * rxn_ptr->coef;
  2209. }
  2210. total_p += exp (lp * md->LOG_10);
  2211. }
  2212. }
  2213. if (total_p > 1.0)
  2214. s_s_ptr->s_s_in = true;
  2215. else
  2216. s_s_ptr->s_s_in = false;
  2217. }
  2218. #ifdef DEBUG_MOHID
  2219. if (d.debug_status)
  2220. fprintf(d.set_f, "MBSS-2) %d\n", s_s_ptr->s_s_in);
  2221. #endif
  2222. }
  2223. Unknown *u;
  2224. for (i = md->s_s_unknown->number; i < count_unknowns; i++)
  2225. {
  2226. u = (*unknown_list)[i];
  2227. if (u->type != S_S_MOLES)
  2228. break;
  2229. u->s_s_in = u->s_s->s_s_in;
  2230. }
  2231. return true;
  2232. }
  2233. CONVERGE_RESULT ModelEngine::Residuals()
  2234. {
  2235. //
  2236. // Calculates residuals for all equations
  2237. //
  2238. int i, j;
  2239. bool converge;
  2240. LDBLE toler,
  2241. sum_residual,
  2242. sinh_constant,
  2243. sum,
  2244. sum1,
  2245. *res,
  2246. g_moles;
  2247. Master *master_ptr,
  2248. *master_ptr1,
  2249. *master_ptr2,
  2250. *m_p;
  2251. Unknown *x, *u2;
  2252. Species *s_p;
  2253. LDBLE sigmaddl,
  2254. negfpsirt;
  2255. sum_residual = 0.0;
  2256. sigmaddl = 0;
  2257. sum = 0;
  2258. converge = true;
  2259. toler = md->convergence_tolerance;
  2260. int index;
  2261. // Calculate residuals
  2262. for (i = 0; i < count_unknowns; i++)
  2263. {
  2264. x = (*unknown_list)[i];
  2265. res = &residual[i];
  2266. #ifdef DEBUG_MOHID
  2267. if (d.debug_status)
  2268. {
  2269. if(md->mass_oxygen_unknown != NULL)
  2270. {
  2271. fprintf(d.set_f, "Residuals-1) %d %d %20.20e %20.20e %20.20e %20.20e %20.20e %20.20e %20.20e %20.20e %20.20e %d\n",
  2272. md->mass_water_switch,
  2273. x->type,
  2274. x->moles,
  2275. x->f,
  2276. md->LOG_10,
  2277. toler,
  2278. md->mu_x,
  2279. md->mass_water_aq_x,
  2280. gd->s_h2o->la,
  2281. md->mass_oxygen_unknown->moles,
  2282. md->mass_oxygen_unknown->f,
  2283. md->iterations);
  2284. }
  2285. else
  2286. {
  2287. fprintf(d.set_f, "Residuals-1) %d %d %20.20e %20.20e %20.20e %20.20e %20.20e %20.20e %20.20e %d\n",
  2288. md->mass_water_switch,
  2289. x->type,
  2290. x->moles,
  2291. x->f,
  2292. md->LOG_10,
  2293. toler,
  2294. md->mu_x,
  2295. md->mass_water_aq_x,
  2296. gd->s_h2o->la,
  2297. md->iterations);
  2298. }
  2299. }
  2300. #endif
  2301. if (x->type == MB)
  2302. {
  2303. *res = x->moles - x->f;
  2304. if (fabs(*res) > toler * x->moles && x->moles > MIN_TOTAL)
  2305. converge = false;
  2306. #ifdef DEBUG_MOHID
  2307. if (d.debug_status)
  2308. fprintf(d.set_f, "Residuals-2) %20.20e\n", *res);
  2309. #endif
  2310. }
  2311. else if (x->type == ALK)
  2312. {
  2313. *res = x->moles - x->f;
  2314. if (fabs(*res) > toler * x->moles)
  2315. converge = false;
  2316. #ifdef DEBUG_MOHID
  2317. if (d.debug_status)
  2318. fprintf(d.set_f, "Residuals-3) %20.20e\n", *res);
  2319. #endif
  2320. }
  2321. else if (x->type == SOLUTION_PHASE_BOUNDARY)
  2322. {
  2323. *res = x->f * md->LOG_10;
  2324. if (fabs(*res) > toler)
  2325. converge = false;
  2326. #ifdef DEBUG_MOHID
  2327. if (d.debug_status)
  2328. fprintf(d.set_f, "Residuals-4) %20.20e\n", *res);
  2329. #endif
  2330. }
  2331. else if (x->type == CB)
  2332. {
  2333. *res = -x->f;
  2334. if (md->ph_unknown == md->charge_balance_unknown)
  2335. *res += x->moles;
  2336. if (fabs(*res) >= toler * md->mu_x * md->mass_water_aq_x)
  2337. converge = false;
  2338. #ifdef DEBUG_MOHID
  2339. if (d.debug_status)
  2340. fprintf(d.set_f, "Residuals-5) %20.20e\n", *res);
  2341. #endif
  2342. }
  2343. else if (x->type == MU) //&& pitzer_model == FALSE
  2344. {
  2345. *res = md->mass_water_aq_x * md->mu_x - (LDBLE)0.5 * x->f;
  2346. if (fabs(*res) > toler * md->mu_x * md->mass_water_aq_x)
  2347. converge = false;
  2348. #ifdef DEBUG_MOHID
  2349. if (d.debug_status)
  2350. fprintf(d.set_f, "Residuals-6) %20.20e\n", *res);
  2351. #endif
  2352. }
  2353. else if (x->type == AH2O) //&& pitzer_model == FALSE
  2354. {
  2355. *res = md->mass_water_aq_x * exp (gd->s_h2o->la * md->LOG_10) - md->mass_water_aq_x + (LDBLE)0.017 * x->f;
  2356. if (fabs(*res) > toler)
  2357. converge = false;
  2358. #ifdef DEBUG_MOHID
  2359. if (d.debug_status)
  2360. fprintf(d.set_f, "Residuals-7) %20.20e\n", *res);
  2361. #endif
  2362. }
  2363. else if (x->type == MH) // && (pitzer_model == FALSE || pitzer_pe == TRUE))
  2364. {
  2365. *res = x->moles - x->f;
  2366. if (md->mass_water_switch)
  2367. *res -= 2 * (md->mass_oxygen_unknown->moles - md->mass_oxygen_unknown->f);
  2368. if (fabs(*res) > toler * (x->moles + 2 * md->mass_oxygen_unknown->moles))
  2369. converge = FALSE;
  2370. #ifdef DEBUG_MOHID
  2371. if (d.debug_status)
  2372. fprintf(d.set_f, "Residuals-8) %20.20e\n", *res);
  2373. #endif
  2374. }
  2375. else if (x->type == MH2O)
  2376. {
  2377. if (md->mass_water_switch)
  2378. continue;
  2379. *res = x->moles - x->f;
  2380. if (fabs(*res) > 0.01 * toler * x->moles)
  2381. converge = false;
  2382. #ifdef DEBUG_MOHID
  2383. if (d.debug_status)
  2384. fprintf(d.set_f, "Residuals-9) %20.20e\n", *res);
  2385. #endif
  2386. }
  2387. else if (x->type == PP)
  2388. {
  2389. *res = x->f * md->LOG_10;
  2390. if (x->pure_phase->add_formula.IsEmpty())
  2391. {
  2392. if (x->dissolve_only)
  2393. {
  2394. if ((*res > toler && x->moles > 0.0) || (*res < -toler && (x->pure_phase->initial_moles - x->moles) > 0))
  2395. converge = false;
  2396. }
  2397. else
  2398. {
  2399. if (*res < -toler || md->iterations < 1)
  2400. converge = false;
  2401. }
  2402. #ifdef DEBUG_MOHID
  2403. if (d.debug_status)
  2404. fprintf(d.set_f, "Residuals-10) %20.20e %20.20e\n", *res, x->pure_phase->initial_moles);
  2405. #endif
  2406. }
  2407. else
  2408. {
  2409. if (*res < -toler || md->iterations < 1)
  2410. converge = FALSE;
  2411. #ifdef DEBUG_MOHID
  2412. if (d.debug_status)
  2413. fprintf(d.set_f, "Residuals-11) %20.20e\n", *res);
  2414. #endif
  2415. }
  2416. }
  2417. else if (x->type == GAS_MOLES)
  2418. {
  2419. *res = x->gas_phase->total_p - x->f;
  2420. if (fabs(*res) > toler && md->gas_in)
  2421. converge = false;
  2422. #ifdef DEBUG_MOHID
  2423. if (d.debug_status)
  2424. fprintf(d.set_f, "Residuals-12) %20.20e %20.20e %d\n",
  2425. *res,
  2426. x->gas_phase->total_p,
  2427. md->gas_in);
  2428. #endif
  2429. }
  2430. else if (x->type == S_S_MOLES)
  2431. {
  2432. *res = x->f * md->LOG_10;
  2433. #ifdef DEBUG_MOHID
  2434. if (d.debug_status)
  2435. fprintf(d.set_f, "Residuals-13) %20.20e\n", *res);
  2436. #endif
  2437. if (x->moles <= MIN_TOTAL_SS && md->iterations > 2)
  2438. continue;
  2439. if (fabs(*res) > toler && x->s_s_in)
  2440. converge = false;
  2441. }
  2442. else if (x->type == EXCH)
  2443. {
  2444. *res = x->moles - x->f;
  2445. if (x->moles <= MIN_RELATED_SURFACE)
  2446. {
  2447. if (fabs(*res) > toler)
  2448. converge = false;
  2449. }
  2450. else if (fabs(*res) > toler * x->moles)
  2451. converge = FALSE;
  2452. #ifdef DEBUG_MOHID
  2453. if (d.debug_status)
  2454. fprintf(d.set_f, "Residuals-14) %20.20e\n", *res);
  2455. #endif
  2456. }
  2457. else if (x->type == SURFACE)
  2458. {
  2459. *res = x->moles - x->f;
  2460. if (x->moles <= MIN_RELATED_SURFACE)
  2461. {
  2462. if (fabs(*res) > toler)
  2463. converge = false;
  2464. }
  2465. else if (fabs(*res) > toler * x->moles)
  2466. converge = false;
  2467. #ifdef DEBUG_MOHID
  2468. if (d.debug_status)
  2469. fprintf(d.set_f, "Residuals-15) %20.20e\n", *res);
  2470. #endif
  2471. }
  2472. else if (x->type == PITZER_GAMMA)
  2473. {
  2474. if (!md->full_pitzer)
  2475. continue;
  2476. *res = x->s->lg - x->s->lg_pitzer;
  2477. if (fabs(*res) > toler)
  2478. converge = false;
  2479. #ifdef DEBUG_MOHID
  2480. if (d.debug_status)
  2481. fprintf(d.set_f, "Residuals-16) %20.20e\n", *res);
  2482. #endif
  2483. }
  2484. else if (x->type == SURFACE_CB && md->use.sur_p->type == DDL)
  2485. {
  2486. sinh_constant = sqrt((LDBLE)8 * (LDBLE)EPSILON * (LDBLE)EPSILON_ZERO * ((LDBLE)R_KJ_DEG_MOL * (LDBLE)1000) * md->tk_x * (LDBLE)1000);
  2487. if (x->surface_charge->grams == 0)
  2488. *res = 0.0;
  2489. else if (md->dl_type_x != NO_DL)
  2490. *res = -x->f;
  2491. else
  2492. *res = sinh_constant * sqrt (md->mu_x) * sinh ((*x->master)[0]->s->la * md->LOG_10) - x->f * (LDBLE)F_C_MOL / (x->surface_charge->specific_area * x->surface_charge->grams);
  2493. if (x->surface_charge->grams > MIN_RELATED_SURFACE && fabs(*res) > toler)
  2494. converge = false;
  2495. #ifdef DEBUG_MOHID
  2496. if (d.debug_status)
  2497. fprintf(d.set_f, "Residuals-17) %20.20e\n", *res);
  2498. #endif
  2499. }
  2500. else if (x->type == SURFACE_CB && md->use.sur_p->type == CD_MUSIC)
  2501. {
  2502. if (x->surface_charge->grams == 0)
  2503. *res = 0.0;
  2504. else
  2505. {
  2506. // sum is in moles of charge
  2507. master_ptr = SurfaceGetPSIMaster(x->surface_charge->name, SURF_PSI);
  2508. master_ptr1 = SurfaceGetPSIMaster(x->surface_charge->name, SURF_PSI1);
  2509. master_ptr2 = SurfaceGetPSIMaster(x->surface_charge->name, SURF_PSI2);
  2510. x->surface_charge->psi = -(master_ptr->s->la * md->LOG_10) * (LDBLE)R_KJ_DEG_MOL * md->tk_x / (LDBLE)F_KJ_V_EQ;
  2511. x->surface_charge->psi1 = -(master_ptr1->s->la * md->LOG_10) * (LDBLE)R_KJ_DEG_MOL * md->tk_x / (LDBLE)F_KJ_V_EQ;
  2512. x->surface_charge->psi2 = -(master_ptr2->s->la * md->LOG_10) * (LDBLE)R_KJ_DEG_MOL * md->tk_x / (LDBLE)F_KJ_V_EQ;
  2513. sum = 0;
  2514. for (j = 0; j < x->comp_unknowns->Count(); j++)
  2515. {
  2516. u2 = (*x->comp_unknowns)[j];
  2517. m_p = (*u2->master)[0];
  2518. sum += u2->moles * m_p->s->z;
  2519. }
  2520. x->surface_charge->sigma0 = (x->f + sum) * (LDBLE)F_C_MOL / (x->surface_charge->specific_area * x->surface_charge->grams);
  2521. // f is in moles
  2522. // eqns A-3
  2523. *res = x->surface_charge->sigma0 - x->surface_charge->capacitance[0] * (x->surface_charge->psi - x->surface_charge->psi1);
  2524. }
  2525. #ifdef DEBUG_MOHID
  2526. if (d.debug_status)
  2527. fprintf(d.set_f, "Residuals-18) %20.20e\n", *res);
  2528. #endif
  2529. if (x->surface_charge->grams > MIN_RELATED_SURFACE && fabs(*res) > toler)
  2530. converge = false;
  2531. }
  2532. else if (x->type == SURFACE_CB1)
  2533. {
  2534. if (x->surface_charge->grams == 0)
  2535. *res = 0.0;
  2536. else
  2537. {
  2538. // eqns A-4
  2539. x->surface_charge->sigma1 = x->f * (LDBLE)F_C_MOL / (x->surface_charge->specific_area * x->surface_charge->grams);
  2540. *res = (x->surface_charge->sigma0 + x->surface_charge->sigma1) - x->surface_charge->capacitance[1] * (x->surface_charge->psi1 - x->surface_charge->psi2);
  2541. }
  2542. #ifdef DEBUG_MOHID
  2543. if (d.debug_status)
  2544. fprintf(d.set_f, "Residuals-19) %20.20e\n", *res);
  2545. #endif
  2546. if (x->surface_charge->grams > MIN_RELATED_SURFACE && fabs (*res) > toler)
  2547. converge = false;
  2548. }
  2549. else if (x->type == SURFACE_CB2)
  2550. {
  2551. if (x->surface_charge->grams == 0)
  2552. *res = 0.0;
  2553. else if (md->dl_type_x != NO_DL)
  2554. {
  2555. sum = 0;
  2556. sum1 = 0;
  2557. for (j = 0; j < md->s_x->Count(); j++)
  2558. {
  2559. s_p = (*md->s_x)[j];
  2560. if (s_p->type == SURF)
  2561. sum += Under(s_p->lm) * s_p->dz[2];
  2562. if (s_p->type < H2O)
  2563. {
  2564. g_moles = (*s_p->diff_layer)[0]->g_moles;
  2565. sum1 += s_p->z * g_moles;
  2566. }
  2567. }
  2568. x->surface_charge->sigma2 = sum * (LDBLE)F_C_MOL / (x->surface_charge->specific_area * x->surface_charge->grams);
  2569. x->surface_charge->sigmaddl = (x->f - sum) * (LDBLE)F_C_MOL / (x->surface_charge->specific_area * x->surface_charge->grams);
  2570. *res = x->f + (x->surface_charge->sigma0 + x->surface_charge->sigma1) * (x->surface_charge->specific_area * x->surface_charge->grams) / (LDBLE)F_C_MOL;
  2571. }
  2572. else
  2573. {
  2574. // eqns A-6 and A-7
  2575. sinh_constant = sqrt ((LDBLE)8 * (LDBLE)EPSILON * (LDBLE)EPSILON_ZERO * ((LDBLE)R_KJ_DEG_MOL * (LDBLE)1000) * md->tk_x * (LDBLE)1000);
  2576. master_ptr2 = SurfaceGetPSIMaster(x->surface_charge->name, SURF_PSI2);
  2577. negfpsirt = master_ptr2->s->la * md->LOG_10;
  2578. sum = 0;
  2579. sum1 = 0;
  2580. for (j = 0; j < md->s_x->Count(); j++)
  2581. {
  2582. s_p = (*md->s_x)[j];
  2583. if (s_p->type < H2O)
  2584. {
  2585. sum += Under(s_p->lm) * (exp(s_p->z * negfpsirt) - 1);
  2586. sum1 += Under(s_p->lm) * s_p->z;
  2587. }
  2588. }
  2589. // add fictitious monovalent ion that balances charge
  2590. sum += sum1 * (exp(-sum1 / fabs (sum1) * negfpsirt) - 1);
  2591. if (sum < 0)
  2592. {
  2593. sum = -sum;
  2594. converge = false;
  2595. }
  2596. x->surface_charge->sigma2 = x->f * (LDBLE)F_C_MOL / (x->surface_charge->specific_area * x->surface_charge->grams);
  2597. if ((negfpsirt) < 0)
  2598. sigmaddl = (LDBLE)-0.5 * sinh_constant * sqrt(sum);
  2599. else
  2600. sigmaddl = (LDBLE)0.5 * sinh_constant * sqrt(sum);
  2601. x->surface_charge->sigmaddl = sigmaddl;
  2602. *res = (x->surface_charge->sigma0 + x->surface_charge->sigma1 + x->surface_charge->sigma2) + sigmaddl;
  2603. }
  2604. #ifdef DEBUG_MOHID
  2605. if (d.debug_status)
  2606. fprintf(d.set_f, "Residuals-20) %20.20e\n", *res);
  2607. #endif
  2608. if (x->surface_charge->grams > MIN_RELATED_SURFACE && fabs(*res) > toler)
  2609. converge = false;
  2610. }
  2611. index = (i + 1) * (count_unknowns + 1) - 1;
  2612. arr[index] = *res;
  2613. sum_residual += fabs(*res);
  2614. }
  2615. #ifdef DEBUG_MOHID
  2616. if (d.debug_status)
  2617. fprintf(d.set_f, "Residuals-21) %20.20e\n", sum_residual);
  2618. #endif
  2619. if (converge)
  2620. return (CONVERGED_CR);
  2621. return (OK_CR);
  2622. }
  2623. bool ModelEngine::KTemp(LDBLE tempc)
  2624. {
  2625. //
  2626. // Calculates log k's for all species and pure_phases
  2627. //
  2628. int i;
  2629. LDBLE tempk;
  2630. tempk = tempc + (LDBLE)273.15;
  2631. // Calculate log k for all aqueous species
  2632. Species *s;
  2633. for (i = 0; i < md->s_x->Count(); i++)
  2634. {
  2635. s = (*md->s_x)[i];
  2636. s->lk = KCalc(s->rxn_x->logk, tempk);
  2637. #ifdef DEBUG_MOHID
  2638. if (d.debug_status)
  2639. {
  2640. fprintf(d.set_f, "KTemp-1) %s %d ", s->name.CharPtr(), i);
  2641. for (int debug_i = 0; debug_i < 7; debug_i++)
  2642. fprintf(d.set_f, "%20.20e ", s->rxn_x->logk[debug_i]);
  2643. fprintf(d.set_f, "%20.20e\n", tempk);
  2644. }
  2645. #endif
  2646. }
  2647. Phase *p;
  2648. for (i = 0; i < gd->phase_list.Count(); i++)
  2649. {
  2650. p = gd->phase_list[i];
  2651. if (p->in)
  2652. p->lk = KCalc(p->rxn_x->logk, tempk);
  2653. }
  2654. // Calculate miscibility gaps for solid solutions
  2655. SS *ss_p;
  2656. if (md->use.ssa_p != NULL)
  2657. {
  2658. for (i = 0; i < md->use.ssa_p->ss_list->Count(); i++)
  2659. {
  2660. ss_p = (*md->use.ssa_p->ss_list)[i];
  2661. if (fabs(tempk - ss_p->tk) > 0.01)
  2662. SSPrep(tempk, ss_p);
  2663. }
  2664. }
  2665. return true;
  2666. }
  2667. bool ModelEngine::Set(bool initial)
  2668. {
  2669. //
  2670. // Sets initial guesses for unknowns if initial == TRUE
  2671. // Revises guesses whether initial is true or not
  2672. //
  2673. int i;
  2674. md->iterations = -1;
  2675. Solution *sol_p = md->use.sol_p;
  2676. // Set initial log concentrations to zero
  2677. Species *s;
  2678. for (i = 0; i < md->s_x->Count(); i++)
  2679. {
  2680. s = (*md->s_x)[i];
  2681. s->lm = LOG_ZERO_MOLALITY;
  2682. s->lg = 0.0;
  2683. }
  2684. // Set master species activities
  2685. md->tc_x = sol_p->tc;
  2686. md->tk_x = md->tc_x + (LDBLE)273.15;
  2687. // H+, e-, H2O
  2688. md->mass_water_aq_x = sol_p->mass_water;
  2689. md->mu_x = sol_p->mu;
  2690. //d.PrintSpeciesInfoToFile("Set", md->species_info_list, md, gd);
  2691. gd->s_h2o->moles = md->mass_water_aq_x / md->gfw_water;
  2692. gd->s_h2o->la = log10(sol_p->ah2o);
  2693. gd->s_hplus->la = -sol_p->ph;
  2694. gd->s_hplus->lm = gd->s_hplus->la;
  2695. gd->s_hplus->moles = exp(gd->s_hplus->lm * md->LOG_10) * md->mass_water_aq_x;
  2696. gd->s_eminus->la = -sol_p->solution_pe;
  2697. #ifdef DEBUG_MOHID
  2698. if (d.debug_status)
  2699. {
  2700. fprintf(d.set_f, "%i %20.20e %20.20e %20.20e %20.20e %20.20e %20.20e %20.20e %20.20e %20.20e %20.20e\n",
  2701. initial, md->tc_x, md->tk_x, md->mass_water_aq_x, md->mu_x, gd->s_h2o->moles, gd->s_h2o->la,
  2702. gd->s_hplus->la, gd->s_hplus->lm, gd->s_hplus->moles, gd->s_eminus->la);
  2703. }
  2704. #endif
  2705. if (initial)
  2706. InitialGuesses();
  2707. if (md->dl_type_x != NO_DL)
  2708. InitialSurfaceWater ();
  2709. #ifdef DEBUG_MOHID
  2710. if (d.debug_status)
  2711. fprintf(d.reviseguesses_f, "called_by_set: %d\n", d.reviseguesses_count++);
  2712. #endif
  2713. ReviseGuesses ();
  2714. return true;
  2715. }
  2716. bool ModelEngine::CheckResiduals()
  2717. {
  2718. //
  2719. // Checks for convergence of all equations, prints any nonconvergence
  2720. // Sets variable remove_unstable_phases if a phase is present,
  2721. // but undersaturated (i.e. aragonite in calcite-saturated solution).
  2722. int i;
  2723. LDBLE epsilon, r;
  2724. bool return_value;
  2725. epsilon = md->convergence_tolerance;
  2726. return_value = true;
  2727. if (md->stop_program)
  2728. return false;
  2729. Unknown *x;
  2730. for (i = 0; i < count_unknowns; i++)
  2731. {
  2732. x = (*unknown_list)[i];
  2733. r = fabs((LDBLE) residual[i]);
  2734. switch(x->type)
  2735. {
  2736. case MB:
  2737. case ALK:
  2738. if (r >= (epsilon * x->moles) && x->moles > MIN_TOTAL)
  2739. {
  2740. sprintf (error_string, "%20s has not converged. Total: %e\tCalculated: %e\tResidual: %e\n",
  2741. x->name, (double) x->moles, (double) x->f, (double) residual[i]);
  2742. if(error != NULL) error->SaveMessage(error_string);
  2743. if (x->type == ALK)
  2744. {
  2745. if(error != NULL) error->SaveMessage("Is non-carbonate alkalinity greater than total alkalinity?\n");
  2746. }
  2747. return_value = false;
  2748. }
  2749. break;
  2750. case SOLUTION_PHASE_BOUNDARY:
  2751. if (r >= epsilon)
  2752. {
  2753. sprintf (error_string, "%20s solution phase boundary has not converged. \tResidual: %e\n",
  2754. x->name, (double) residual[i]);
  2755. if(error != NULL) error->SaveMessage(error_string);
  2756. }
  2757. break;
  2758. case AH2O:
  2759. if (r >= epsilon)
  2760. {
  2761. sprintf (error_string, "%20s Activity of water has not converged. \tResidual: %e\n",
  2762. x->name, (double) residual[i]);
  2763. if(error != NULL) error->SaveMessage(error_string);
  2764. }
  2765. break;
  2766. case CB:
  2767. if (r >= epsilon * md->mu_x * md->mass_water_aq_x)
  2768. {
  2769. sprintf (error_string, "%20s Charge balance has not converged. \tResidual: %e\n",
  2770. x->name, (double) residual[i]);
  2771. if(error != NULL) error->SaveMessage(error_string);
  2772. }
  2773. break;
  2774. case MU:
  2775. if (r >= (epsilon * md->mu_x * md->mass_water_aq_x))
  2776. {
  2777. sprintf (error_string, "%20s Ionic strength has not converged. \tResidual: %e\n",
  2778. x->name, (double) residual[i]);
  2779. if(error != NULL) error->SaveMessage(error_string);
  2780. }
  2781. break;
  2782. case MH:
  2783. if (r > (epsilon * (x->moles + 2 * md->mass_oxygen_unknown->moles)))
  2784. {
  2785. sprintf (error_string, "%20s Mass of hydrogen has not converged. \tResidual: %e\n",
  2786. x->name, (double) residual[i]);
  2787. if(error != NULL) error->SaveMessage(error_string);
  2788. }
  2789. break;
  2790. case MH2O:
  2791. if (md->mass_water_switch)
  2792. continue;
  2793. if (r >= (0.01 * epsilon * x->moles))
  2794. {
  2795. sprintf (error_string, "%20s Mass of oxygen has not converged. \tResidual: %e\n",
  2796. x->name, (double) residual[i]);
  2797. if(error != NULL) error->SaveMessage(error_string);
  2798. }
  2799. break;
  2800. case PP:
  2801. if (x->pure_phase->add_formula.IsEmpty())
  2802. {
  2803. if (x->dissolve_only)
  2804. {
  2805. if ((residual[i] > epsilon && x->moles > 0.0) || (residual[i] < -epsilon && (x->pure_phase->initial_moles - x->moles) > 0))
  2806. {
  2807. sprintf (error_string, "%20s Dissolve_only pure phase has not converged. \tResidual: %e\n",
  2808. x->name, (double) residual[i]);
  2809. if(error != NULL) error->SaveMessage(error_string);
  2810. }
  2811. }
  2812. else
  2813. {
  2814. if (residual[i] >= epsilon && x->moles > 0.0)
  2815. {
  2816. md->remove_unstable_phases = true;
  2817. sprintf (error_string, "%20s Pure phase has not converged. \tResidual: %e\n",
  2818. x->name, (double) residual[i]);
  2819. if(error != NULL) error->SaveMessage(error_string);
  2820. }
  2821. else if (residual[i] <= -epsilon)
  2822. {
  2823. sprintf (error_string, "%20s Pure phase has not converged. \tResidual: %e\n",
  2824. x->name, (double) residual[i]);
  2825. if(error != NULL) error->SaveMessage(error_string);
  2826. }
  2827. }
  2828. }
  2829. else
  2830. {
  2831. if (r >= epsilon && x->moles > 0.0)
  2832. {
  2833. sprintf (error_string, "%s, Pure phase with add formula has not converged.\n\t SI may be a local minimum.\tResidual: %e\n",
  2834. x->name, (double) residual[i]);
  2835. if(error != NULL) error->SaveMessage(error_string);
  2836. }
  2837. }
  2838. break;
  2839. case EXCH:
  2840. if ((x->moles <= MIN_RELATED_SURFACE && r > epsilon) || (x->moles > MIN_RELATED_SURFACE && (r > epsilon * x->moles)))
  2841. {
  2842. sprintf (error_string, "%20s Exchanger mass balance has not converged. \tResidual: %e\n",
  2843. x->name, (double) residual[i]);
  2844. if(error != NULL) error->SaveMessage(error_string);
  2845. }
  2846. break;
  2847. case SURFACE:
  2848. if ((x->moles <= MIN_RELATED_SURFACE && r > epsilon) || (x->moles > MIN_RELATED_SURFACE && (r > epsilon * x->moles)))
  2849. {
  2850. sprintf (error_string, "%20s Surface mass balance has not converged. \tResidual: %e\n",
  2851. x->name, (double) residual[i]);
  2852. if(error != NULL) error->SaveMessage(error_string);
  2853. }
  2854. break;
  2855. case SURFACE_CB:
  2856. case SURFACE_CB1:
  2857. case SURFACE_CB2:
  2858. if (x->surface_charge->grams > MIN_RELATED_SURFACE && r > epsilon)
  2859. {
  2860. sprintf (error_string, "%20s Surface charge/potential has not converged. \tResidual: %e\n",
  2861. x->name, (double) residual[i]);
  2862. if(error != NULL) error->SaveMessage(error_string);
  2863. }
  2864. break;
  2865. case GAS_MOLES:
  2866. if (!md->gas_in)
  2867. continue;
  2868. if (residual[i] >= epsilon || residual[i] <= -epsilon)
  2869. {
  2870. sprintf (error_string, "%20s Total moles in gas phase has not converged. \tResidual: %e\n",
  2871. x->name, (double) residual[i]);
  2872. if(error != NULL) error->SaveMessage(error_string);
  2873. }
  2874. break;
  2875. case PITZER_GAMMA:
  2876. if (r > epsilon)
  2877. {
  2878. sprintf (error_string, "%20s log gamma not converged.\tResidual: %e\n",
  2879. x->name, (double) residual[i]);
  2880. if(error != NULL) error->SaveMessage(error_string);
  2881. }
  2882. break;
  2883. case S_S_MOLES:
  2884. if (!x->s_s_in)
  2885. continue;
  2886. if (x->moles <= MIN_TOTAL_SS)
  2887. continue;
  2888. if (residual[i] >= epsilon || residual[i] <= -epsilon)
  2889. {
  2890. sprintf (error_string, "%20s Total moles in solid solution has not converged. \tResidual: %e\n",
  2891. x->name, (double) residual[i]);
  2892. if(error != NULL) error->SaveMessage(error_string);
  2893. }
  2894. break;
  2895. }
  2896. }
  2897. if (md->remove_unstable_phases)
  2898. {
  2899. sprintf (error_string, "%20sRemoving unstable phases, iteration %d.", " ", md->iterations);
  2900. if(error != NULL) error->SaveMessage(error_string);
  2901. }
  2902. return return_value;
  2903. }
  2904. bool ModelEngine::SumSpecies()
  2905. {
  2906. //
  2907. // Calculates total alk, total carbon, total co2, electrical balance,
  2908. // total hydrogen, and total oxygen.
  2909. // Sorts species for summing and printing based on valence state and
  2910. // concentrations.
  2911. //
  2912. // Sums total valence states and stores in master[i]->total.
  2913. //
  2914. int i, j;
  2915. Master *master_ptr;
  2916. // Set global variables
  2917. md->ph_x = -gd->s_hplus->la;
  2918. md->solution_pe_x = -gd->s_eminus->la;
  2919. md->ah2o_x = exp(gd->s_h2o->la * md->LOG_10);
  2920. md->density_x = 1.0;
  2921. if (gd->s_o2 != NULL)
  2922. gd->s_o2->moles = Under(gd->s_o2->lm) * md->mass_water_aq_x;
  2923. if (gd->s_h2 != NULL)
  2924. gd->s_h2->moles = Under(gd->s_h2->lm) * md->mass_water_aq_x;
  2925. // Calculate sums
  2926. md->total_alkalinity = 0.0;
  2927. md->total_carbon = 0.0;
  2928. md->total_co2 = 0.0;
  2929. md->cb_x = 0.0;
  2930. md->total_ions_x = 0.0;
  2931. md->total_o_x = 0.0;
  2932. md->total_h_x = 0.0;
  2933. Species *sx;
  2934. for (i = 0; i < md->s_x->Count(); i++)
  2935. {
  2936. sx = (*md->s_x)[i];
  2937. if (sx->type == EX || sx->type == SURF)
  2938. continue;
  2939. md->cb_x += sx->z * sx->moles;
  2940. md->total_ions_x += fabs(sx->z * sx->moles);
  2941. md->total_alkalinity += sx->alk * sx->moles;
  2942. md->total_carbon += sx->carbon * sx->moles;
  2943. md->total_co2 += sx->co2 * sx->moles;
  2944. md->total_h_x += sx->h * sx->moles;
  2945. md->total_o_x += sx->o * sx->moles;
  2946. if (md->use.sur_p != NULL)
  2947. {
  2948. if (md->use.sur_p->debye_lengths > 0 && md->state >= REACTION && sx->type == H2O)
  2949. {
  2950. md->total_h_x -= 2 * md->mass_water_surfaces_x / md->gfw_water;
  2951. md->total_o_x -= md->mass_water_surfaces_x / md->gfw_water;
  2952. }
  2953. }
  2954. }
  2955. // Sum valence states, put in master->total
  2956. for (i = 0; i < gd->master_list.Count(); i++)
  2957. {
  2958. master_ptr = gd->master_list[i];
  2959. master_ptr->total = 0.0;
  2960. master_ptr->total_primary = 0.0;
  2961. }
  2962. SpeciesInfo *si;
  2963. for (i = 0; i < md->species_info_list->Count(); i++)
  2964. {
  2965. si = (*md->species_info_list)[i];
  2966. if (si->master_s->secondary != NULL)
  2967. master_ptr = si->master_s->secondary;
  2968. else
  2969. master_ptr = si->master_s->primary;
  2970. master_ptr->total += si->s->moles * si->coef;
  2971. }
  2972. // Calculate mass-balance sums
  2973. Unknown *u;
  2974. for (i = 0; i < count_unknowns; i++)
  2975. {
  2976. u = (*unknown_list)[i];
  2977. if (u->type == MB || u->type == SOLUTION_PHASE_BOUNDARY || u->type == EXCH ||
  2978. u->type == SURFACE || (u->type == CB && u != md->ph_unknown && u != md->pe_unknown))
  2979. {
  2980. u->sum = 0.0;
  2981. for (j = 0; j < u->master->Count(); j++)
  2982. {
  2983. master_ptr = (*u->master)[j];
  2984. u->sum += master_ptr->total;
  2985. }
  2986. }
  2987. else if (u->type == ALK)
  2988. u->sum = md->total_co2;
  2989. }
  2990. // Calculate total element concentrations
  2991. for (i = 0; i < gd->master_list.Count(); i++)
  2992. {
  2993. master_ptr = gd->master_list[i];
  2994. master_ptr->e->primary->total_primary += master_ptr->total;
  2995. }
  2996. // Calculate isotope ratios
  2997. //CalculateValues (); //Probably isn't used until isotopes are included on program
  2998. return true;
  2999. }
  3000. bool ModelEngine::NumericalJacobian()
  3001. {
  3002. LDBLE *base;
  3003. LDBLE d_, d1, d2;
  3004. int i, j;
  3005. if (md->use.sur_p == NULL || md->use.sur_p->type != CD_MUSIC)
  3006. return true;
  3007. //d.PrintArrayToFile(arr, arr_capacity, "numerical_jacobian-0");
  3008. base = NULL;
  3009. try
  3010. {
  3011. md->calculating_deriv = true;
  3012. Gammas(md->mu_x);
  3013. //d.PrintGammasToFile("NumericalJacobian-1", md->s_x);
  3014. Molalities(true);
  3015. MBSums();
  3016. Residuals();
  3017. //d.PrintArrayToFile(arr, arr_capacity, "NumericalJacobian-1");
  3018. // Clear array, note residuals are in array[i, count_unknowns+1]
  3019. for (i = 0; i < count_unknowns; i++)
  3020. arr[i] = 0.0;
  3021. for (i = 1; i < count_unknowns; i++)
  3022. memcpy ((void *) &arr[i * (count_unknowns + 1)], (void *) &arr[0], (size_t) count_unknowns * sizeof (LDBLE));
  3023. base = new LDBLE [count_unknowns];
  3024. for (i = 0; i < count_unknowns; i++)
  3025. base[i] = residual[i];
  3026. d_ = (LDBLE)1e-6;
  3027. d1 = d_ * log((LDBLE)10.0);
  3028. d2 = 0;
  3029. Unknown *x;
  3030. Master *m;
  3031. for (i = 0; i < count_unknowns; i++)
  3032. {
  3033. x = (*unknown_list)[i];
  3034. m = (*x->master)[0];
  3035. switch (x->type)
  3036. {
  3037. case MB:
  3038. case ALK:
  3039. case CB:
  3040. case SOLUTION_PHASE_BOUNDARY:
  3041. case EXCH:
  3042. case SURFACE:
  3043. case SURFACE_CB:
  3044. case SURFACE_CB1:
  3045. case SURFACE_CB2:
  3046. m->s->la += d_;
  3047. d2 = d1;
  3048. break;
  3049. case MH:
  3050. gd->s_eminus->la += d_;
  3051. d2 = d1;
  3052. break;
  3053. case AH2O:
  3054. m->s->la += d_;
  3055. d2 = d1;
  3056. break;
  3057. case PITZER_GAMMA:
  3058. x->s->lg += d_;
  3059. d2 = d_;
  3060. break;
  3061. case MH2O:
  3062. md->mass_water_aq_x *= ((LDBLE)1.0 + d_);
  3063. m->s->moles = md->mass_water_aq_x / md->gfw_water;
  3064. d2 = log ((LDBLE)1.0 + d_);
  3065. break;
  3066. case MU:
  3067. d2 = d_ * md->mu_x;
  3068. md->mu_x += d2;
  3069. //d.PrintSpeciesInfoToFile("NumericalJacobian-1", md->species_info_list, md, gd);
  3070. Gammas(md->mu_x);
  3071. //d.PrintGammasToFile("NumericalJacobian-2", md->s_x);
  3072. break;
  3073. case PP:
  3074. for (j = 0; j < count_unknowns; j++)
  3075. delta[j] = 0.0;
  3076. d2 = (LDBLE)-1e-8;
  3077. delta[i] = d2;
  3078. Reset();
  3079. //d.PrintDeltaToFile("Reset-2", delta, count_unknowns);
  3080. d2 = delta[i];
  3081. break;
  3082. case S_S_MOLES:
  3083. if (!x->s_s_in)
  3084. continue;
  3085. for (j = 0; j < count_unknowns; j++)
  3086. delta[j] = 0.0;
  3087. d2 = -d_ * x->moles;
  3088. d2 = (LDBLE)-.1 * x->moles;
  3089. delta[i] = d2;
  3090. Reset ();
  3091. //d.PrintDeltaToFile("Reset-3", delta, count_unknowns);
  3092. d2 = delta[i];
  3093. break;
  3094. case GAS_MOLES:
  3095. if (!md->gas_in)
  3096. continue;
  3097. d2 = d_ * x->moles;
  3098. if (d2 < 1e-14)
  3099. d2 = (LDBLE)1e-14;
  3100. x->moles += d2;
  3101. break;
  3102. }
  3103. Molalities(true);
  3104. MBSums();
  3105. Residuals ();
  3106. //d.PrintArrayToFile(arr, arr_capacity, "NumericalJacobian-2");
  3107. for (j = 0; j < count_unknowns; j++)
  3108. arr[j * (count_unknowns + 1) + i] = -(residual[j] - base[j]) / d2;
  3109. switch (x->type)
  3110. {
  3111. case MB:
  3112. case ALK:
  3113. case CB:
  3114. case SOLUTION_PHASE_BOUNDARY:
  3115. case EXCH:
  3116. case SURFACE:
  3117. case SURFACE_CB:
  3118. case SURFACE_CB1:
  3119. case SURFACE_CB2:
  3120. case AH2O:
  3121. m->s->la -= d_;
  3122. break;
  3123. case MH:
  3124. gd->s_eminus->la -= d_;
  3125. if (arr[i * (count_unknowns + 1) + i] == 0)
  3126. arr[i * (count_unknowns + 1) + i] = exp (gd->s_h2->lm * md->LOG_10) * 2;
  3127. break;
  3128. case PITZER_GAMMA:
  3129. x->s->lg -= d_;
  3130. break;
  3131. case MH2O:
  3132. md->mass_water_aq_x /= (1 + d_);
  3133. m->s->moles = md->mass_water_aq_x / md->gfw_water;
  3134. break;
  3135. case MU:
  3136. md->mu_x -= d2;
  3137. //d.PrintSpeciesInfoToFile("NumericalJacobian-2", md->species_info_list, md, gd);
  3138. Gammas(md->mu_x);
  3139. //d.PrintGammasToFile("NumericalJacobian-3", md->s_x);
  3140. break;
  3141. case PP:
  3142. delta[i] = -d2;
  3143. Reset ();
  3144. //d.PrintDeltaToFile("Reset-4", delta, count_unknowns);
  3145. break;
  3146. case S_S_MOLES:
  3147. delta[i] = -d2;
  3148. Reset ();
  3149. //d.PrintDeltaToFile("Reset-5", delta, count_unknowns);
  3150. break;
  3151. case GAS_MOLES:
  3152. x->moles -= d2;
  3153. break;
  3154. }
  3155. }
  3156. Molalities(true);
  3157. MBSums();
  3158. MBGases();
  3159. MBSS();
  3160. Residuals();
  3161. //d.PrintArrayToFile(arr,arr_capacity, "numerical_jacobian-3");
  3162. }
  3163. catch(...)
  3164. {
  3165. delete base;
  3166. throw;
  3167. }
  3168. delete base;
  3169. md->calculating_deriv = false;
  3170. return true;
  3171. }
  3172. bool ModelEngine::JacobianSums()
  3173. {
  3174. //
  3175. // Fills in jacobian array, uses arrays sum_jacob0, sum_jacob1, and sum_jacob2.
  3176. //
  3177. int i,
  3178. j,
  3179. k,
  3180. index;
  3181. LDBLE sinh_constant;
  3182. STCoef *stc_p;
  3183. // Clear array, note residuals are in array[i, count_unknowns+1]
  3184. for (i = 0; i < count_unknowns; i++)
  3185. arr[i] = 0.0;
  3186. for (i = 1; i < count_unknowns; i++)
  3187. memcpy ((void *) &arr[i * (count_unknowns + 1)], (void *) &arr[0], (size_t) count_unknowns * sizeof (LDBLE));
  3188. // Add constant terms
  3189. for (k = 0; k < md->sum_jacob0->Count(); k++)
  3190. {
  3191. stc_p = (*md->sum_jacob0)[k];
  3192. *stc_p->target += stc_p->coef;
  3193. #ifdef DEBUG_MOHID
  3194. if (d.debug_status)
  3195. fprintf(d.set_f, "JacobianSums-1) %20.20e %20.20e\n", *stc_p->target, stc_p->coef);
  3196. #endif
  3197. }
  3198. // Add terms with coefficients of 1.0
  3199. for (k = 0; k < md->sum_jacob1->Count(); k++)
  3200. {
  3201. stc_p = (*md->sum_jacob1)[k];
  3202. *stc_p->target += *stc_p->source;
  3203. #ifdef DEBUG_MOHID
  3204. if (d.debug_status)
  3205. fprintf(d.set_f, "JacobianSums-2) %20.20e %20.20e\n", *stc_p->target, *stc_p->source);
  3206. #endif
  3207. }
  3208. // Add terms with coefficients != 1.0
  3209. for (k = 0; k < md->sum_jacob2->Count(); k++)
  3210. {
  3211. stc_p = (*md->sum_jacob2)[k];
  3212. *stc_p->target += *stc_p->source * stc_p->coef;
  3213. #ifdef DEBUG_MOHID
  3214. if (d.debug_status)
  3215. fprintf(d.set_f, "JacobianSums-3) %20.20e %20.20e %20.20e\n", *stc_p->target, *stc_p->source, stc_p->coef);
  3216. #endif
  3217. }
  3218. // Make final adustments to jacobian array
  3219. // Ionic strength
  3220. if (md->mu_unknown != NULL)
  3221. {
  3222. for (i = 0; i < count_unknowns; i++)
  3223. {
  3224. index = md->mu_unknown->number * (count_unknowns + 1) + i;
  3225. arr[index] *= 0.5;
  3226. #ifdef DEBUG_MOHID
  3227. if (d.debug_status)
  3228. fprintf(d.set_f, "JacobianSums-4) %d %20.20e\n", index, arr[index]);
  3229. #endif
  3230. }
  3231. index = md->mu_unknown->number * (count_unknowns + 1) + md->mu_unknown->number;
  3232. arr[index] -= md->mass_water_aq_x;
  3233. #ifdef DEBUG_MOHID
  3234. if (d.debug_status)
  3235. fprintf(d.set_f, "JacobianSums-5) %d %20.20e\n", index, arr[index]);
  3236. #endif
  3237. }
  3238. // Activity of water
  3239. if (md->mass_oxygen_unknown != NULL && md->mu_unknown != NULL)
  3240. {
  3241. index = md->mu_unknown->number * (count_unknowns + 1) + md->mass_oxygen_unknown->number;
  3242. arr[index] -= md->mu_x * md->mass_water_aq_x;
  3243. #ifdef DEBUG_MOHID
  3244. if (d.debug_status)
  3245. fprintf(d.set_f, "JacobianSums-6) %d %20.20e\n", index, arr[index]);
  3246. #endif
  3247. }
  3248. if (md->ah2o_unknown != NULL)
  3249. {
  3250. for (i = 0; i < count_unknowns; i++)
  3251. {
  3252. index = md->ah2o_unknown->number * (count_unknowns + 1) + i;
  3253. arr[index] *= (LDBLE)-0.017;
  3254. #ifdef DEBUG_MOHID
  3255. if (d.debug_status)
  3256. fprintf(d.set_f, "JacobianSums-7) %d %20.20e\n", index, arr[index]);
  3257. #endif
  3258. }
  3259. index = md->ah2o_unknown->number * (count_unknowns + 1) + md->ah2o_unknown->number;
  3260. arr[index] -= md->mass_water_aq_x * exp (gd->s_h2o->la * md->LOG_10);
  3261. #ifdef DEBUG_MOHID
  3262. if (d.debug_status)
  3263. fprintf(d.set_f, "JacobianSums-8) %d %20.20e\n", index, arr[index]);
  3264. #endif
  3265. }
  3266. if (md->mass_oxygen_unknown != NULL && md->ah2o_unknown != NULL)
  3267. {
  3268. index = md->ah2o_unknown->number * (count_unknowns + 1) + md->mass_oxygen_unknown->number;
  3269. arr[index] -= (exp(gd->s_h2o->la * md->LOG_10) - 1) * md->mass_water_aq_x;
  3270. #ifdef DEBUG_MOHID
  3271. if (d.debug_status)
  3272. fprintf(d.set_f, "JacobianSums-9) %d %20.20e\n", index, arr[index]);
  3273. #endif
  3274. }
  3275. // Surface charge balance
  3276. if (md->surface_unknown != NULL && md->dl_type_x == NO_DL)
  3277. {
  3278. sinh_constant = sqrt ((LDBLE)8 * (LDBLE)EPSILON * (LDBLE)EPSILON_ZERO * ((LDBLE)R_KJ_DEG_MOL * (LDBLE)1000) * md->tk_x * (LDBLE)1000);
  3279. Unknown *x;
  3280. Master *m;
  3281. for (i = 0; i < count_unknowns; i++)
  3282. {
  3283. x = (*unknown_list)[i];
  3284. if (x->type == SURFACE_CB && x->surface_charge->grams > 0)
  3285. {
  3286. for (j = 0; j < count_unknowns; j++)
  3287. {
  3288. index = x->number * (count_unknowns + 1) + j;
  3289. arr[index] *= (LDBLE)F_C_MOL / (x->surface_charge->specific_area * x->surface_charge->grams);
  3290. #ifdef DEBUG_MOHID
  3291. if (d.debug_status)
  3292. fprintf(d.set_f, "JacobianSums-10) %d %20.20e\n", index, arr[index]);
  3293. #endif
  3294. }
  3295. m = (*x->master)[0];
  3296. index = x->number * (count_unknowns + 1) + x->number;
  3297. arr[index] -= sinh_constant * sqrt(md->mu_x) * cosh(m->s->la * md->LOG_10);
  3298. #ifdef DEBUG_MOHID
  3299. if (d.debug_status)
  3300. fprintf(d.set_f, "JacobianSums-11) %d %20.20e\n", index, arr[index]);
  3301. #endif
  3302. if (md->mu_unknown != NULL)
  3303. {
  3304. index = x->number * (count_unknowns + 1) + md->mu_unknown->number;
  3305. arr[index] -= (LDBLE)0.5 * sinh_constant / sqrt(md->mu_x) * sinh(m->s->la * md->LOG_10);
  3306. #ifdef DEBUG_MOHID
  3307. if (d.debug_status)
  3308. fprintf(d.set_f, "JacobianSums-12) %d %20.20e\n", index, arr[index]);
  3309. #endif
  3310. }
  3311. }
  3312. }
  3313. }
  3314. return true;
  3315. }
  3316. int ModelEngine::Ineq(int in_kode)
  3317. {
  3318. /*
  3319. * Sets up equations and inequalities for Cl1.
  3320. * Scales columns if necessary.
  3321. * Eliminates equations that are not necessary because
  3322. * gas_phase, s_s, or phase equation is not needed
  3323. * Mallocs space
  3324. * Calls Cl1
  3325. * Rescales results if necessary
  3326. */
  3327. int i, j;
  3328. int return_code;
  3329. int count_rows;
  3330. int count_optimize, count_equal;
  3331. int max_row_count, max_column_count; //eram extern
  3332. int k, l, m, n;
  3333. int klmd, nklmd, n2d;
  3334. int iter;
  3335. LDBLE error;
  3336. LDBLE max;
  3337. int kode;
  3338. LDBLE min;
  3339. Unknown *x;
  3340. LDBLE r, to_fill_LDBLE = 0.0;
  3341. int to_fill_int = 0;
  3342. #ifdef DEBUG_MOHID
  3343. if (d.debug_status)
  3344. for(int debug_i = 0; debug_i < count_unknowns; debug_i++)
  3345. fprintf(d.ineq_f, "ineq-0) %s %d %d %20.20e %20.20e\n",
  3346. (*unknown_list)[debug_i]->name.CharPtr(),
  3347. (*unknown_list)[debug_i]->type,
  3348. (*unknown_list)[debug_i]->number,
  3349. (*unknown_list)[debug_i]->moles,
  3350. (*unknown_list)[debug_i]->f);
  3351. #endif
  3352. if (md->remove_unstable_phases)
  3353. {
  3354. for (i = 0; i < count_unknowns; i++)
  3355. {
  3356. x = (*unknown_list)[i];
  3357. r = residual[i];
  3358. if (x->type == PP && r > 0e-8 && x->moles > 0 && x->pure_phase->add_formula.IsEmpty() && !x->dissolve_only)
  3359. delta[i] = x->moles;
  3360. else
  3361. delta[i] = 0.0;
  3362. #ifdef DEBUG_MOHID
  3363. if (d.debug_status)
  3364. fprintf(d.ineq_f, "Ineq-1) %d %20.20e %20.20e %20.20e\n", i, x->moles, residual[i], delta[i]);
  3365. #endif
  3366. }
  3367. md->remove_unstable_phases = false;
  3368. return OK;
  3369. }
  3370. IneqInit(3 * count_unknowns, 3 * count_unknowns);
  3371. NormalNewMax(count_unknowns);
  3372. Fill(normal, 1.0, normal_max());
  3373. for (i = 0; i < count_unknowns; i++)
  3374. {
  3375. x = (*unknown_list)[i];
  3376. max = 0.0;
  3377. #ifdef DEBUG_MOHID
  3378. if (d.debug_status)
  3379. fprintf(d.ineq_f, "Ineq-2) %d %d %20.20e %s\n",
  3380. i,
  3381. x->type,
  3382. x->moles,
  3383. x->name.CharPtr());
  3384. #endif
  3385. if (x->type == MB || x->type == ALK || x->type == EXCH || x->type == SURFACE || x->type == SURFACE_CB || SURFACE_CB1 || SURFACE_CB2)
  3386. {
  3387. if (x->moles <= MIN_RELATED_SURFACE && (x->type == EXCH || x->type == SURFACE))
  3388. continue;
  3389. for (j = 0; j < count_unknowns; j++)
  3390. {
  3391. if (x->type == SURFACE && (*unknown_list)[j]->type == SURFACE_CB)
  3392. continue;
  3393. if (x->type == SURFACE_CB1 && (*unknown_list)[j]->type == SURFACE_CB2)
  3394. continue;
  3395. if (fabs(arr[j * (count_unknowns + 1) + i]) > max)
  3396. {
  3397. max = fabs(arr[j * (count_unknowns + 1) + i]);
  3398. if (max > md->min_value)
  3399. {
  3400. #ifdef DEBUG_MOHID
  3401. if (d.debug_status)
  3402. fprintf(d.ineq_f, "Ineq-3) %d %d %20.20e %20.20e %s\n",
  3403. j,
  3404. (*unknown_list)[j]->type,
  3405. max,
  3406. md->min_value,
  3407. (*unknown_list)[j]->name);
  3408. #endif
  3409. break;
  3410. }
  3411. }
  3412. }
  3413. if (md->diagonal_scale)
  3414. {
  3415. if (fabs (arr[i * (count_unknowns + 1) + i]) < md->min_value)
  3416. {
  3417. max = fabs(arr[i * (count_unknowns + 1) + i]);
  3418. #ifdef DEBUG_MOHID
  3419. if (d.debug_status)
  3420. fprintf(d.ineq_f, "Ineq-4) %20.20e %d %20.20e %20.20e\n",
  3421. max,
  3422. i * (count_unknowns + 1) + i,
  3423. arr[i * (count_unknowns + 1) + i],
  3424. md->min_value);
  3425. #endif
  3426. }
  3427. }
  3428. if (max == 0)
  3429. {
  3430. arr[i * (count_unknowns + 1) + i] = (LDBLE)1e-5 * x->moles;
  3431. max = fabs ((LDBLE)1e-5 * x->moles);
  3432. #ifdef DEBUG_MOHID
  3433. if (d.debug_status)
  3434. fprintf(d.ineq_f, "Ineq-5) %20.20e %d %20.20e\n",
  3435. max,
  3436. i * (count_unknowns + 1) + i,
  3437. arr[i * (count_unknowns + 1) + i]);
  3438. #endif
  3439. }
  3440. }
  3441. if (x->type == MH)
  3442. {
  3443. min = (LDBLE)1e-12;
  3444. min = (LDBLE)MIN_TOTAL;
  3445. arr[x->number * (count_unknowns + 1) + x->number] += min;
  3446. if (fabs (arr[x->number * (count_unknowns + 1) + x->number]) < min)
  3447. arr[x->number * (count_unknowns + 1) + x->number] = min;
  3448. max = 0.0;
  3449. for (j = 0; j < count_unknowns; j++)
  3450. {
  3451. if ((*unknown_list)[j]->type != MB &&
  3452. (*unknown_list)[j]->type != SURFACE &&
  3453. (*unknown_list)[j]->type != SURFACE_CB &&
  3454. (*unknown_list)[j]->type != SURFACE_CB1 &&
  3455. (*unknown_list)[j]->type != SURFACE_CB2 &&
  3456. (*unknown_list)[j]->type != EXCH &&
  3457. (*unknown_list)[j]->type != MH &&
  3458. (*unknown_list)[j]->type != MH2O)
  3459. continue;
  3460. if (fabs (arr[j * (count_unknowns + 1) + i]) > max)
  3461. {
  3462. max = fabs (arr[j * (count_unknowns + 1) + i]);
  3463. if (max > md->min_value)
  3464. break;
  3465. }
  3466. }
  3467. #ifdef DEBUG_MOHID
  3468. if (d.debug_status)
  3469. fprintf(d.ineq_f, "Ineq-6) %20.20e %d %20.20e\n",
  3470. max,
  3471. x->number * (count_unknowns + 1) + x->number,
  3472. arr[x->number * (count_unknowns + 1) + x->number]);
  3473. #endif
  3474. }
  3475. if (max > 0.0 && max < md->min_value)
  3476. {
  3477. for (j = 0; j < count_unknowns; j++)
  3478. {
  3479. arr[j * (count_unknowns + 1) + i] *= md->min_value / max;
  3480. #ifdef DEBUG_MOHID
  3481. if (d.debug_status)
  3482. fprintf(d.ineq_f, "Ineq-7) %d %20.20e\n",
  3483. j * (count_unknowns + 1) + i,
  3484. arr[j * (count_unknowns + 1) + i]);
  3485. #endif
  3486. }
  3487. normal[i] = md->min_value / max;
  3488. #ifdef DEBUG_MOHID
  3489. if (d.debug_status)
  3490. fprintf(d.ineq_f, "Ineq-8) %d %20.20e\n",
  3491. i,
  3492. normal[i]);
  3493. #endif
  3494. }
  3495. }
  3496. // Allocate arrays for inequality solver
  3497. max_row_count = 2 * count_unknowns + 2;
  3498. max_column_count = count_unknowns + 2;
  3499. IneqArrayNewMax(max_row_count * max_column_count);
  3500. Fill(ineq_array, 0.0, ineq_array_max());
  3501. BackEqNewMax(max_row_count);
  3502. Fill(back_eq, 0, back_eq_max());
  3503. ZeroNewMax(max_row_count);
  3504. Fill(zero, 0.0, zero_max());
  3505. ResNewMax(max_row_count);
  3506. Fill(res, 0.0, res_max());
  3507. Delta1NewMax(max_column_count);
  3508. Fill(delta1, 0.0, delta1_max());
  3509. // Copy equations to optimize into ineq_array
  3510. count_rows = 0;
  3511. for (i = 0; i < count_unknowns; i++)
  3512. {
  3513. x = (*unknown_list)[i];
  3514. if (md->iterations < md->aqueous_only)
  3515. continue;
  3516. #ifdef DEBUG_MOHID
  3517. if (d.debug_status == 1)
  3518. fprintf(d.ineq_f, "Ineq-9) %d %20.20e %20.20e\n",
  3519. x->type,
  3520. x->moles,
  3521. x->f);
  3522. #endif
  3523. if (x->type == PP)
  3524. {
  3525. if (!x->pure_phase->phase->in)
  3526. continue;
  3527. if (x->pure_phase->force_equality)
  3528. continue;
  3529. if (x->f > 0e-8 && x->moles <= 0 && x->pure_phase->add_formula.IsEmpty())
  3530. continue;
  3531. else if (x->f < 0e-8 && x->dissolve_only && (x->moles - x->pure_phase->initial_moles >= 0))
  3532. continue;
  3533. else
  3534. {
  3535. memcpy ((void *) &ineq_array[count_rows * max_column_count], (void *) &arr[i * (count_unknowns + 1)], (size_t) (count_unknowns + 1) * sizeof (LDBLE));
  3536. back_eq[count_rows] = i;
  3537. if (x->pure_phase->add_formula.IsEmpty() && !x->dissolve_only)
  3538. {
  3539. res[count_rows] = 1.0;
  3540. #ifdef DEBUG_MOHID
  3541. if (d.debug_status)
  3542. fprintf(d.ineq_f, "Ineq-10) %20.20e %d\n", res[count_rows], count_rows);
  3543. #endif
  3544. }
  3545. if (md->pp_scale != 1)
  3546. for (j = 0; j < count_unknowns + 1; j++)
  3547. {
  3548. ineq_array[count_rows * max_column_count + j] *= md->pp_scale;
  3549. #ifdef DEBUG_MOHID
  3550. if (d.debug_status)
  3551. fprintf(d.ineq_f, "Ineq-11) %20.20e %d\n", ineq_array[count_rows * max_column_count + j], count_rows * max_column_count + j);
  3552. #endif
  3553. }
  3554. if (in_kode != 1)
  3555. {
  3556. res[count_rows] = 0.0;
  3557. #ifdef DEBUG_MOHID
  3558. if (d.debug_status)
  3559. fprintf(d.ineq_f, "Ineq-12) %20.20e\n", res[count_rows], count_rows);
  3560. #endif
  3561. }
  3562. count_rows++;
  3563. }
  3564. }
  3565. else if (x->type == ALK || x->type == SOLUTION_PHASE_BOUNDARY)
  3566. {
  3567. memcpy ((void *) &ineq_array[count_rows * max_column_count], (void *) &arr[i * (count_unknowns + 1)], (size_t) (count_unknowns + 1) * sizeof (LDBLE));
  3568. back_eq[count_rows] = i;
  3569. #ifdef DEBUG_MOHID
  3570. if (d.debug_status)
  3571. fprintf(d.ineq_f, "Ineq-13) %d %d\n", back_eq[count_rows], count_rows);
  3572. #endif
  3573. count_rows++;
  3574. }
  3575. else if (x->type == GAS_MOLES && md->gas_in)
  3576. {
  3577. memcpy ((void *) &ineq_array[count_rows * max_column_count], (void *) &arr[i * (count_unknowns + 1)], (size_t) (count_unknowns + 1) * sizeof (LDBLE));
  3578. back_eq[count_rows] = i;
  3579. res[count_rows] = 1.0;
  3580. if (in_kode != 1)
  3581. res[count_rows] = 0.0;
  3582. #ifdef DEBUG_MOHID
  3583. if (d.debug_status)
  3584. fprintf(d.ineq_f, "Ineq-14) %d %20.20e %d\n", back_eq[count_rows], res[count_rows], count_rows);
  3585. #endif
  3586. count_rows++;
  3587. }
  3588. else if (x->type == S_S_MOLES && x->s_s_in)
  3589. {
  3590. memcpy ((void *) &ineq_array[count_rows * max_column_count], (void *) &arr[i * (count_unknowns + 1)], (size_t) (count_unknowns + 1) * sizeof (LDBLE));
  3591. back_eq[count_rows] = i;
  3592. res[count_rows] = 1.0;
  3593. if (in_kode != 1)
  3594. res[count_rows] = 0.0;
  3595. #ifdef DEBUG_MOHID
  3596. if (d.debug_status)
  3597. fprintf(d.ineq_f, "Ineq-15) %d %20.20e %d\n", back_eq[count_rows], res[count_rows], count_rows);
  3598. #endif
  3599. count_rows++;
  3600. }
  3601. }
  3602. count_optimize = count_rows;
  3603. // Copy equality equations into ineq_array
  3604. Unknown *x_b;
  3605. for (i = 0; i < count_unknowns; i++)
  3606. {
  3607. x = (*unknown_list)[i];
  3608. if (i > 0)
  3609. x_b = (*unknown_list)[i - 1];
  3610. if (x->type != SOLUTION_PHASE_BOUNDARY && x->type != ALK && x->type != GAS_MOLES && x->type != S_S_MOLES)
  3611. {
  3612. if (x->type == PP && !x->pure_phase->force_equality)
  3613. continue;
  3614. if (md->mass_water_switch && x == md->mass_oxygen_unknown)
  3615. continue;
  3616. if (x->type == EXCH && x->moles <= MIN_RELATED_SURFACE)
  3617. continue;
  3618. if (x->type == SURFACE && x->phase_unknown == NULL && x->moles <= MIN_RELATED_SURFACE)
  3619. continue;
  3620. if ((x->type == SURFACE_CB || x->type == SURFACE_CB1 || x->type == SURFACE_CB2) && x->surface_charge->grams <= MIN_RELATED_SURFACE)
  3621. continue;
  3622. if (x->type == SURFACE && x->phase_unknown != NULL && x->phase_unknown->moles <= MIN_RELATED_SURFACE && x->phase_unknown->pure_phase->add_formula.IsEmpty())
  3623. continue;
  3624. if ((x->type == SURFACE_CB || x->type == SURFACE_CB1 || x->type == SURFACE_CB2) && x_b->phase_unknown != NULL && x->surface_charge->grams <= MIN_RELATED_SURFACE && x_b->phase_unknown->pure_phase->add_formula.IsEmpty())
  3625. continue;
  3626. #ifdef DEBUG_MOHID
  3627. if (d.debug_status)
  3628. fprintf(d.ineq_f, "Ineq-16) %d %d %d %20.20e %20.20e %s\n", i, x->number, x->type, x->moles, x->f, x->name.CharPtr());
  3629. #endif
  3630. memcpy ((void *) &ineq_array[count_rows * max_column_count], (void *) &arr[i * (count_unknowns + 1)], (size_t) (count_unknowns + 1) * sizeof (LDBLE));
  3631. back_eq[count_rows] = i;
  3632. #ifdef DEBUG_MOHID
  3633. if (d.debug_status)
  3634. fprintf(d.ineq_f, "Ineq-17) %d %d\n", count_rows, back_eq[count_rows]);
  3635. #endif
  3636. if (md->mass_water_switch && x == md->mass_hydrogen_unknown)
  3637. {
  3638. k = md->mass_oxygen_unknown->number;
  3639. for (j = 0; j < count_unknowns; j++)
  3640. {
  3641. ineq_array[count_rows * max_column_count + j] -= 2 * arr[k * (count_unknowns + 1) + j];
  3642. #ifdef DEBUG_MOHID
  3643. if (d.debug_status)
  3644. fprintf(d.ineq_f, "Ineq-18) %d %d %20.20e %20.20e\n", count_rows * max_column_count + j, k * (count_unknowns + 1) + j, ineq_array[count_rows * max_column_count + j], arr[k * (count_unknowns + 1) + j]);
  3645. #endif
  3646. }
  3647. }
  3648. count_rows++;
  3649. }
  3650. else if (x->type == PITZER_GAMMA)
  3651. {
  3652. memcpy ((void *) &ineq_array[count_rows * max_column_count], (void *) &arr[i * (count_unknowns + 1)], (size_t) (count_unknowns + 1) * sizeof (LDBLE));
  3653. back_eq[count_rows] = i;
  3654. #ifdef DEBUG_MOHID
  3655. if (d.debug_status)
  3656. fprintf(d.ineq_f, "Ineq-19) %d %d\n", count_rows, back_eq[count_rows]);
  3657. #endif
  3658. }
  3659. }
  3660. count_equal = count_rows - count_optimize;
  3661. // Copy inequality constraints into ineq
  3662. if (md->pure_phase_unknown != NULL)
  3663. {
  3664. for (i = 0; i < count_unknowns; i++)
  3665. {
  3666. x = (*unknown_list)[i];
  3667. if (x->type == PP)
  3668. {
  3669. if (x->pure_phase->phase->in == FALSE)
  3670. continue;
  3671. #ifdef DEBUG_MOHID
  3672. if (d.debug_status)
  3673. fprintf(d.ineq_f, "Ineq-20) %d %d %d %20.20e %20.20e\n", i, x->number, x->type, x->moles, x->f);
  3674. #endif
  3675. if (x->moles <= 0.0 && x->f > 0e-8 && x->pure_phase->add_formula.IsEmpty())
  3676. continue;
  3677. else if (x->moles <= 0.0)
  3678. {
  3679. delta1[i] = -1.0;
  3680. #ifdef DEBUG_MOHID
  3681. if (d.debug_status)
  3682. fprintf(d.ineq_f, "Ineq-21) %d %20.20e\n", i, delta1[i]);
  3683. #endif
  3684. }
  3685. else if (x->f < 0e-8 && x->dissolve_only && (x->moles - x->pure_phase->initial_moles >= 0))
  3686. continue;
  3687. else
  3688. {
  3689. memcpy ((void *) &ineq_array[count_rows * max_column_count], (void *) &zero[0], (size_t) (count_unknowns + 1) * sizeof (LDBLE));
  3690. ineq_array[count_rows * max_column_count + i] = 1.0;
  3691. ineq_array[count_rows * max_column_count + count_unknowns] = x->moles;
  3692. back_eq[count_rows] = i;
  3693. #ifdef DEBUG_MOHID
  3694. if (d.debug_status)
  3695. fprintf(d.ineq_f, "Ineq-22) %d %20.20e %d %20.20e %d %d\n",
  3696. count_rows * max_column_count + i,
  3697. ineq_array[count_rows * max_column_count + i],
  3698. count_rows * max_column_count + count_unknowns,
  3699. ineq_array[count_rows * max_column_count + count_unknowns],
  3700. back_eq[count_rows],
  3701. count_rows);
  3702. #endif
  3703. count_rows++;
  3704. }
  3705. if (x->dissolve_only)
  3706. {
  3707. memcpy ((void *) &ineq_array[count_rows * max_column_count], (void *) &zero[0], (size_t) (count_unknowns + 1) * sizeof (LDBLE));
  3708. ineq_array[count_rows * max_column_count + i] = -1.0;
  3709. ineq_array[count_rows * max_column_count + count_unknowns] = x->pure_phase->initial_moles - x->moles;
  3710. back_eq[count_rows] = i;
  3711. #ifdef DEBUG_MOHID
  3712. if (d.debug_status)
  3713. fprintf(d.ineq_f, "Ineq-23) %d %20.20e %d %20.20e %d %d\n",
  3714. count_rows * max_column_count + i,
  3715. ineq_array[count_rows * max_column_count + i],
  3716. count_rows * max_column_count + count_unknowns,
  3717. ineq_array[count_rows * max_column_count + count_unknowns],
  3718. back_eq[count_rows],
  3719. count_rows);
  3720. #endif
  3721. count_rows++;
  3722. }
  3723. }
  3724. }
  3725. }
  3726. /*
  3727. * Hydrogen mass balance is good
  3728. */
  3729. /*
  3730. * No moles and undersaturated, mass transfer must be zero
  3731. */
  3732. if (md->pure_phase_unknown != NULL)
  3733. {
  3734. for (i = 0; i < count_unknowns; i++)
  3735. {
  3736. x = (*unknown_list)[i];
  3737. if (x->type == PP)
  3738. {
  3739. #ifdef DEBUG_MOHID
  3740. if (d.debug_status)
  3741. fprintf(d.ineq_f, "Ineq-24) %d %d %d %20.20e %20.20e\n", i, x->number, x->type, x->moles, x->f);
  3742. #endif
  3743. if ((x->moles <= 0.0 && x->f > 0e-8 && x->pure_phase->add_formula.IsEmpty()) || !x->pure_phase->phase->in)
  3744. for (j = 0; j < count_rows; j++)
  3745. {
  3746. ineq_array[j * max_column_count + i] = 0.0;
  3747. #ifdef DEBUG_MOHID
  3748. if (d.debug_status)
  3749. fprintf(d.ineq_f, "Ineq-25) %d %20.20e\n", j * max_column_count + i, ineq_array[j * max_column_count + i]);
  3750. #endif
  3751. }
  3752. if (x->dissolve_only && x->f < 0e-8 && (x->moles - x->pure_phase->initial_moles >= 0))
  3753. for (j = 0; j < count_rows; j++)
  3754. {
  3755. ineq_array[j * max_column_count + i] = 0.0;
  3756. #ifdef DEBUG_MOHID
  3757. if (d.debug_status)
  3758. fprintf(d.ineq_f, "Ineq-26) %d %20.20e\n", j * max_column_count + i, ineq_array[j * max_column_count + i]);
  3759. #endif
  3760. }
  3761. }
  3762. }
  3763. }
  3764. // No moles of exchanger
  3765. if (md->use.exc_p != NULL && (md->use.exc_p->related_phases || md->use.exc_p->related_rate))
  3766. for (i = 0; i < count_unknowns; i++)
  3767. {
  3768. x = (*unknown_list)[i];
  3769. if (x->type == EXCH && x->moles <= 0)
  3770. {
  3771. #ifdef DEBUG_MOHID
  3772. if (d.debug_status)
  3773. fprintf(d.ineq_f, "Ineq-27) %d %d %d %20.20e %20.20e\n", i, x->number, x->type, x->moles, x->f);
  3774. #endif
  3775. for (j = 0; j < count_rows; j++)
  3776. {
  3777. ineq_array[j * max_column_count + i] = 0.0;
  3778. #ifdef DEBUG_MOHID
  3779. if (d.debug_status)
  3780. fprintf(d.ineq_f, "Ineq-28) %d %20.20e\n", j * max_column_count + i, ineq_array[j * max_column_count + i]);
  3781. #endif
  3782. }
  3783. }
  3784. }
  3785. // No moles of surface
  3786. if (md->use.sur_p != NULL && (md->use.sur_p->related_phases || md->use.sur_p->related_rate))
  3787. for (i = 0; i < count_unknowns; i++)
  3788. {
  3789. x = (*unknown_list)[i];
  3790. if (i < 0)
  3791. x_b = (*unknown_list)[i - 1];
  3792. else
  3793. x_b = NULL;
  3794. if ((x->type == SURFACE && x->phase_unknown != NULL && x->phase_unknown->moles <= MIN_RELATED_SURFACE && x->phase_unknown->pure_phase->add_formula.IsEmpty()) || ((x->type == SURFACE_CB || x->type == SURFACE_CB1 || x->type == SURFACE_CB2) && x_b->phase_unknown != NULL && x->surface_charge->grams <= MIN_RELATED_SURFACE && x_b->phase_unknown->pure_phase->add_formula.IsEmpty()))
  3795. {
  3796. #ifdef DEBUG_MOHID
  3797. if (d.debug_status)
  3798. fprintf(d.ineq_f, "Ineq-29) %d %d %d %20.20e %20.20e\n", i, x->number, x->type, x->moles, x->f);
  3799. #endif
  3800. for (j = 0; j < count_rows; j++)
  3801. {
  3802. ineq_array[j * max_column_count + i] = 0.0;
  3803. #ifdef DEBUG_MOHID
  3804. if (d.debug_status)
  3805. fprintf(d.ineq_f, "Ineq-30) %d %20.20e\n", j * max_column_count + i, ineq_array[j * max_column_count + i]);
  3806. #endif
  3807. }
  3808. }
  3809. }
  3810. // No moles of surface
  3811. if (md->use.sur_p != NULL)
  3812. for (i = 0; i < count_unknowns; i++)
  3813. {
  3814. x = (*unknown_list)[i];
  3815. if (i > 0)
  3816. x_b = (*unknown_list)[i - 1];
  3817. else
  3818. x_b = NULL;
  3819. if ((x->type == SURFACE && x->phase_unknown == NULL && x->moles <= MIN_RELATED_SURFACE) || ((x->type == SURFACE_CB || x->type == SURFACE_CB1 || x->type == SURFACE_CB2) && x_b->phase_unknown == NULL && x->surface_charge->grams <= MIN_RELATED_SURFACE))
  3820. {
  3821. #ifdef DEBUG_MOHID
  3822. if (d.debug_status)
  3823. fprintf(d.ineq_f, "Ineq-31) %d %d %d %20.20e %20.20e\n", i, x->number, x->type, x->moles, x->f);
  3824. #endif
  3825. for (j = 0; j < count_rows; j++)
  3826. {
  3827. ineq_array[j * max_column_count + i] = 0.0;
  3828. #ifdef DEBUG_MOHID
  3829. if (d.debug_status)
  3830. fprintf(d.ineq_f, "Ineq-32) %d %20.20e\n", j * max_column_count + i, ineq_array[j * max_column_count + i]);
  3831. #endif
  3832. }
  3833. }
  3834. }
  3835. // Moles of gas must be >= zero
  3836. if (md->gas_in)
  3837. {
  3838. i = md->gas_unknown->number;
  3839. memcpy ((void *) &ineq_array[count_rows * max_column_count], (void *) &zero[0], (size_t) (count_unknowns + 1) * sizeof (LDBLE));
  3840. ineq_array[count_rows * max_column_count + i] = -1.0;
  3841. ineq_array[count_rows * max_column_count + count_unknowns] = x->moles;
  3842. back_eq[count_rows] = i;
  3843. #ifdef DEBUG_MOHID
  3844. if (d.debug_status)
  3845. fprintf(d.ineq_f, "Ineq-33) %d %20.20e %d %20.20e %d %d\n",
  3846. count_rows * max_column_count + i,
  3847. ineq_array[count_rows * max_column_count + i],
  3848. count_rows * max_column_count + count_unknowns,
  3849. ineq_array[count_rows * max_column_count + count_unknowns],
  3850. back_eq[count_rows],
  3851. count_rows);
  3852. #endif
  3853. count_rows++;
  3854. }
  3855. else if (md->use.gas_p != NULL && !md->gas_in)
  3856. {
  3857. i = md->gas_unknown->number;
  3858. for (j = 0; j < count_rows; j++)
  3859. {
  3860. ineq_array[j * max_column_count + i] = 0.0;
  3861. #ifdef DEBUG_MOHID
  3862. if (d.debug_status)
  3863. fprintf(d.ineq_f, "Ineq-34) %d %20.20e\n",
  3864. j * max_column_count + i,
  3865. ineq_array[j * max_column_count + i]);
  3866. #endif
  3867. }
  3868. }
  3869. // Phase must be "in" and moles of solid solution must be >= zero
  3870. if (md->s_s_unknown != NULL)
  3871. for (i = md->s_s_unknown->number; i < count_unknowns; i++)
  3872. {
  3873. //ToDo: this code was copied without the definition of x, so, x would be anything...
  3874. x = (*unknown_list)[i];
  3875. if (x->type != S_S_MOLES)
  3876. break;
  3877. #ifdef DEBUG_MOHID
  3878. if (d.debug_status)
  3879. fprintf(d.ineq_f, "Ineq-35) %d %d %d %20.20e %20.20e\n", i, x->number, x->type, x->moles, x->f);
  3880. #endif
  3881. if (x->p->in && x->s_s_in)
  3882. {
  3883. memcpy ((void *) &ineq_array[count_rows * max_column_count], (void *) &zero[0], (size_t) (count_unknowns + 1) * sizeof (LDBLE));
  3884. ineq_array[count_rows * max_column_count + i] = 1.0;
  3885. ineq_array[count_rows * max_column_count + count_unknowns] = (LDBLE)0.99 * x->moles - (LDBLE)MIN_TOTAL_SS;
  3886. back_eq[count_rows] = i;
  3887. #ifdef DEBUG_MOHID
  3888. if (d.debug_status)
  3889. fprintf(d.ineq_f, "Ineq-36) %d %20.20e %d %20.20e %d %d\n",
  3890. count_rows * max_column_count + i,
  3891. ineq_array[count_rows * max_column_count + i],
  3892. count_rows * max_column_count + count_unknowns,
  3893. ineq_array[count_rows * max_column_count + count_unknowns],
  3894. back_eq[count_rows],
  3895. count_rows);
  3896. #endif
  3897. count_rows++;
  3898. }
  3899. else
  3900. for (j = 0; j < count_rows; j++)
  3901. {
  3902. ineq_array[j * max_column_count + i] = 0.0;
  3903. #ifdef DEBUG_MOHID
  3904. if (d.debug_status)
  3905. fprintf(d.ineq_f, "Ineq-37) %d %20.20e\n",
  3906. j * max_column_count + i,
  3907. ineq_array[j * max_column_count + i]);
  3908. #endif
  3909. }
  3910. }
  3911. // Add inequality if total moles of element is less than zero
  3912. if (md->negative_concentrations)
  3913. {
  3914. for (i = 0; i < count_unknowns; i++)
  3915. {
  3916. x = (*unknown_list)[i];
  3917. if (x->type == MB && x->moles < 0.0)
  3918. {
  3919. #ifdef DEBUG_MOHID
  3920. if (d.debug_status)
  3921. fprintf(d.ineq_f, "Ineq-38) %d %d %d %20.20e %20.20e\n", i, x->number, x->type, x->moles, x->f);
  3922. #endif
  3923. memcpy ((void *) &ineq_array[count_rows * max_column_count], (void *) &arr[i * (count_unknowns + 1)], (size_t) (count_unknowns + 1) * sizeof (LDBLE));
  3924. back_eq[count_rows] = i;
  3925. for (j = 0; j < count_unknowns; j++)
  3926. if ((*unknown_list)[j]->type < PP)
  3927. {
  3928. ineq_array[count_rows * max_column_count + j] = 0.0;
  3929. #ifdef DEBUG_MOHID
  3930. if (d.debug_status)
  3931. fprintf(d.ineq_f, "Ineq-39) %d %20.20e\n",
  3932. count_rows * max_column_count + j,
  3933. ineq_array[count_rows * max_column_count + j]);
  3934. #endif
  3935. }
  3936. count_rows++;
  3937. }
  3938. }
  3939. }
  3940. // Zero column for mass of water
  3941. if (md->mass_oxygen_unknown != NULL && md->mass_water_switch)
  3942. {
  3943. k = md->mass_oxygen_unknown->number;
  3944. for (j = 0; j < count_rows + 1; j++)
  3945. {
  3946. ineq_array[j * max_column_count + k] = 0;
  3947. #ifdef DEBUG_MOHID
  3948. if (d.debug_status)
  3949. fprintf(d.ineq_f, "Ineq-40) %d %20.20e\n",
  3950. j * max_column_count + k,
  3951. ineq_array[j * max_column_count + k]);
  3952. #endif
  3953. }
  3954. }
  3955. // Scale column for pure phases
  3956. for (i = 0; i < count_unknowns; i++)
  3957. {
  3958. x = (*unknown_list)[i];
  3959. if ((x->type == PP || x->type == S_S_MOLES) && md->pp_column_scale != 1.0)
  3960. {
  3961. #ifdef DEBUG_MOHID
  3962. if (d.debug_status)
  3963. fprintf(d.ineq_f, "Ineq-41) %d %d %d %20.20e %20.20e\n", i, x->number, x->type, x->moles, x->f);
  3964. #endif
  3965. for (j = 0; j < count_rows; j++)
  3966. {
  3967. ineq_array[j * max_column_count + i] *= md->pp_column_scale;
  3968. #ifdef DEBUG_MOHID
  3969. if (d.debug_status)
  3970. fprintf(d.ineq_f, "Ineq-42) %d %20.20e\n",
  3971. j * max_column_count + i,
  3972. ineq_array[j * max_column_count + i]);
  3973. #endif
  3974. }
  3975. normal[i] = md->pp_column_scale;
  3976. #ifdef DEBUG_MOHID
  3977. if (d.debug_status)
  3978. fprintf(d.ineq_f, "Ineq-43) %d %20.20e\n",
  3979. i,
  3980. normal[i]);
  3981. #endif
  3982. }
  3983. }
  3984. // Calculate dimensions
  3985. k = count_optimize;
  3986. l = count_equal;
  3987. m = count_rows - l - k;
  3988. if (m < 0)
  3989. m = 0;
  3990. n = count_unknowns;
  3991. klmd = max_row_count - 2;
  3992. nklmd = n + klmd;
  3993. n2d = n + 2;
  3994. kode = 1;
  3995. if (in_kode == 2)
  3996. kode = 1;
  3997. iter = 200;
  3998. #ifdef DEBUG_MOHID
  3999. if (d.debug_status)
  4000. fprintf(d.ineq_f, "Ineq-44) %d %d %d %d %d %d\n",
  4001. k, l, m, klmd, nklmd, n2d);
  4002. #endif
  4003. // Allocate space for arrays
  4004. CuNewMax(2 * nklmd);
  4005. IuNewMax(2 * nklmd);
  4006. IsNewMax(klmd);
  4007. CL1(k, l, m, n, nklmd, n2d, &ineq_array[0], &kode, md->ineq_tol, &iter, &delta1[0], &res[0], &error, &cu[0], &iu[0], &is[0], false);
  4008. if (kode == 1)
  4009. return_code = ERROR;
  4010. else if (kode == 2)
  4011. return_code = 2;
  4012. else
  4013. return_code = OK;
  4014. // Copy delta1 into delta and scale
  4015. memcpy ((void *) &delta[0], (void *) &delta1[0], (size_t) count_unknowns * sizeof (LDBLE));
  4016. for (i = 0; i < count_unknowns; i++)
  4017. delta[i] *= normal[i];
  4018. #ifdef DEBUG_MOHID
  4019. if (d.debug_status)
  4020. for (int debug_i = 0; debug_i < count_unknowns; debug_i++)
  4021. fprintf(d.ineq_f, "Ineq-45) %20.20e\n", delta[debug_i]);
  4022. #endif
  4023. // Rescale columns of array
  4024. for (i = 0; i < count_unknowns; i++)
  4025. if (normal[i] != 1.0)
  4026. for (j = 0; j < count_unknowns; j++)
  4027. {
  4028. arr[j * (count_unknowns + 1) + i] /= normal[i];
  4029. #ifdef DEBUG_MOHID
  4030. if (d.debug_status)
  4031. fprintf(d.ineq_f, "Ineq-46) %d %20.20e %20.20e %d\n",
  4032. j * (count_unknowns + 1) + i,
  4033. arr[j * (count_unknowns + 1) + i],
  4034. normal[i],
  4035. i);
  4036. #endif
  4037. }
  4038. #ifdef DEBUG_MOHID
  4039. if (d.debug_status)
  4040. fprintf(d.ineq_f, "Ineq-FIM)====================\n");
  4041. #endif
  4042. return (return_code);
  4043. }
  4044. bool ModelEngine::Reset()
  4045. {
  4046. //
  4047. // Checks deltas (changes to unknowns) to make sure they are reasonable
  4048. // Scales deltas if necessary
  4049. // Updates unknowns with deltas
  4050. //
  4051. int i, j;
  4052. bool converge;
  4053. LDBLE up, down;
  4054. LDBLE d_;
  4055. LDBLE factor, f0;
  4056. LDBLE sum_deltas;
  4057. LDBLE step_up;
  4058. LDBLE mu_calc;
  4059. LDBLE old_moles;
  4060. Master *m;
  4061. /*
  4062. if (d.si_count == 132)
  4063. getch();
  4064. */
  4065. // Calculate interphase mass transfers
  4066. step_up = log(md->step_size_now);
  4067. factor = 1.0;
  4068. Unknown *x;
  4069. STCoef *stc_p;
  4070. if ((md->pure_phase_unknown != NULL || md->s_s_unknown != NULL) && !md->calculating_deriv)
  4071. {
  4072. // Don't take out more mineral than is present
  4073. for (i = 0; i < count_unknowns; i++)
  4074. {
  4075. x = (*unknown_list)[i];
  4076. if (x->type == PP || x->type == S_S_MOLES)
  4077. {
  4078. if (delta[i] < -1e8)
  4079. delta[i] = -10.0;
  4080. else if (delta[i] > 1e8)
  4081. delta[i] = 10.0;
  4082. #ifdef DEBUG_MOHID
  4083. if (d.debug_status)
  4084. fprintf(d.reset_f, "reset-1) %s %d %d %20.20e %20.20e\n", x->name.CharPtr(), x->number, x->type, x->moles, delta[i]);
  4085. #endif
  4086. if (x->dissolve_only)
  4087. {
  4088. if (delta[i] < 0.0 && (-delta[i] > (x->pure_phase->initial_moles - x->moles)))
  4089. {
  4090. #ifdef DEBUG_MOHID
  4091. if (d.debug_status)
  4092. fprintf(d.reset_f, "reset-2) %s %20.20e\n", x->pure_phase->name.CharPtr(), x->pure_phase->initial_moles);
  4093. #endif
  4094. if ((x->pure_phase->initial_moles - x->moles) != 0.0)
  4095. {
  4096. f0 = fabs (delta[i] / (x->pure_phase->initial_moles - x->moles));
  4097. if (f0 > factor)
  4098. factor = f0;
  4099. #ifdef DEBUG_MOHID
  4100. if (d.debug_status)
  4101. fprintf(d.reset_f, "reset-3) %20.20e %20.20e\n", f0, factor);
  4102. #endif
  4103. }
  4104. else
  4105. {
  4106. delta[i] = 0;
  4107. #ifdef DEBUG_MOHID
  4108. if (d.debug_status)
  4109. fprintf(d.reset_f, "reset-4) %20.20e\n", delta[i]);
  4110. #endif
  4111. }
  4112. }
  4113. }
  4114. if (x->moles > 0.0 && delta[i] > x->moles)
  4115. {
  4116. f0 = delta[i] / x->moles;
  4117. if (f0 > factor)
  4118. factor = f0;
  4119. #ifdef DEBUG_MOHID
  4120. if (d.debug_status)
  4121. fprintf(d.reset_f, "reset-5) %20.20e %20.20e\n", f0, factor);
  4122. #endif
  4123. }
  4124. else if (delta[i] > 0.0 && x->moles <= 0.0)
  4125. {
  4126. delta[i] = 0.0;
  4127. #ifdef DEBUG_MOHID
  4128. if (d.debug_status)
  4129. fprintf(d.reset_f, "reset-6) %20.20e\n", delta[i]);
  4130. #endif
  4131. }
  4132. else if (delta[i] < (LDBLE)-100.0)
  4133. {
  4134. f0 = -delta[i] / (LDBLE)100.0;
  4135. if (f0 > factor)
  4136. factor = f0;
  4137. #ifdef DEBUG_MOHID
  4138. if (d.debug_status)
  4139. fprintf(d.reset_f, "reset-7) %20.20e %20.20e\n", f0, factor);
  4140. #endif
  4141. }
  4142. }
  4143. }
  4144. }
  4145. // Calculate change in element concentrations due to pure phases and gases
  4146. for (i = 0; i < count_unknowns; i++)
  4147. if (_isnan(delta[i]))
  4148. {
  4149. delta[i] = 0;
  4150. #ifdef DEBUG_MOHID
  4151. if (d.debug_status)
  4152. fprintf(d.reset_f, "reset-8) %d %20.20e\n", i, delta[i]);
  4153. #endif
  4154. }
  4155. if (md->pure_phase_unknown != NULL || md->gas_unknown != NULL || md->s_s_unknown != NULL)
  4156. {
  4157. for (i = 0; i < count_unknowns; i++)
  4158. (*unknown_list)[i]->delta = 0.0;
  4159. for (i = 0; i < md->sum_delta->Count(); i++)
  4160. {
  4161. stc_p = (*md->sum_delta)[i];
  4162. *stc_p->target += *stc_p->source * stc_p->coef;
  4163. #ifdef DEBUG_MOHID
  4164. if (d.debug_status)
  4165. fprintf(d.reset_f, "reset-9) %d %20.20e %20.20e %20.20e\n", i, *stc_p->target, *stc_p->source, stc_p->coef);
  4166. #endif
  4167. }
  4168. // Apply factor from minerals to deltas
  4169. for (i = 0; i < count_unknowns; i++)
  4170. {
  4171. x = (*unknown_list)[i];
  4172. x->delta /= factor;
  4173. if (x->type == PP || x->type == S_S_MOLES)
  4174. delta[i] /= factor;
  4175. #ifdef DEBUG_MOHID
  4176. if (d.debug_status)
  4177. fprintf(d.reset_f, "reset-10) %s %d %d %20.20e %20.20e\n",
  4178. x->name.CharPtr(),
  4179. x->number, x->type,
  4180. x->delta, delta[i]);
  4181. #endif
  4182. }
  4183. }
  4184. // Calc factor for mass balance equations for aqueous unknowns
  4185. factor = 1.0;
  4186. sum_deltas = 0.0;
  4187. for (i = 0; i < count_unknowns; i++)
  4188. {
  4189. x = (*unknown_list)[i];
  4190. // fixes underflow problem on Windows
  4191. if (delta[i] > 0)
  4192. sum_deltas += delta[i];
  4193. else
  4194. sum_deltas -= delta[i];
  4195. #ifdef DEBUG_MOHID
  4196. if (d.debug_status)
  4197. fprintf(d.reset_f, "reset-11) %d %d %20.20e %20.20e %20.20e %20.20e\n", i, x->type, x->moles, sum_deltas, step_up, md->mu_x);
  4198. #endif
  4199. if (!md->calculating_deriv)
  4200. {
  4201. up = step_up;
  4202. down = up;
  4203. if (x->type <= SOLUTION_PHASE_BOUNDARY)
  4204. {
  4205. up = step_up;
  4206. down = (LDBLE)1.3 * up;
  4207. }
  4208. else if (x->type == MU)
  4209. {
  4210. up = 100 * md->mu_x;
  4211. down = md->mu_x;
  4212. }
  4213. else if (x->type == AH2O)
  4214. down = up;
  4215. else if (x->type == MH)
  4216. {
  4217. up = log (md->pe_step_size_now);
  4218. down = (LDBLE)1.3 * up;
  4219. }
  4220. else if (x->type == MH2O)
  4221. {
  4222. up = log ((LDBLE)1.3);
  4223. down = log ((LDBLE)1.2);
  4224. }
  4225. else if (x->type == PP)
  4226. continue;
  4227. else if (x->type == GAS_MOLES)
  4228. {
  4229. up = (LDBLE)1000. * x->moles;
  4230. if (up <= 0.0)
  4231. up = (LDBLE)1e-1;
  4232. if (up >= 1.0)
  4233. up = 1.0;
  4234. down = x->moles;
  4235. }
  4236. else if (x->type == S_S_MOLES)
  4237. continue;
  4238. else if (x->type == EXCH)
  4239. {
  4240. up = step_up;
  4241. down = (LDBLE)1.3 * up;
  4242. }
  4243. else if (x->type == SURFACE)
  4244. {
  4245. up = step_up;
  4246. down = (LDBLE)1.3 * up;
  4247. }
  4248. else if (x->type == SURFACE_CB || x->type == SURFACE_CB1 || x->type == SURFACE_CB2)
  4249. {
  4250. up = step_up;
  4251. down = (LDBLE)1.3 * up;
  4252. }
  4253. if (delta[i] > 0.0)
  4254. {
  4255. f0 = delta[i] / up;
  4256. if (f0 > factor)
  4257. factor = f0;
  4258. }
  4259. else
  4260. {
  4261. f0 = delta[i] / (-down);
  4262. if (f0 > factor)
  4263. factor = f0;
  4264. }
  4265. #ifdef DEBUG_MOHID
  4266. if (d.debug_status)
  4267. fprintf(d.reset_f, "reset-12) %20.20e %20.20e\n", f0, factor);
  4268. #endif
  4269. }
  4270. }
  4271. factor = (LDBLE)1.0 / factor;
  4272. for (i = 0; i < count_unknowns; i++)
  4273. {
  4274. if ((*unknown_list)[i]->type != PP && (*unknown_list)[i]->type != S_S_MOLES)
  4275. delta[i] *= factor;
  4276. #ifdef DEBUG_MOHID
  4277. if (d.debug_status)
  4278. fprintf(d.reset_f, "reset-13) %20.20e %20.20e\n", delta[i], factor);
  4279. #endif
  4280. }
  4281. SurfaceDiffLayer *sdl_p;
  4282. // Solution mass balances: MB, ALK, CB, SOLUTION_PHASE_BOUNDARY
  4283. for (i = 0; i < count_unknowns; i++)
  4284. {
  4285. x = (*unknown_list)[i];
  4286. if (x->master->Count() > 0)
  4287. m = (*x->master)[0];
  4288. else
  4289. m = NULL;
  4290. #ifdef DEBUG_MOHID
  4291. if (d.debug_status)
  4292. fprintf(d.reset_f, "reset-14) %d %d %20.20e %20.20e\n",
  4293. i, x->type, x->moles, delta[i]);
  4294. #endif
  4295. if (x->type == MB || x->type == ALK || x->type == EXCH || x->type == SURFACE)
  4296. {
  4297. d_ = delta[i] / md->LOG_10;
  4298. #ifdef DEBUG_MOHID
  4299. if (d.debug_status)
  4300. fprintf(d.reset_f, "reset-15) %20.20e\n",
  4301. d_);
  4302. #endif
  4303. if (x->type == SURFACE)
  4304. {
  4305. old_moles = x->moles;
  4306. if (x->phase_unknown != NULL)
  4307. {
  4308. x->moles = x->surface_comp->phase_proportion * (x->phase_unknown->moles - delta[x->phase_unknown->number]);
  4309. if (x->phase_unknown->moles - delta[x->phase_unknown->number] <= MIN_RELATED_SURFACE)
  4310. {
  4311. x->moles = 0.0;
  4312. if (fabs (x->f) > MIN_RELATED_SURFACE)
  4313. m->s->la -= 5.;
  4314. }
  4315. if (old_moles <= 0 && x->moles > 0)
  4316. m->s->la = log10 (x->moles) - (LDBLE)5.;
  4317. #ifdef DEBUG_MOHID
  4318. if (d.debug_status)
  4319. fprintf(d.reset_f, "reset-16) %20.20e %s %20.20e %d %20.20e %20.20e %20.20e %20.20e\n",
  4320. x->surface_comp->phase_proportion,
  4321. x->phase_unknown->name.CharPtr(),
  4322. x->phase_unknown->moles,
  4323. x->phase_unknown->number,
  4324. delta[x->phase_unknown->number],
  4325. x->moles,
  4326. x->f,
  4327. m->s->la);
  4328. #endif
  4329. }
  4330. }
  4331. if (x->type == EXCH && x->moles <= MIN_RELATED_SURFACE)
  4332. {
  4333. x->moles = 0.0;
  4334. if (fabs (x->f) > MIN_RELATED_SURFACE)
  4335. m->s->la -= 5.;
  4336. #ifdef DEBUG_MOHID
  4337. if (d.debug_status)
  4338. fprintf(d.reset_f, "reset-17) %20.15 %20.20e\n",
  4339. x->moles,
  4340. m->s->la);
  4341. #endif
  4342. }
  4343. m->s->la += d_;
  4344. if (m->s->la < (LDBLE) (DBL_MIN_10_EXP + 10))
  4345. m->s->la = (LDBLE) (DBL_MIN_10_EXP + 10);
  4346. #ifdef DEBUG_MOHID
  4347. if (d.debug_status)
  4348. fprintf(d.reset_f, "reset-18) %20.20e\n",
  4349. m->s->la);
  4350. #endif
  4351. }
  4352. else if (x->type == SURFACE_CB || x->type == SURFACE_CB1 || x->type == SURFACE_CB2)
  4353. {
  4354. d_ = delta[i] / md->LOG_10;
  4355. #ifdef DEBUG_MOHID
  4356. if (d.debug_status)
  4357. fprintf(d.reset_f, "reset-19) %20.20e\n",
  4358. d_);
  4359. #endif
  4360. if (x->phase_unknown != NULL)
  4361. {
  4362. x->surface_charge->grams = (x->phase_unknown->moles - delta[x->phase_unknown->number]);
  4363. if (x->surface_charge->grams <= MIN_RELATED_SURFACE)
  4364. x->surface_charge->grams = 0.0;
  4365. }
  4366. if (x->surface_charge->grams <= MIN_RELATED_SURFACE)
  4367. x->surface_charge->grams = 0.0;
  4368. x->related_moles = x->surface_charge->grams;
  4369. m->s->la += d_;
  4370. #ifdef DEBUG_MOHID
  4371. if (d.debug_status)
  4372. fprintf(d.reset_f, "reset-20) %20.20e %s %20.20e %d %20.20e %20.20e %20.20e\n",
  4373. x->surface_charge->grams,
  4374. x->phase_unknown->name.CharPtr(),
  4375. x->phase_unknown->moles,
  4376. x->phase_unknown->number,
  4377. delta[x->phase_unknown->number],
  4378. x->related_moles,
  4379. m->s->la);
  4380. #endif
  4381. // recalculate g's for component
  4382. if (md->dl_type_x != NO_DL && (md->use.sur_p->type == DDL || (md->use.sur_p->type == CD_MUSIC && x->type == SURFACE_CB2)))
  4383. {
  4384. for (j = 0; j < x->surface_charge->g->Count(); j++)
  4385. {
  4386. sdl_p = (*x->surface_charge->g)[j];
  4387. if (md->use.sur_p->dl_type != DONNAN_DL)
  4388. sdl_p->g += sdl_p->dg * delta[i];
  4389. #ifdef DEBUG_MOHID
  4390. if (d.debug_status)
  4391. fprintf(d.reset_f, "reset-21) %20.20e %20.20e\n",
  4392. sdl_p->g,
  4393. delta[i]);
  4394. #endif
  4395. }
  4396. //ToDo: Put in "calcalldonnan file a "mark" saying that was called from here
  4397. if (md->use.sur_p->dl_type == DONNAN_DL)
  4398. CalcAllDonnan ();
  4399. }
  4400. }
  4401. else if (x->type == SOLUTION_PHASE_BOUNDARY)
  4402. {
  4403. d_ = delta[i] / md->LOG_10;
  4404. m->s->la += d_;
  4405. #ifdef DEBUG_MOHID
  4406. if (d.debug_status)
  4407. fprintf(d.reset_f, "reset-22) %20.20e %20.20e\n",
  4408. d_,
  4409. m->s->la);
  4410. #endif
  4411. }
  4412. else if (x->type == CB)
  4413. {
  4414. d_ = delta[i] / md->LOG_10;
  4415. m->s->la += d_;
  4416. #ifdef DEBUG_MOHID
  4417. if (d.debug_status)
  4418. fprintf(d.reset_f, "reset-23) %20.20e %20.20e\n",
  4419. d_,
  4420. m->s->la);
  4421. #endif
  4422. }
  4423. else if (x->type == MU)
  4424. {
  4425. mu_calc = (LDBLE)0.5 * md->mu_unknown->f / md->mass_water_aq_x;
  4426. d_ = md->mu_x + delta[i];
  4427. if (d_ < 1e-7)
  4428. {
  4429. delta[i] = sqrt(mu_calc * md->mu_x) - md->mu_x;
  4430. md->mu_x = sqrt(mu_calc * md->mu_x);
  4431. //d.PrintSpeciesInfoToFile("Reset-1", md->species_info_list, md, gd);
  4432. }
  4433. else
  4434. {
  4435. md->mu_x += delta[i];
  4436. //d.PrintSpeciesInfoToFile("Reset-2", md->species_info_list, md, gd);
  4437. }
  4438. if (md->mu_x <= 1e-8)
  4439. {
  4440. md->mu_x = (LDBLE)1e-8;
  4441. //d.PrintSpeciesInfoToFile("Reset-3", md->species_info_list, md, gd);
  4442. }
  4443. #ifdef DEBUG_MOHID
  4444. if (d.debug_status)
  4445. fprintf(d.reset_f, "reset-24) %20.20e %20.20e %20.20e %20.20e\n",
  4446. d_,
  4447. mu_calc,
  4448. delta[i],
  4449. md->mu_x);
  4450. #endif
  4451. }
  4452. else if (x->type == AH2O)
  4453. {
  4454. d_ = delta[i] / md->LOG_10;
  4455. gd->s_h2o->la += d_;
  4456. if (gd->s_h2o->la < -1.0)
  4457. {
  4458. d_ = (LDBLE)-1.0 - gd->s_h2o->la;
  4459. delta[i] = d_ * md->LOG_10;
  4460. gd->s_h2o->la = -1.0;
  4461. }
  4462. #ifdef DEBUG_MOHID
  4463. if (d.debug_status)
  4464. fprintf(d.reset_f, "reset-25) %20.20e %20.20e %20.20e\n",
  4465. d_,
  4466. gd->s_h2o->la,
  4467. delta[i]);
  4468. #endif
  4469. }
  4470. else if (x->type == MH)
  4471. {
  4472. d_ = delta[i] / md->LOG_10;
  4473. gd->s_eminus->la += d_;
  4474. #ifdef DEBUG_MOHID
  4475. if (d.debug_status)
  4476. fprintf(d.reset_f, "reset-26) %20.20e %20.20e %20.20e\n",
  4477. d_,
  4478. gd->s_eminus->la,
  4479. delta[i]);
  4480. #endif
  4481. }
  4482. else if (x->type == MH2O)
  4483. {
  4484. if (md->mass_water_switch)
  4485. continue;
  4486. d_ = exp(delta[i]);
  4487. md->mass_water_aq_x *= d_;
  4488. md->mass_water_bulk_x = md->mass_water_aq_x + md->mass_water_surfaces_x;
  4489. m->s->moles = md->mass_water_aq_x / md->gfw_water;
  4490. if (md->use.sur_p != NULL && md->use.sur_p->debye_lengths > 0)
  4491. m->s->moles = md->mass_water_bulk_x / md->gfw_water;
  4492. if (md->mass_water_aq_x < 1e-10)
  4493. return false;
  4494. #ifdef DEBUG_MOHID
  4495. if (d.debug_status)
  4496. fprintf(d.reset_f, "reset-27) %20.20e %20.20e %20.20e %20.20e\n",
  4497. d_,
  4498. md->mass_water_aq_x,
  4499. md->mass_water_bulk_x,
  4500. m->s->moles);
  4501. #endif
  4502. }
  4503. else if (x->type == PP)
  4504. {
  4505. if (Equal(x->moles, delta[i], md->ineq_tol))
  4506. x->moles = 0.0;
  4507. else
  4508. x->moles -= delta[i];
  4509. if (x->dissolve_only && Equal(x->moles, x->pure_phase->initial_moles, md->ineq_tol))
  4510. x->moles = x->pure_phase->initial_moles;
  4511. #ifdef DEBUG_MOHID
  4512. if (d.debug_status)
  4513. fprintf(d.reset_f, "reset-28) %20.20e %20.20e %20.20e %20.20e\n",
  4514. x->moles,
  4515. x->pure_phase->initial_moles,
  4516. md->ineq_tol,
  4517. delta[i]);
  4518. #endif
  4519. }
  4520. else if (x->type == GAS_MOLES)
  4521. {
  4522. x->moles += delta[i];
  4523. if (x->moles < MIN_TOTAL)
  4524. x->moles = (LDBLE)MIN_TOTAL;
  4525. #ifdef DEBUG_MOHID
  4526. if (d.debug_status)
  4527. fprintf(d.reset_f, "reset-29) %20.20e %20.20e\n",
  4528. x->moles,
  4529. delta[i]);
  4530. #endif
  4531. }
  4532. else if (x->type == S_S_MOLES)
  4533. {
  4534. x->moles -= delta[i];
  4535. if (x->moles < MIN_TOTAL_SS && !md->calculating_deriv)
  4536. x->moles = (LDBLE)MIN_TOTAL_SS;
  4537. x->s_s_comp->moles = x->moles;
  4538. #ifdef DEBUG_MOHID
  4539. if (d.debug_status)
  4540. fprintf(d.reset_f, "reset-30) %20.20e %20.20e\n",
  4541. x->moles,
  4542. delta[i]);
  4543. #endif
  4544. }
  4545. else if (x->type == PITZER_GAMMA)
  4546. {
  4547. if (!md->full_pitzer)
  4548. continue;
  4549. d_ = delta[i];
  4550. x->s->lg += d_;
  4551. #ifdef DEBUG_MOHID
  4552. if (d.debug_status)
  4553. fprintf(d.reset_f, "reset-31) %20.20e %20.20e\n",
  4554. x->s->lg,
  4555. delta[i]);
  4556. #endif
  4557. }
  4558. }
  4559. // Reset total molalities in mass balance equations
  4560. if (md->pure_phase_unknown != NULL || md->gas_unknown != NULL || md->s_s_unknown != NULL)
  4561. {
  4562. for (i = 0; i < count_unknowns; i++)
  4563. {
  4564. x = (*unknown_list)[i];
  4565. if (x->type == MB || x->type == MH || x->type == MH2O || x->type == CB || x->type == EXCH || x->type == SURFACE)
  4566. {
  4567. if (x->type == SURFACE)
  4568. x->delta = 0.0;
  4569. x->moles += x->delta;
  4570. }
  4571. #ifdef DEBUG_MOHID
  4572. if (d.debug_status)
  4573. fprintf(d.reset_f, "reset-32) %d %20.20e %20.20e\n",
  4574. i,
  4575. x->delta,
  4576. x->moles);
  4577. #endif
  4578. }
  4579. }
  4580. #ifdef DEBUG_MOHID
  4581. if (d.debug_status)
  4582. fprintf(d.reset_f, "FIM===)\n");
  4583. #endif
  4584. converge = false;
  4585. return (converge);
  4586. }
  4587. bool ModelEngine::Gammas(LDBLE mu)
  4588. {
  4589. //
  4590. // Calculates gammas and [moles * d(ln gamma)/d mu] for all aqueous
  4591. // species.
  4592. //
  4593. //ToDo: Retirar a parte do debug
  4594. Species *s;
  4595. int i, j;
  4596. LDBLE a_llnl, b_llnl, bdot_llnl, log_g_co2, dln_g_co2, c2_llnl;
  4597. LDBLE s1, s2, s3;
  4598. LDBLE c1, c2, a, b;
  4599. LDBLE muhalf, equiv;
  4600. a_llnl = b_llnl = bdot_llnl = log_g_co2 = dln_g_co2 = c2_llnl = 0;
  4601. // compute temperature dependence of a and b for debye-huckel
  4602. s1 = (LDBLE)374.11 - md->tc_x;
  4603. s2 = pow ((LDBLE)s1, (LDBLE)(1.0 / 3.0));
  4604. s3 = (LDBLE)1.0 + (LDBLE)0.1342489 * s2 - (LDBLE)3.946263e-03 * s1;
  4605. s3 = s3 / ((LDBLE)3.1975 - (LDBLE)0.3151548 * s2 - (LDBLE)1.203374e-03 * s1 + (LDBLE)7.48908e-13 * (s1 * s1 * s1 * s1));
  4606. s3 = sqrt(s3);
  4607. if (md->tk_x >= 373.15)
  4608. c1 = (LDBLE)5321.0 / md->tk_x + (LDBLE)233.76 - md->tk_x * (md->tk_x * ((LDBLE)8.292e-07 * md->tk_x - (LDBLE)1.417e-03) + (LDBLE)0.9297);
  4609. else
  4610. c1 = (LDBLE)2727.586 + (LDBLE)0.6224107 * md->tk_x - (LDBLE)466.9151 * log (md->tk_x) - (LDBLE)52000.87 / md->tk_x;
  4611. c1 = sqrt (c1 * md->tk_x);
  4612. a = (LDBLE)1824827.7 * s3 / (c1 * c1 * c1);
  4613. b = (LDBLE)50.2905 * s3 / c1;
  4614. // constants for equations
  4615. muhalf = sqrt(mu);
  4616. c1 = (-a) * md->LOG_10 * ((LDBLE)1.0 / ((LDBLE)2 * muhalf * (muhalf + (LDBLE)1.0) * (muhalf + (LDBLE)1.0)) - (LDBLE)0.3);
  4617. c2 = -a / (2 * muhalf);
  4618. #ifdef DEBUG_MOHID
  4619. if (d.debug_status)
  4620. fprintf(d.gammas_f, "1) %20.20e %20.20e %20.20e %20.20e %20.20e %20.20e %20.20e %20.20e %20.20e \n",
  4621. s1, s2, s3, c1, c2, a, b, mu, muhalf);
  4622. #endif
  4623. // Calculate activity coefficients
  4624. Species *sx;
  4625. for (i = 0; i < md->s_x->Count(); i++)
  4626. {
  4627. sx = (*md->s_x)[i];
  4628. #ifdef DEBUG_MOHID
  4629. if (d.debug_status)
  4630. fprintf(d.gammas_f, "2) number:%i gflag:%d %20.20e %20.20e %20.20e %20.20e %20.20e %20.20e\n",
  4631. sx->number, sx->gflag,
  4632. sx->dhb,
  4633. sx->moles,
  4634. sx->z,
  4635. sx->dha,
  4636. gd->s_h2o->la,
  4637. md->gfw_water);
  4638. #endif
  4639. switch (sx->gflag)
  4640. {
  4641. case 0: // uncharged
  4642. sx->lg = sx->dhb * mu;
  4643. sx->dg = sx->dhb * md->LOG_10 * sx->moles;
  4644. break;
  4645. case 1: // Davies
  4646. sx->lg = -sx->z * sx->z * a * (muhalf / ((LDBLE)1.0 + muhalf) - (LDBLE)0.3 * mu);
  4647. sx->dg = c1 * sx->z * sx->z * sx->moles;
  4648. break;
  4649. case 2: // Extended D-H, WATEQ D-H
  4650. sx->lg = -a * muhalf * sx->z * sx->z / ((LDBLE)1.0 + sx->dha * b * muhalf) + sx->dhb * mu;
  4651. sx->dg = (c2 * sx->z * sx->z / (((LDBLE)1.0 + sx->dha * b * muhalf) * ((LDBLE)1.0 + sx->dha * b * muhalf)) + sx->dhb) * md->LOG_10 * sx->moles;
  4652. break;
  4653. case 3: // Always 1.0
  4654. sx->lg = 0.0;
  4655. sx->dg = 0.0;
  4656. break;
  4657. case 4: // Exchange
  4658. // Find CEC
  4659. // z contains valence of cation for exchange species, alk contains cec
  4660. for (j = 1; j < sx->rxn_x->token_list->Count(); j++)
  4661. {
  4662. s = sx->rxn_x->token_list->Element(j)->s;
  4663. if (s->type == EX)
  4664. {
  4665. sx->alk = s->primary->u->moles;
  4666. #ifdef DEBUG_MOHID
  4667. if (d.debug_status)
  4668. fprintf(d.gammas_f, "3) j:%d s_number:%d type:%d primary_number:%d u_number:%d %20.20e %20.20e\n",
  4669. i,
  4670. (*sx->rxn_x->token_list)[j]->s->number,
  4671. (*sx->rxn_x->token_list)[j]->s->type,
  4672. (*sx->rxn_x->token_list)[j]->s->primary->number,
  4673. (*sx->rxn_x->token_list)[j]->s->primary->u->number,
  4674. (*sx->rxn_x->token_list)[j]->s->primary->u->moles,
  4675. sx->alk);
  4676. #endif
  4677. break;
  4678. }
  4679. }
  4680. #ifdef DEBUG_MOHID
  4681. if (d.debug_status)
  4682. fprintf(d.gammas_f, "4) exch_flag:%d %20.20e\n", sx->exch_gflag, sx->equiv);
  4683. #endif
  4684. if (sx->exch_gflag == 1 && sx->alk > 0) // Davies
  4685. {
  4686. sx->lg = -sx->equiv * sx->equiv * a * (muhalf / ((LDBLE)1.0 + muhalf) - (LDBLE)0.3 * mu) + log10 (fabs (sx->equiv) / sx->alk);
  4687. sx->dg = c1 * sx->equiv * sx->equiv * sx->moles;
  4688. }
  4689. else if (sx->exch_gflag == 2 && sx->alk > 0) // Extended D-H, WATEQ D-H
  4690. {
  4691. sx->lg = -a * muhalf * sx->equiv * sx->equiv / ((LDBLE)1.0 + sx->dha * b * muhalf) + sx->dhb * mu + log10 (fabs (sx->equiv) / sx->alk);
  4692. sx->dg = (c2 * sx->equiv * sx->equiv / (((LDBLE)1.0 + sx->dha * b * muhalf) * ((LDBLE)1.0 + sx->dha * b * muhalf)) + sx->dhb) * md->LOG_10 * sx->moles;
  4693. }
  4694. else if (sx->exch_gflag == 7 && sx->alk > 0)
  4695. return false;
  4696. else
  4697. {
  4698. // Master species is a dummy variable with meaningless activity and mass
  4699. if (sx->primary != NULL)
  4700. {
  4701. sx->lg = 0.0;
  4702. sx->dg = 0.0;
  4703. }
  4704. else
  4705. {
  4706. if (sx->alk <= 0)
  4707. sx->lg = 0.0;
  4708. else
  4709. sx->lg = log10 (fabs (sx->equiv) / sx->alk);
  4710. sx->dg = 0.0;
  4711. }
  4712. }
  4713. break;
  4714. case 5: // Always 1.0
  4715. sx->lg = 0.0;
  4716. sx->dg = 0.0;
  4717. break;
  4718. case 6: // Surface
  4719. // Find moles of sites.
  4720. // s_x[i]->equiv is stoichiometric coefficient of sites in species
  4721. for (j = 1; j < sx->rxn_x->token_list->Count(); j++)
  4722. {
  4723. s = sx->rxn_x->token_list->Element(j)->s;
  4724. if (s->type == SURF)
  4725. {
  4726. sx->alk = s->primary->u->moles;
  4727. #ifdef DEBUG_MOHID
  4728. if (d.debug_status)
  4729. fprintf(d.gammas_f, "5) j:%d s_number:%d type:%d primary_number:%d u_number:%d %20.20e %20.20e\n",
  4730. i,
  4731. (*sx->rxn_x->token_list)[j]->s->number,
  4732. (*sx->rxn_x->token_list)[j]->s->type,
  4733. (*sx->rxn_x->token_list)[j]->s->primary->number,
  4734. (*sx->rxn_x->token_list)[j]->s->primary->u->number,
  4735. (*sx->rxn_x->token_list)[j]->s->primary->u->moles,
  4736. sx->alk);
  4737. #endif
  4738. break;
  4739. }
  4740. }
  4741. if (md->use.sur_p->type == CD_MUSIC)
  4742. equiv = 1.0; // mole fraction
  4743. else
  4744. equiv = sx->equiv;
  4745. #ifdef DEBUG_MOHID
  4746. if (d.debug_status)
  4747. fprintf(d.gammas_f, "6) %20.20e\n", equiv);
  4748. #endif
  4749. if (sx->alk > 0)
  4750. {
  4751. sx->lg = log10 (equiv / sx->alk);
  4752. sx->dg = 0.0;
  4753. }
  4754. else
  4755. {
  4756. sx->lg = 0.0;
  4757. sx->dg = 0.0;
  4758. }
  4759. break;
  4760. case 7:
  4761. case 8:
  4762. return false;
  4763. case 9: // activity water
  4764. #ifdef DEBUG_MOHID
  4765. if (d.debug_status)
  4766. fprintf(d.gammas_f, "7) %20.20e %20.20e\n", gd->s_h2o->la, md->gfw_water);
  4767. #endif
  4768. sx->lg = log10 (exp (gd->s_h2o->la * md->LOG_10) * md->gfw_water);
  4769. sx->dg = 0.0;
  4770. break;
  4771. }
  4772. #ifdef DEBUG_MOHID
  4773. if (d.debug_status)
  4774. fprintf(d.gammas_f, "8) %20.20e %20.20e\n", sx->lg, sx->dg);
  4775. #endif
  4776. }
  4777. #ifdef DEBUG_MOHID
  4778. if (d.debug_status)
  4779. fprintf(d.gammas_f, "9) FIM------------\n");
  4780. #endif
  4781. return true;
  4782. }
  4783. bool ModelEngine::Molalities(bool allow_overflow)
  4784. {
  4785. //
  4786. // Calculates la for master species
  4787. // Calculates lm and moles from lk, lg, and la's of master species
  4788. // Adjusts lm of h2 and o2.
  4789. //
  4790. int i, j, k, count_g;
  4791. LDBLE total_g;
  4792. ReactionToken *rxn_token;
  4793. // la for master species
  4794. Master *m;
  4795. for (i = 0; i < gd->master_list.Count(); i++)
  4796. {
  4797. m = gd->master_list[i];
  4798. if (m->rewrite)
  4799. {
  4800. m->s->la = m->s->lm + m->s->lg;
  4801. #ifdef DEBUG_MOHID
  4802. if(d.debug_status)
  4803. fprintf(d.molalities_f, "1) m:%d s:%d %20.20e %20.20e %20.20e\n",
  4804. m->number,
  4805. m->s->number,
  4806. m->s->lg,
  4807. m->s->lm,
  4808. m->s->la);
  4809. #endif
  4810. }
  4811. }
  4812. if (md->dl_type_x != NO_DL)
  4813. {
  4814. gd->s_h2o->tot_g_moles = gd->s_h2o->moles;
  4815. gd->s_h2o->tot_dh2o_moles = 0.0;
  4816. }
  4817. #ifdef DEBUG_MOHID
  4818. if(d.debug_status)
  4819. fprintf(d.molalities_f, "2) %20.20e %20.20e %20.20e\n",
  4820. gd->s_h2o->tot_g_moles,
  4821. gd->s_h2o->tot_dh2o_moles);
  4822. #endif
  4823. Species *sx;
  4824. for (i = 0; i < md->s_x->Count(); i++)
  4825. {
  4826. sx = (*md->s_x)[i];
  4827. if (sx->type > HPLUS && sx->type != EX && sx->type != SURF)
  4828. continue;
  4829. // lm and moles for all aqueous species
  4830. sx->lm = sx->lk - sx->lg;
  4831. #ifdef DEBUG_MOHID
  4832. if(d.debug_status)
  4833. fprintf(d.molalities_f, "3) number:%d type:%d %20.20e %20.20e %20.20e\n",
  4834. sx->number,
  4835. sx->type,
  4836. sx->lg,
  4837. sx->lk,
  4838. sx->lm);
  4839. #endif
  4840. for (k = 1; k < sx->rxn_x->token_list->Count(); k++)
  4841. {
  4842. rxn_token = sx->rxn_x->token_list->Element(k);
  4843. sx->lm += rxn_token->s->la * rxn_token->coef;
  4844. #ifdef DEBUG_MOHID
  4845. if(d.debug_status)
  4846. fprintf(d.molalities_f, "4) number:%d %20.20e %20.20e\n",
  4847. rxn_token->s->number,
  4848. rxn_token->s->la,
  4849. rxn_token->coef);
  4850. #endif
  4851. }
  4852. if (sx->type == EX)
  4853. sx->moles = exp (sx->lm * md->LOG_10);
  4854. else if (sx->type == SURF)
  4855. sx->moles = exp (sx->lm * md->LOG_10); // formerly * mass water
  4856. else
  4857. {
  4858. sx->moles = Under(sx->lm) * md->mass_water_aq_x;
  4859. if (sx->moles / md->mass_water_aq_x > 30)
  4860. if (md->iterations >= 0 && !allow_overflow)
  4861. return false;
  4862. }
  4863. #ifdef DEBUG_MOHID
  4864. if(d.debug_status)
  4865. fprintf(d.molalities_f, "5) %20.20e %20.20e\n",
  4866. sx->lm,
  4867. md->mass_water_aq_x,
  4868. sx->moles);
  4869. #endif
  4870. }
  4871. // other terms for diffuse layer model
  4872. if (md->use.sur_p != NULL && md->use.sur_p->type == CD_MUSIC && md->dl_type_x != NO_DL)
  4873. CalcAllDonnan();
  4874. for (i = 0; i < md->s_x->Count(); i++)
  4875. {
  4876. sx = (*md->s_x)[i];
  4877. if (sx->type > HPLUS && sx->type != EX && sx->type != SURF)
  4878. continue;
  4879. if (md->use.sur_p != NULL && md->dl_type_x != NO_DL && sx->type <= HPLUS)
  4880. {
  4881. total_g = 0.0;
  4882. sx->tot_dh2o_moles = 0.0;
  4883. SpeciesDiffLayer *sdl;
  4884. SurfaceDiffLayer *surdl_p;
  4885. SurfaceCharge *sc_p;
  4886. for (j = 0; j < md->use.sur_p->charge->Count(); j++)
  4887. {
  4888. sc_p = (*md->use.sur_p->charge)[j];
  4889. sdl = (*sx->diff_layer)[j];
  4890. count_g = sdl->count_g;
  4891. surdl_p = (*sdl->charge->g)[count_g];
  4892. sdl->g_moles = sx->moles * (surdl_p->g + sdl->charge->mass_water / md->mass_water_aq_x);
  4893. if (sx->moles > 1e-30)
  4894. sdl->dg_g_moles = sx->dg * sdl->g_moles / sx->moles;
  4895. total_g += surdl_p->g + sdl->charge->mass_water / md->mass_water_aq_x;
  4896. sdl->dx_moles = sx->moles * surdl_p->dg;
  4897. sdl->dh2o_moles = -sx->moles * sdl->charge->mass_water / md->mass_water_aq_x;
  4898. sx->tot_dh2o_moles += sdl->dh2o_moles;
  4899. sdl->drelated_moles = sx->moles * sc_p->specific_area * md->use.sur_p->thickness / md->mass_water_aq_x;
  4900. }
  4901. sx->tot_g_moles = sx->moles * (1 + total_g);
  4902. if (sx->moles > 1e-30)
  4903. sx->dg_total_g = sx->dg * sx->tot_g_moles / sx->moles;
  4904. else
  4905. sx->dg_total_g = 0.0;
  4906. }
  4907. }
  4908. if (md->use.gas_p != NULL)
  4909. CalcGasPressures();
  4910. if (md->use.ssa_p != NULL)
  4911. CalcSSFractions();
  4912. #ifdef DEBUG_MOHID
  4913. if(d.debug_status)
  4914. fprintf(d.molalities_f, "FIM)----------------\n");
  4915. #endif
  4916. return true;
  4917. }
  4918. bool ModelEngine::ReviseGuesses()
  4919. {
  4920. // Revise la's of master species
  4921. int i;
  4922. int iter, max_iter;
  4923. bool repeat, fail;
  4924. LDBLE weight, f;
  4925. String filename;
  4926. Unknown *u;
  4927. Master *m;
  4928. max_iter = 10;
  4929. #ifdef DEBUG_MOHID
  4930. if (d.debug_status)
  4931. fprintf(d.gammas_f, "ReviseGuesses-1) called_by_reviseguesses:%i\n", d.gammas_count++);
  4932. #endif
  4933. Gammas(md->mu_x);
  4934. #ifdef DEBUG_MOHID
  4935. if (d.debug_status)
  4936. fprintf(d.reviseguesses_f, "2) %20.20e\n", md->mu_x);
  4937. #endif
  4938. iter = 0;
  4939. repeat = true;
  4940. fail = false;
  4941. while (repeat)
  4942. {
  4943. iter++;
  4944. if (iter == max_iter + 1)
  4945. fail = true;
  4946. #ifdef DEBUG_MOHID
  4947. if (d.debug_status)
  4948. fprintf(d.reviseguesses_f, "3) %i %i\n", iter, fail);
  4949. #endif
  4950. if (iter > 2 * max_iter)
  4951. return false;
  4952. #ifdef DEBUG_MOHID
  4953. if (d.debug_status)
  4954. fprintf(d.molalities_f, "called_by_revise_guesses) %i\n", d.molalities_count++);
  4955. #endif
  4956. Molalities(true);
  4957. #ifdef DEBUG_MOHID
  4958. if (d.debug_status)
  4959. fprintf(d.mbsums_f, "called_by_revise_guesses) %i\n", d.mbsums_count++);
  4960. #endif
  4961. MBSums();
  4962. if (md->state < REACTION)
  4963. SumSpecies();
  4964. else
  4965. {
  4966. for (i = 0; i < count_unknowns; i++)
  4967. {
  4968. u = (*unknown_list)[i];
  4969. u->sum = u->f;
  4970. #ifdef DEBUG_MOHID
  4971. if (d.debug_status)
  4972. fprintf(d.reviseguesses_f, "4) %i %i %20.20e\n", i, u->number, u->f);
  4973. #endif
  4974. }
  4975. }
  4976. repeat = false;
  4977. for (i = 0; i < count_unknowns; i++)
  4978. {
  4979. u = (*unknown_list)[i];
  4980. #ifdef DEBUG_MOHID
  4981. if (d.debug_status)
  4982. fprintf(d.reviseguesses_f, "5) i:%i number:%i type:%i\n", i, u->number, u->type);
  4983. #endif
  4984. if (u == md->ph_unknown || u == md->pe_unknown)
  4985. continue;
  4986. if (u->type == MB || u->type == CB || u->type == SOLUTION_PHASE_BOUNDARY || u->type == EXCH || u->type == SURFACE)
  4987. {
  4988. if (fabs (u->moles) < 1e-30)
  4989. u->moles = 0;
  4990. f = fabs (u->sum);
  4991. #ifdef DEBUG_MOHID
  4992. if (d.debug_status)
  4993. fprintf(d.reviseguesses_f, "6) %20.20e %20.20e\n", u->moles, f);
  4994. #endif
  4995. if (f == 0 && u->moles == 0)
  4996. {
  4997. m = (*u->master)[0];
  4998. m->s->la = MIN_RELATED_LOG_ACTIVITY;
  4999. #ifdef DEBUG_MOHID
  5000. if (d.debug_status)
  5001. fprintf(d.reviseguesses_f, "7) m:%i s:%i %20.20e\n", m->number, m->s->number, m->s->la);
  5002. #endif
  5003. continue;
  5004. }
  5005. else if (f == 0)
  5006. {
  5007. m = (*u->master)[0];
  5008. repeat = true;
  5009. m->s->la += 5;
  5010. if (m->s->la < -999.0)
  5011. m->s->la = MIN_RELATED_LOG_ACTIVITY;
  5012. #ifdef DEBUG_MOHID
  5013. if (d.debug_status)
  5014. fprintf(d.reviseguesses_f, "8) repeat:%i m:%i s:%i %20.20e\n", repeat, m->number, m->s->number, m->s->la);
  5015. #endif
  5016. }
  5017. else if (fail && f < 1.5 * fabs (u->moles))
  5018. {
  5019. continue;
  5020. }
  5021. else if (f > 1.5 * fabs (u->moles) || f < 1e-5 * fabs (u->moles))
  5022. {
  5023. m = (*u->master)[0];
  5024. weight = (f < (LDBLE)1e-5 * fabs (u->moles)) ? (LDBLE)0.3 : (LDBLE)1.0;
  5025. if (u->moles <= 0)
  5026. m->s->la = MIN_RELATED_LOG_ACTIVITY;
  5027. else
  5028. {
  5029. repeat = true;
  5030. m->s->la += weight * log10 (fabs (u->moles / u->sum));
  5031. }
  5032. #ifdef DEBUG_MOHID
  5033. if (d.debug_status)
  5034. fprintf(d.reviseguesses_f, "9) repeat:%i m:%i s:%i weight:%20.15 %20.20e\n", repeat, m->number, m->s->number, weight, m->s->la);
  5035. #endif
  5036. }
  5037. }
  5038. else if (u->type == ALK)
  5039. {
  5040. f = md->total_co2;
  5041. #ifdef DEBUG_MOHID
  5042. if (d.debug_status)
  5043. fprintf(d.reviseguesses_f, "10) %20.15 %20.20e\n", f, u->moles);
  5044. #endif
  5045. if (fail && f < 1.5 * fabs (u->moles))
  5046. continue;
  5047. if (f > 1.5 * fabs (u->moles) || f < 1e-5 * fabs (u->moles))
  5048. {
  5049. m = (*u->master)[0];
  5050. repeat = true;
  5051. weight = (f < (LDBLE)1e-5 * fabs (u->moles)) ? (LDBLE)0.3 : (LDBLE)1.0;
  5052. m->s->la += weight * log10 (fabs (u->moles / u->sum));
  5053. #ifdef DEBUG_MOHID
  5054. if (d.debug_status)
  5055. fprintf(d.reviseguesses_f, "11) repeat:%i m:%i s:%i weight:%20.15 %20.15 %20.15 %20.20e\n", repeat, m->number, m->s->number, weight, m->s->la, u->moles, u->sum);
  5056. #endif
  5057. }
  5058. }
  5059. }
  5060. }
  5061. md->mu_x = md->mu_unknown->f * (LDBLE)0.5 / md->mass_water_aq_x;
  5062. if (md->mu_x <= 1e-8)
  5063. md->mu_x = (LDBLE)1e-8;
  5064. #ifdef DEBUG_MOHID
  5065. if (d.debug_status)
  5066. fprintf(d.reviseguesses_f, "12) %20.20e %20.20e %20.20e\n", md->mu_x, md->mu_unknown->f, md->mass_water_aq_x);
  5067. #endif
  5068. #ifdef DEBUG_MOHID
  5069. if (d.debug_status)
  5070. fprintf(d.gammas_f, "ReviseGuesses-13) called_by_reviseguesses:%i\n", d.gammas_count++);
  5071. #endif
  5072. Gammas(md->mu_x);
  5073. #ifdef DEBUG_MOHID
  5074. if (d.debug_status)
  5075. fprintf(d.reviseguesses_f, "14) %20.20e\n", md->mu_x);
  5076. #endif
  5077. return true;
  5078. }
  5079. bool ModelEngine::InitialSurfaceWater()
  5080. {
  5081. //
  5082. // In initial surface calculation, need to calculate
  5083. // mass of water in diffuse layer.
  5084. // diffuse layer water + aqueous solution water = bulk water.
  5085. // Ionic strength is fixed, so diffuse-layer water will not change
  5086. //
  5087. int i;
  5088. LDBLE debye_length, b, r, rd, ddl_limit, rd_limit, fraction, sum_surfs, s;
  5089. LDBLE damp_aq;
  5090. Unknown *u;
  5091. // Debye length = 1/k = sqrt[eta*eta_zero*R*T/(2*F**2*mu_x*1000)], Dzombak and Morel, p 36
  5092. // 1000 converts kJ to J; 1000 converts Liters to meter**3; debye_length is in meters.
  5093. debye_length = (EPSILON * EPSILON_ZERO * R_KJ_DEG_MOL * (LDBLE)1000.0 * md->tk_x) / ((LDBLE)2. * F_C_MOL * F_C_MOL * md->mu_x * (LDBLE)1000.);
  5094. debye_length = sqrt (debye_length);
  5095. // ddl is at most the fraction ddl_limit of bulk water
  5096. ddl_limit = md->use.sur_p->DDL_limit;
  5097. // Loop through all surface components, calculate each H2O surface (diffuse layer),
  5098. // H2O aq, and H2O bulk (diffuse layers plus aqueous).
  5099. if (md->use.sur_p->debye_lengths > 0)
  5100. {
  5101. sum_surfs = 0.0;
  5102. for (i = 0; i < count_unknowns; i++)
  5103. {
  5104. u = (*unknown_list)[i];
  5105. if (u->type != SURFACE_CB)
  5106. continue;
  5107. sum_surfs += u->surface_charge->specific_area * u->surface_charge->grams;
  5108. }
  5109. rd = debye_length * md->use.sur_p->debye_lengths;
  5110. md->use.sur_p->thickness = rd;
  5111. if (md->state == INITIAL_SURFACE)
  5112. {
  5113. // distribute water over DDL (rd) and free pore (r - rd)
  5114. // find r: free pore (m3) = pi * (r - rd)^2 * L, where L = A / (2*pi*r), A = sum_surfs = sum of the surface areas
  5115. b = (LDBLE)-2 * (rd + md->use.sol_p->mass_water / ((LDBLE)1000 * sum_surfs));
  5116. r = (LDBLE)0.5 * (-b + sqrt (b * b - (LDBLE)4 * rd * rd));
  5117. rd_limit = (1 - sqrt (1 - ddl_limit)) * r;
  5118. // rd should be smaller than r and the ddl limit
  5119. if (rd > rd_limit)
  5120. {
  5121. md->mass_water_surfaces_x = md->use.sol_p->mass_water * ddl_limit / (1 - ddl_limit);
  5122. r = (LDBLE)0.002 * (md->mass_water_surfaces_x + md->use.sol_p->mass_water) / sum_surfs;
  5123. rd_limit = (1 - sqrt (1 - ddl_limit)) * r;
  5124. md->use.sur_p->thickness = rd = rd_limit;
  5125. }
  5126. else
  5127. md->mass_water_surfaces_x = (r * r / pow (r - rd, 2) - 1) * md->use.sol_p->mass_water;
  5128. for (i = 0; i < count_unknowns; i++)
  5129. {
  5130. u = (*unknown_list)[i];
  5131. if (u->type != SURFACE_CB)
  5132. continue;
  5133. s = u->surface_charge->specific_area * u->surface_charge->grams;
  5134. u->surface_charge->mass_water = md->mass_water_surfaces_x * s / sum_surfs;
  5135. }
  5136. }
  5137. else
  5138. {
  5139. r = (LDBLE)0.002 * md->mass_water_bulk_x / sum_surfs;
  5140. rd_limit = (1 - sqrt (1 - ddl_limit)) * r;
  5141. if (rd > rd_limit)
  5142. {
  5143. md->use.sur_p->thickness = rd = rd_limit;
  5144. fraction = ddl_limit;
  5145. }
  5146. else
  5147. fraction = 1 - pow (r - rd, 2) / (r * r);
  5148. if (md->g_iterations > 10)
  5149. damp_aq = (LDBLE)0.2;
  5150. else if (md->g_iterations > 5)
  5151. damp_aq = (LDBLE)0.5;
  5152. else
  5153. damp_aq = (LDBLE)1.0;
  5154. md->mass_water_surfaces_x = damp_aq * fraction * md->mass_water_bulk_x + (1 - damp_aq) * md->mass_water_surfaces_x;
  5155. for (i = 0; i < count_unknowns; i++)
  5156. {
  5157. u = (*unknown_list)[i];
  5158. if (u->type != SURFACE_CB)
  5159. continue;
  5160. s = u->surface_charge->specific_area * u->surface_charge->grams;
  5161. u->surface_charge->mass_water = md->mass_water_surfaces_x * s / sum_surfs;
  5162. }
  5163. }
  5164. }
  5165. else
  5166. {
  5167. // take constant thickness of, default 1e-8 m (100 Angstroms)
  5168. md->mass_water_surfaces_x = 0.0;
  5169. for (i = 0; i < count_unknowns; i++)
  5170. {
  5171. u = (*unknown_list)[i];
  5172. if (u->type != SURFACE_CB)
  5173. continue;
  5174. u->surface_charge->mass_water = u->surface_charge->specific_area * u->surface_charge->grams * md->use.sur_p->thickness * 1000;
  5175. md->mass_water_surfaces_x += u->surface_charge->mass_water;
  5176. }
  5177. }
  5178. if (md->use.sur_p->type == CD_MUSIC)
  5179. md->mass_water_bulk_x = md->mass_water_aq_x + md->mass_water_surfaces_x;
  5180. else
  5181. {
  5182. // for variable distribution of water over DDL and bulk...
  5183. if (md->state > INITIAL_SURFACE)
  5184. md->mass_water_aq_x = md->mass_water_bulk_x - md->mass_water_surfaces_x;
  5185. else
  5186. md->mass_water_bulk_x = md->mass_water_aq_x + md->mass_water_surfaces_x;
  5187. }
  5188. return true;
  5189. }
  5190. bool ModelEngine::MBSums()
  5191. {
  5192. //
  5193. // Calculates sums of species for calculation of mass balances, charge
  5194. // balance. Also calculates saturation indices for solution_phase_boundaries
  5195. // and pure_phases. After this routine total calcium calculated from all
  5196. // calcium species in solution is stored in x[i]->f. Also calculates
  5197. // x[i]->sum for some types of unknowns. Uses arrays sum_mb1 and
  5198. // sum_mb1, which are generated in prep and reprep.
  5199. //
  5200. int k;
  5201. Unknown *u;
  5202. // Clear functions in unknowns
  5203. for (k = 0; k < count_unknowns; k++)
  5204. {
  5205. u = (*unknown_list)[k];
  5206. u->f = 0.0;
  5207. u->sum = 0.0;
  5208. }
  5209. // Add terms with coefficients of 1.0
  5210. STCoef *sc_p;
  5211. for (k = 0; k < md->sum_mb1->Count(); k++)
  5212. {
  5213. sc_p = (*md->sum_mb1)[k];
  5214. *sc_p->target += *sc_p->source;
  5215. #ifdef DEBUG_MOHID
  5216. if (d.debug_status)
  5217. fprintf(d.mbsums_f, "%20.20e %20.20e\n", *sc_p->target, *sc_p->source);
  5218. #endif
  5219. }
  5220. // Add terms with coefficients != 1.0
  5221. for (k = 0; k < md->sum_mb2->Count(); k++)
  5222. {
  5223. sc_p = (*md->sum_mb2)[k];
  5224. *sc_p->target += (*sc_p->source * sc_p->coef);
  5225. #ifdef DEBUG_MOHID
  5226. if (d.debug_status)
  5227. fprintf(d.mbsums_f, "%20.20e %20.20e %20.20e\n", *sc_p->target, *sc_p->source, sc_p->coef);
  5228. #endif
  5229. }
  5230. #ifdef DEBUG_MOHID
  5231. if (d.debug_status)
  5232. fprintf(d.mbsums_f, "FIM----------\n");
  5233. #endif
  5234. return true;
  5235. }
  5236. bool ModelEngine::SwitchBases()
  5237. {
  5238. //
  5239. // Check if activity of first master species is predominant among activities of
  5240. // secondary master species included in mass balance.
  5241. //
  5242. int i, j;
  5243. int first;
  5244. bool return_value;
  5245. LDBLE la, la1;
  5246. return_value = false;
  5247. Unknown *u;
  5248. Master *m;
  5249. for (i = 0; i < count_unknowns; i++)
  5250. {
  5251. u = (*unknown_list)[i];
  5252. if (u->type != MB)
  5253. continue;
  5254. first = 0;
  5255. la = (*u->master)[0]->s->la;
  5256. for (j = 1; j < u->master->Count() != NULL; j++)
  5257. {
  5258. m = (*u->master)[j];
  5259. la1 = m->s->lm + m->s->lg;
  5260. if (first == 0 && la1 > la + 10.)
  5261. {
  5262. la = la1;
  5263. first = j;
  5264. }
  5265. else if (first != 0 && la1 > la)
  5266. {
  5267. la = la1;
  5268. first = j;
  5269. }
  5270. }
  5271. if (first != 0)
  5272. {
  5273. u->master->Swap(0, first);
  5274. (*u->master)[0]->in = true;
  5275. (*u->master)[0]->rewrite = false;
  5276. (*u->master)[first]->rewrite = true;
  5277. (*u->master)[first]->in = false;
  5278. return_value = true;
  5279. }
  5280. }
  5281. return (return_value);
  5282. }
  5283. bool ModelEngine::Reprep()
  5284. {
  5285. //
  5286. // If a basis species has been switched, makes new model.
  5287. // Unknowns are not changed, but mass-action equations are
  5288. // rewritten and lists for mass balance and jacobian are regenerated
  5289. //
  5290. int i;
  5291. Master *m;
  5292. // Initialize s, master, and unknown pointers
  5293. for (i = 0; i < gd->master_list.Count(); i++)
  5294. {
  5295. m = gd->master_list[i];
  5296. if (!m->in)
  5297. continue;
  5298. m->rxn_primary->CopyTo(m->rxn_secondary);
  5299. }
  5300. if (!ResetupMaster())
  5301. return false;
  5302. // Set unknown pointers, unknown types, validity checks
  5303. if (!TidyRedox())
  5304. return false;
  5305. // Free arrays built in build_model
  5306. md->s_x->Clear();
  5307. md->sum_mb1->Clear();
  5308. md->sum_mb2->Clear();
  5309. md->sum_jacob0->Clear();
  5310. md->sum_jacob1->Clear();
  5311. md->sum_jacob2->Clear();
  5312. md->sum_delta->Clear();
  5313. // Build model again
  5314. if (!BuildModel ())
  5315. return false;
  5316. md->same_model = false;
  5317. KTemp(md->tc_x);
  5318. return true;
  5319. }
  5320. Master *ModelEngine::SurfaceGetPSIMaster(String name, int plane)
  5321. {
  5322. Master *master_ptr;
  5323. String token;
  5324. if (name.IsEmpty())
  5325. return NULL;
  5326. token = name;
  5327. token += "_psi";
  5328. switch (plane)
  5329. {
  5330. case SURF_PSI:
  5331. break;
  5332. case SURF_PSI1:
  5333. token += "b";
  5334. break;
  5335. case SURF_PSI2:
  5336. token += "d";
  5337. break;
  5338. default:
  5339. throw exception();
  5340. }
  5341. master_ptr = gd->master_list.Search(&token);
  5342. return (master_ptr);
  5343. }
  5344. LDBLE ModelEngine::Under(LDBLE xval)
  5345. {
  5346. //
  5347. // Exponentiate a number, x, but censor large and small numbers
  5348. // log values less than MIN_LM are set to 0
  5349. // log values greater than MAX_LM are set to 10**MAX_LM
  5350. //
  5351. if (xval < -40.)
  5352. return (0.0);
  5353. if (xval > 3.)
  5354. return (1.0e3);
  5355. return (exp (xval * md->LOG_10));
  5356. }
  5357. bool ModelEngine::SSPrep(LDBLE t, SS *s_s_ptr)
  5358. {
  5359. /*
  5360. int i, j, k, converged, divisions;
  5361. LDBLE r, rt, ag0, ag1, crit_pt;
  5362. LDBLE xc, tc;
  5363. LDBLE x, x0, x1, xsm1, xsm2, xb1, xb2;
  5364. LDBLE xc1, xc2;
  5365. LDBLE facb1, faca1, spim1, xblm1, acrae, acrael, xliapt, xliapm;
  5366. LDBLE xaly, xaly1, xaly2;
  5367. LDBLE faca, facb, spialy, facal, facbl;
  5368. LDBLE tol;
  5369. if (pr.s_s_assemblage == FALSE)
  5370. print = FALSE;
  5371. tol = 1e-6;
  5372. r = R_KJ_DEG_MOL;
  5373. rt = r * t;
  5374. a0 = s_s_ptr->ag0 / rt;
  5375. a1 = s_s_ptr->ag1 / rt;
  5376. s_s_ptr->a0 = a0;
  5377. s_s_ptr->a1 = a1;
  5378. ag0 = a0 * rt;
  5379. ag1 = a1 * rt;
  5380. kc = exp (k_calc (ss0->phase->rxn->logk, t) * md->LOG_10);
  5381. kb = exp (k_calc (ss1->phase->rxn->logk, t) * md->LOG_10);
  5382. crit_pt = fabs (a0) + fabs (a1);
  5383. s_s_ptr->miscibility = FALSE;
  5384. s_s_ptr->spinodal = FALSE;
  5385. xsm1 = 0.5;
  5386. xsm2 = 0.5;
  5387. xb1 = 0.5;
  5388. xb2 = 0.5;
  5389. xc1 = 0;
  5390. xc2 = 0;
  5391. if (crit_pt >= tol)
  5392. {
  5393. if (fabs (a1) < tol)
  5394. {
  5395. xc = 0.5;
  5396. tc = ag0 / (2 * r);
  5397. }
  5398. else
  5399. {
  5400. xc = 0.5 + (pow ((ag0 * ag0 + 27 * ag1 * ag1), 0.5) - ag0) / (18 * ag1);
  5401. tc = (12 * ag1 * xc - 6 * ag1 + 2 * ag0) * (xc - xc * xc) / r;
  5402. }
  5403. if (print == TRUE)
  5404. {
  5405. sprintf (error_string, "Description of Solid Solution %s",
  5406. s_s_ptr->name);
  5407. dup_print (error_string, TRUE);
  5408. }
  5409. if (print == TRUE)
  5410. {
  5411. output_msg (OUTPUT_MESSAGE,
  5412. "\t Temperature: %g kelvin\n",
  5413. (double) t);
  5414. output_msg (OUTPUT_MESSAGE,
  5415. "\t A0 (dimensionless): %g\n",
  5416. (double) a0);
  5417. output_msg (OUTPUT_MESSAGE,
  5418. "\t A1 (dimensionless): %g\n",
  5419. (double) a1);
  5420. output_msg (OUTPUT_MESSAGE,
  5421. "\t A0 (kJ/mol): %g\n",
  5422. (double) ag0);
  5423. output_msg (OUTPUT_MESSAGE,
  5424. "\t A1 (kJ/mol): %g\n\n",
  5425. (double) ag1);
  5426. }
  5427. if (xc < 0 || xc > 1)
  5428. {
  5429. if (print == TRUE)
  5430. output_msg (OUTPUT_MESSAGE,
  5431. "No miscibility gap above 0 degrees kelvin.\n");
  5432. }
  5433. else
  5434. {
  5435. if (print == TRUE)
  5436. {
  5437. output_msg (OUTPUT_MESSAGE,
  5438. "\t Critical mole-fraction of component 2: %g\n",
  5439. (double) xc);
  5440. output_msg (OUTPUT_MESSAGE,
  5441. "\t Critical temperature: %g kelvin\n",
  5442. (double) tc);
  5443. output_msg (OUTPUT_MESSAGE,
  5444. "\n(The critical temperature calculation assumes that the Guggenheim model\ndefined at %g kelvin md->is valid at the critical temperature.)\n\n\n",
  5445. (double) t);
  5446. }
  5447. }
  5448. if (tc >= t)
  5449. {
  5450. x0 = 0;
  5451. x1 = 1;
  5452. if (scan (f_spinodal, &x0, &x1) == TRUE)
  5453. {
  5454. xsm1 = halve (f_spinodal, x0, x1, tol);
  5455. s_s_ptr->spinodal = TRUE;
  5456. x0 = x1;
  5457. x1 = 1;
  5458. if (scan (f_spinodal, &x0, &x1) == TRUE)
  5459. {
  5460. xsm2 = halve (f_spinodal, x0, x1, tol);
  5461. }
  5462. else
  5463. {
  5464. error_msg ("Failed to find second spinodal point.", STOP);
  5465. }
  5466. }
  5467. }
  5468. }
  5469. if (s_s_ptr->spinodal == TRUE)
  5470. {
  5471. if (print == TRUE)
  5472. output_msg (OUTPUT_MESSAGE,
  5473. "\t Spinodal-gap mole fractions, component 2: %g\t%g\n",
  5474. (double) xsm1, (double) xsm2);
  5475. converged = FALSE;
  5476. if (converged == FALSE)
  5477. {
  5478. for (i = 1; i < 3; i++)
  5479. {
  5480. divisions = (int) pow (10., i);
  5481. for (j = 0; j < divisions; j++)
  5482. {
  5483. for (k = divisions; k > 0; k--)
  5484. {
  5485. xc1 = (LDBLE) j / divisions + 0.001;
  5486. xc2 = (LDBLE) k / divisions;
  5487. converged = solve_misc (&xc1, &xc2, tol);
  5488. if (converged == TRUE)
  5489. break;
  5490. }
  5491. if (converged == TRUE)
  5492. break;
  5493. }
  5494. if (converged == TRUE)
  5495. break;
  5496. }
  5497. }
  5498. if (converged == FALSE)
  5499. {
  5500. error_msg ("Failed to find miscibility gap.", STOP);
  5501. }
  5502. s_s_ptr->miscibility = TRUE;
  5503. if (xc1 < xc2)
  5504. {
  5505. xb1 = 1 - xc2;
  5506. xb2 = 1 - xc1;
  5507. xc1 = 1 - xb1;
  5508. xc2 = 1 - xb2;
  5509. }
  5510. else
  5511. {
  5512. xb1 = 1 - xc1;
  5513. xb2 = 1 - xc2;
  5514. }
  5515. facb1 = kb * xb1 * exp (xc1 * xc1 * (a0 + a1 * (4 * xb1 - 1)));
  5516. faca1 = kc * xc1 * exp (xb1 * xb1 * (a0 - a1 * (3 - 4 * xb1)));
  5517. spim1 = log10 (faca1 + facb1);
  5518. xblm1 = 1. / (1. + faca1 / facb1);
  5519. acrae = facb1 / faca1;
  5520. acrael = log10 (acrae);
  5521. xliapt = log10 (facb1);
  5522. xliapm = log10 (faca1);
  5523. if (print == TRUE)
  5524. {
  5525. output_msg (OUTPUT_MESSAGE,
  5526. "\t Miscibility-gap fractions, component 2: %g\t%g\n",
  5527. (double) xb1, (double) xb2);
  5528. fprintf(f, "\n\t\t\tEutectic Point Calculations\n\n");
  5529. output_msg (OUTPUT_MESSAGE,
  5530. "\t Aqueous activity ratio (comp2/comp1): %g\n",
  5531. (double) acrae);
  5532. output_msg (OUTPUT_MESSAGE,
  5533. "\t Log aqueous activity ratio (comp2/comp1): %g\n",
  5534. (double) acrael);
  5535. output_msg (OUTPUT_MESSAGE,
  5536. "\t Aqueous activity fraction of component 2: %g\n",
  5537. (double) xblm1);
  5538. output_msg (OUTPUT_MESSAGE,
  5539. "\t Log IAP (component 2): %g\n",
  5540. (double) xliapt);
  5541. output_msg (OUTPUT_MESSAGE,
  5542. "\t Log IAP (component 1): %g\n",
  5543. (double) xliapm);
  5544. output_msg (OUTPUT_MESSAGE,
  5545. "\t Log Sum Pi: %g\n",
  5546. (double) spim1);
  5547. }
  5548. s_s_ptr->tk = t;
  5549. s_s_ptr->xb1 = xb1;
  5550. s_s_ptr->xb2 = xb2;
  5551. }
  5552. xaly = -1.0;
  5553. x = a0 * a0 + 3 * a1 * a1 + 6 * a1 * log (kb / kc);
  5554. if (x > 0)
  5555. {
  5556. if (fabs (x - a0 * a0) >= tol)
  5557. {
  5558. xaly1 = (-(a0 - 3 * a1) + pow (x, 0.5)) / (6 * a1);
  5559. xaly2 = (-(a0 - 3 * a1) - pow (x, 0.5)) / (6 * a1);
  5560. if (xaly1 >= 0 && xaly1 <= 1)
  5561. {
  5562. xaly = xaly1;
  5563. }
  5564. if (xaly2 >= 0 && xaly2 <= 1)
  5565. {
  5566. xaly = xaly2;
  5567. }
  5568. }
  5569. else
  5570. {
  5571. xaly = 0.5 + log (kb / kc) / (2 * a0);
  5572. }
  5573. if (xaly > 0 && xaly < 1)
  5574. {
  5575. faca = kc * (1 - xaly) * exp (xaly * xaly * (a0 - a1 * (3 - 4 * xaly)));
  5576. facb =
  5577. kb * xaly * exp ((1 - xaly) * (1 - xaly) *
  5578. (a0 + a1 * (4 * xaly - 1.0)));
  5579. spialy = log10 (faca + facb);
  5580. facal = log10 (faca);
  5581. facbl = log10 (facb);
  5582. if (xaly > xb1 && xaly < xb2)
  5583. {
  5584. if (print == TRUE)
  5585. output_msg (OUTPUT_MESSAGE,
  5586. "\nLocal minimum in the solidus curve coresponding to a maximum\nin the minimum stoichiometric saturation curve.\n\n");
  5587. }
  5588. else
  5589. {
  5590. if (print == TRUE)
  5591. fprintf(f, "\n\t\t\tAlyotropic Point\n\n");
  5592. }
  5593. if (print == TRUE)
  5594. {
  5595. output_msg (OUTPUT_MESSAGE,
  5596. "\t Solid mole fraction of component 2: %g\n",
  5597. (double) xaly);
  5598. output_msg (OUTPUT_MESSAGE,
  5599. "\t Log IAP (component 2): %g\n",
  5600. (double) facbl);
  5601. output_msg (OUTPUT_MESSAGE,
  5602. "\t Log IAP (component 1): %g\n",
  5603. (double) facal);
  5604. output_msg (OUTPUT_MESSAGE,
  5605. "\t Log Sum Pi: %g\n",
  5606. (double) spialy);
  5607. }
  5608. }
  5609. }
  5610. */
  5611. return true;
  5612. }
  5613. bool ModelEngine::InitialGuesses()
  5614. {
  5615. //
  5616. // Make initial guesses for activities of master species and ionic strength
  5617. //
  5618. int i;
  5619. Solution *sol_p = md->use.sol_p;
  5620. md->mu_x = gd->s_hplus->moles + exp((sol_p->ph - (LDBLE)14.) * md->LOG_10) * md->mass_water_aq_x;
  5621. md->mu_x /= md->mass_water_aq_x;
  5622. //d.PrintSpeciesInfoToFile("Initialguesses-1", md->species_info_list, md, gd);
  5623. gd->s_h2o->la = 0.0;
  5624. Unknown *u;
  5625. Master *m;
  5626. for (i = 0; i < count_unknowns; i++)
  5627. {
  5628. u = (*unknown_list)[i];
  5629. if (u == md->ph_unknown || u == md->pe_unknown)
  5630. continue;
  5631. if (u->type < CB)
  5632. {
  5633. m = (*u->master)[0];
  5634. md->mu_x += u->moles / md->mass_water_aq_x * (LDBLE)0.5 * m->s->z * m->s->z;
  5635. //d.PrintSpeciesInfoToFile("InitialGuesses-2", md->species_info_list, md, gd);
  5636. m->s->la = log10 (u->moles / md->mass_water_aq_x);
  5637. }
  5638. else if (u->type == CB || u->type == SOLUTION_PHASE_BOUNDARY)
  5639. {
  5640. m = (*u->master)[0];
  5641. m->s->la = log10 ((LDBLE)0.001 * u->moles / md->mass_water_aq_x);
  5642. }
  5643. else if (u->type == EXCH)
  5644. {
  5645. m = (*u->master)[0];
  5646. if (u->moles <= 0)
  5647. m->s->la = MIN_RELATED_LOG_ACTIVITY;
  5648. else
  5649. m->s->la = log10 (u->moles);
  5650. }
  5651. else if (u->type == SURFACE)
  5652. {
  5653. m = (*u->master)[0];
  5654. if (u->moles <= 0)
  5655. m->s->la = MIN_RELATED_LOG_ACTIVITY;
  5656. else
  5657. m->s->la = log10 ((LDBLE)0.1 * u->moles);
  5658. }
  5659. else if (u->type == SURFACE_CB)
  5660. {
  5661. m = (*u->master)[0];
  5662. m->s->la = 0.0;
  5663. }
  5664. }
  5665. return true;
  5666. }
  5667. bool ModelEngine::CalculateValues()
  5668. {
  5669. /*
  5670. int j;
  5671. struct calculate_value *calculate_value_ptr;
  5672. struct isotope_ratio *isotope_ratio_ptr;
  5673. struct isotope_alpha *isotope_alpha_ptr;
  5674. struct master_isotope *master_isotope_ptr;
  5675. char command[] = "run";
  5676. for (j = 0; j < count_calculate_value; j++)
  5677. {
  5678. calculate_value[j]->calculated = FALSE;
  5679. calculate_value[j]->value = MISSING;
  5680. }
  5681. for (j = 0; j < count_calculate_value; j++)
  5682. {
  5683. calculate_value_ptr = calculate_value[j];
  5684. rate_moles = NAN;
  5685. if (calculate_value_ptr->new_def == TRUE)
  5686. {
  5687. if (basic_compile
  5688. (calculate_value[j]->commands, &calculate_value[j]->linebase,
  5689. &calculate_value[j]->varbase, &calculate_value[j]->loopbase) != 0)
  5690. {
  5691. sprintf (error_string, "Fatal Basic error in CALCULATE_VALUES %s.",
  5692. calculate_value[j]->name);
  5693. error_msg (error_string, STOP);
  5694. }
  5695. calculate_value_ptr->new_def = FALSE;
  5696. }
  5697. if (basic_run
  5698. (command, calculate_value[j]->linebase, calculate_value[j]->varbase,
  5699. calculate_value[j]->loopbase) != 0)
  5700. {
  5701. sprintf (error_string, "Fatal Basic error in calculate_value %s.",
  5702. calculate_value[j]->name);
  5703. error_msg (error_string, STOP);
  5704. }
  5705. if (rate_moles == NAN)
  5706. {
  5707. sprintf (error_string, "Calculated value not SAVE'd for %s.",
  5708. calculate_value[j]->name);
  5709. error_msg (error_string, STOP);
  5710. }
  5711. else
  5712. {
  5713. calculate_value[j]->calculated = TRUE;
  5714. calculate_value[j]->value = rate_moles;
  5715. }
  5716. }
  5717. for (j = 0; j < count_isotope_ratio; j++)
  5718. {
  5719. isotope_ratio_ptr = isotope_ratio[j];
  5720. master_isotope_ptr =
  5721. master_isotope_search (isotope_ratio_ptr->isotope_name);
  5722. calculate_value_ptr = calculate_value_search (isotope_ratio_ptr->name);
  5723. if (calculate_value_ptr->value == MISSING)
  5724. {
  5725. isotope_ratio_ptr->ratio = MISSING;
  5726. isotope_ratio_ptr->converted_ratio = MISSING;
  5727. }
  5728. else
  5729. {
  5730. isotope_ratio_ptr->ratio = calculate_value_ptr->value;
  5731. isotope_ratio_ptr->converted_ratio =
  5732. convert_isotope (master_isotope_ptr, calculate_value_ptr->value);
  5733. }
  5734. }
  5735. for (j = 0; j < count_isotope_alpha; j++)
  5736. {
  5737. isotope_alpha_ptr = isotope_alpha[j];
  5738. calculate_value_ptr = calculate_value_search (isotope_alpha_ptr->name);
  5739. if (calculate_value_ptr->value == MISSING)
  5740. {
  5741. isotope_alpha_ptr->value = MISSING;
  5742. }
  5743. else
  5744. {
  5745. isotope_alpha_ptr->value = calculate_value_ptr->value;
  5746. }
  5747. }
  5748. */
  5749. return true;
  5750. }
  5751. bool ModelEngine::IneqInit(int max_row_count, int max_column_count)
  5752. {
  5753. if (normal_max() <= 0)
  5754. NormalNewMax(count_unknowns);
  5755. if (ineq_array_max() <= 0)
  5756. IneqArrayNewMax(max_row_count * max_column_count);
  5757. if (back_eq_max() <= 0)
  5758. BackEqNewMax(max_row_count);
  5759. if (zero_max() <= 0)
  5760. ZeroNewMax(max_row_count);
  5761. if (res_max() <= 0)
  5762. ResNewMax(max_row_count);
  5763. if (delta1_max() <= 0)
  5764. Delta1NewMax(max_column_count);
  5765. if (cu_max() <= 0)
  5766. CuNewMax(3 * max_row_count);
  5767. if (iu_max() <= 0)
  5768. IuNewMax(3 * max_row_count);
  5769. if (is_max() <= 0)
  5770. IsNewMax(3 * max_row_count);
  5771. return true;
  5772. }
  5773. bool ModelEngine::CL1(int k, int l, int m, int n,
  5774. int nklmd, int n2d,
  5775. LDBLE * q,
  5776. int *kode, LDBLE toler,
  5777. int *iter, LDBLE * x, LDBLE * res, LDBLE * error,
  5778. LDBLE *cu, int *iu, int *s, int check)
  5779. {
  5780. union double_or_int
  5781. {
  5782. int ival;
  5783. LDBLE dval;
  5784. } *q2;
  5785. static int nklm;
  5786. static LDBLE xmin, xmax;
  5787. static int iout, i, j;
  5788. static LDBLE z;
  5789. static int maxit, n1, n2;
  5790. static LDBLE pivot;
  5791. static int ia, ii, kk, in, nk, js;
  5792. static LDBLE sn;
  5793. static int iphase, kforce;
  5794. static LDBLE zu, zv;
  5795. static LDBLE tpivot;
  5796. static int klm, jmn, nkl, jpn;
  5797. static LDBLE cuv, sum;
  5798. static int klm1;
  5799. int q_dim, cu_dim;
  5800. int kode_arg;
  5801. LDBLE check_toler;
  5802. zv = 0;
  5803. kode_arg = *kode;
  5804. CL1Space(check, n2d, k+l+m, nklmd);
  5805. q_dim = n2d;
  5806. q2 = (union double_or_int *) q;
  5807. cu_dim = nklmd;
  5808. maxit = *iter;
  5809. n1 = n + 1;
  5810. n2 = n + 2;
  5811. nk = n + k;
  5812. nkl = nk + l;
  5813. klm = k + l + m;
  5814. klm1 = klm + 1;
  5815. nklm = n + klm;
  5816. kforce = 1;
  5817. *iter = 0;
  5818. js = 0;
  5819. ia = -1;
  5820. #ifdef DEBUG_MOHID
  5821. /*
  5822. if (d.debug_status)
  5823. fprintf(cl1_f, "CL1-1) %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
  5824. kode_arg,
  5825. q_dim,
  5826. cu_dim,
  5827. max_it,
  5828. n1,
  5829. n2,
  5830. nk,
  5831. nlk,
  5832. klm,
  5833. klm1,
  5834. nklm,
  5835. kforce,
  5836. *iter,
  5837. js,
  5838. ia);
  5839. */
  5840. #endif
  5841. for (j = 0; j < n; ++j)
  5842. {
  5843. q2[klm1 * q_dim + j].ival = j + 1;
  5844. #ifdef DEBUG_MOHID
  5845. /*
  5846. if (d.debug_status)
  5847. fprintf(cl1_f, "CL1-2) %d %d",
  5848. klm1 * q_dim + j,
  5849. q2[klm1 * q_dim + j].ival);
  5850. */
  5851. #endif
  5852. }
  5853. for (i = 0; i < klm; ++i)
  5854. {
  5855. q2[i * q_dim + n1].ival = n + i + 1;
  5856. if (q2[i * q_dim + n].dval < 0.)
  5857. {
  5858. for (j = 0; j < n1; ++j)
  5859. q2[i * q_dim + j].dval = -q2[i * q_dim + j].dval;
  5860. q2[i * q_dim + n1].ival = -q2[i * q_dim + n1].ival;
  5861. }
  5862. }
  5863. iphase = 2;
  5864. memcpy ((void *) &cu[0], (void *) &scratch_e[0], (size_t) nklm * sizeof (LDBLE));
  5865. for (j = 0; j < nklm; ++j)
  5866. iu[j] = 0;
  5867. if (l != 0)
  5868. {
  5869. for (j = nk; j < nkl; ++j)
  5870. {
  5871. cu[j] = 1.;
  5872. iu[j] = 1;
  5873. }
  5874. iphase = 1;
  5875. }
  5876. memcpy ((void *) &cu[cu_dim], (void *) &cu[0], (size_t) nklm * sizeof (LDBLE));
  5877. memcpy ((void *) &iu[cu_dim], (void *) &iu[0], (size_t) nklm * sizeof (int));
  5878. if (m != 0)
  5879. {
  5880. for (j = nkl; j < nklm; ++j)
  5881. {
  5882. cu[cu_dim + j] = 1.;
  5883. iu[cu_dim + j] = 1;
  5884. jmn = j - n;
  5885. if (q2[jmn * q_dim + n1].ival < 0)
  5886. iphase = 1;
  5887. }
  5888. }
  5889. if (*kode != 0)
  5890. {
  5891. for (j = 0; j < n; ++j)
  5892. {
  5893. if (x[j] < 0.)
  5894. {
  5895. cu[j] = 1.;
  5896. iu[j] = 1;
  5897. }
  5898. else if (x[j] > 0.)
  5899. {
  5900. cu[cu_dim + j] = 1.;
  5901. iu[cu_dim + j] = 1;
  5902. }
  5903. }
  5904. for (j = 0; j < k; ++j)
  5905. {
  5906. jpn = j + n;
  5907. if (res[j] < 0.)
  5908. {
  5909. cu[jpn] = 1.;
  5910. iu[jpn] = 1;
  5911. if (q2[j * q_dim + n1].ival > 0)
  5912. iphase = 1;
  5913. }
  5914. else if (res[j] > 0.)
  5915. {
  5916. cu[cu_dim + jpn] = 1.;
  5917. iu[cu_dim + jpn] = 1;
  5918. if (q2[j * q_dim + n1].ival < 0)
  5919. iphase = 1;
  5920. }
  5921. }
  5922. }
  5923. if (iphase == 2)
  5924. {
  5925. goto L500;
  5926. }
  5927. L160:
  5928. for (j = js; j < n1; ++j)
  5929. {
  5930. sum = 0.;
  5931. for (i = 0; i < klm; ++i)
  5932. {
  5933. ii = q2[i * q_dim + n1].ival;
  5934. if (ii < 0)
  5935. z = cu[cu_dim - ii - 1];
  5936. else
  5937. z = cu[ii - 1];
  5938. sum += q2[i * q_dim + j].dval * z;
  5939. }
  5940. q2[klm * q_dim + j].dval = sum;
  5941. }
  5942. for (j = js; j < n; ++j)
  5943. {
  5944. ii = q2[klm1 * q_dim + j].ival;
  5945. if (ii < 0)
  5946. z = cu[cu_dim - ii - 1];
  5947. else
  5948. z = cu[ii - 1];
  5949. q2[klm * q_dim + j].dval -= z;
  5950. }
  5951. L240:
  5952. xmax = 0.;
  5953. if (js >= n)
  5954. goto L490;
  5955. for (j = js; j < n; ++j)
  5956. {
  5957. zu = q2[klm * q_dim + j].dval;
  5958. ii = q2[klm1 * q_dim + j].ival;
  5959. if (ii > 0)
  5960. zv = -zu - cu[ii - 1] - cu[cu_dim + ii - 1];
  5961. else
  5962. {
  5963. ii = -ii;
  5964. zv = zu;
  5965. zu = -zu - cu[ii - 1] - cu[cu_dim + ii - 1];
  5966. }
  5967. if (kforce == 1 && ii > n)
  5968. continue;
  5969. if (iu[ii - 1] != 1 && zu > xmax)
  5970. {
  5971. xmax = zu;
  5972. in = j;
  5973. }
  5974. if (iu[cu_dim + ii - 1] != 1 && zv > xmax)
  5975. {
  5976. xmax = zv;
  5977. in = j;
  5978. }
  5979. }
  5980. if (xmax <= toler)
  5981. goto L490;
  5982. if (q2[klm * q_dim + in].dval != xmax)
  5983. {
  5984. for (i = 0; i < klm1; ++i)
  5985. q2[i * q_dim + in].dval = -q2[i * q_dim + in].dval;
  5986. q2[klm1 * q_dim + in].ival = -q2[klm1 * q_dim + in].ival;
  5987. q2[klm * q_dim + in].dval = xmax;
  5988. }
  5989. if (iphase != 1 && ia != -1)
  5990. {
  5991. xmax = 0.;
  5992. for (i = 0; i <= ia; ++i)
  5993. {
  5994. z = fabs (q2[i * q_dim + in].dval);
  5995. if (z > xmax)
  5996. {
  5997. xmax = z;
  5998. iout = i;
  5999. }
  6000. }
  6001. if (xmax > toler)
  6002. {
  6003. memcpy ((void *) &scratch_e[0], (void *) &(q2[ia * q_dim]), (size_t) n2 * sizeof (LDBLE));
  6004. memcpy ((void *) &(q2[ia * q_dim]), (void *) &(q2[iout * q_dim]), (size_t) n2 * sizeof (LDBLE));
  6005. memcpy ((void *) &(q2[iout * q_dim]), (void *) &scratch_e[0], (size_t) n2 * sizeof (LDBLE));
  6006. iout = ia;
  6007. --ia;
  6008. pivot = q2[iout * q_dim + in].dval;
  6009. goto L420;
  6010. }
  6011. }
  6012. kk = -1;
  6013. for (i = 0; i < klm; ++i)
  6014. {
  6015. z = q2[i * q_dim + in].dval;
  6016. if (z > toler)
  6017. {
  6018. ++kk;
  6019. res[kk] = q2[i * q_dim + n].dval / z;
  6020. s[kk] = i;
  6021. }
  6022. }
  6023. L350:
  6024. if (kk < 0)
  6025. {
  6026. *kode = 2;
  6027. goto L590;
  6028. }
  6029. xmin = res[0];
  6030. iout = s[0];
  6031. j = 0;
  6032. if (kk != 0)
  6033. {
  6034. for (i = 1; i <= kk; ++i)
  6035. {
  6036. if (res[i] < xmin)
  6037. {
  6038. j = i;
  6039. xmin = res[i];
  6040. iout = s[i];
  6041. }
  6042. }
  6043. res[j] = res[kk];
  6044. s[j] = s[kk];
  6045. }
  6046. --kk;
  6047. pivot = q2[iout * q_dim + in].dval;
  6048. ii = q2[iout * q_dim + n1].ival;
  6049. if (iphase != 1)
  6050. {
  6051. if (ii < 0 && iu[-ii - 1] == 1)
  6052. goto L420;
  6053. else if (iu[cu_dim + ii - 1] == 1)
  6054. goto L420;
  6055. }
  6056. ii = abs (ii);
  6057. cuv = cu[ii - 1] + cu[cu_dim + ii - 1];
  6058. if (q2[klm * q_dim + in].dval - pivot * cuv > toler)
  6059. {
  6060. for (j = js; j < n1; ++j)
  6061. {
  6062. z = q2[iout * q_dim + j].dval;
  6063. q2[klm * q_dim + j].dval -= z * cuv;
  6064. q2[iout * q_dim + j].dval = -z;
  6065. }
  6066. q2[iout * q_dim + n1].ival = -q2[iout * q_dim + n1].ival;
  6067. goto L350;
  6068. }
  6069. L420:
  6070. if (*iter >= maxit)
  6071. {
  6072. *kode = 3;
  6073. goto L590;
  6074. }
  6075. ++(*iter);
  6076. for (j = js; j < n1; ++j)
  6077. if (j != in)
  6078. q2[iout * q_dim + j].dval /= pivot;
  6079. for (j = js; j < n1; ++j)
  6080. {
  6081. if (j != in)
  6082. {
  6083. z = -q2[iout * q_dim + j].dval;
  6084. for (i = 0; i < klm1; ++i)
  6085. if (i != iout)
  6086. q2[i * q_dim + j].dval += z * q2[i * q_dim + in].dval;
  6087. }
  6088. }
  6089. tpivot = -pivot;
  6090. for (i = 0; i < klm1; ++i)
  6091. if (i != iout)
  6092. q2[i * q_dim + in].dval /= tpivot;
  6093. q2[iout * q_dim + in].dval = (LDBLE)1. / pivot;
  6094. ii = q2[iout * q_dim + n1].ival;
  6095. q2[iout * q_dim + n1].ival = q2[klm1 * q_dim + in].ival;
  6096. q2[klm1 * q_dim + in].ival = ii;
  6097. ii = abs (ii);
  6098. if (iu[ii - 1] == 0 || iu[cu_dim + ii - 1] == 0)
  6099. goto L240;
  6100. for (i = 0; i < klm1; ++i)
  6101. {
  6102. z = q2[i * q_dim + in].dval;
  6103. q2[i * q_dim + in].dval = q2[i * q_dim + js].dval;
  6104. q2[i * q_dim + js].dval = z;
  6105. }
  6106. i = q2[klm1 * q_dim + in].ival;
  6107. q2[klm1 * q_dim + in].ival = q2[klm1 * q_dim + js].ival;
  6108. q2[klm1 * q_dim + js].ival = i;
  6109. ++js;
  6110. goto L240;
  6111. L490:
  6112. if (kforce == 0)
  6113. {
  6114. if (iphase == 1)
  6115. {
  6116. if (q2[klm * q_dim + n].dval <= toler)
  6117. goto L500;
  6118. *kode = 1;
  6119. goto L590;
  6120. }
  6121. *kode = 0;
  6122. goto L590;
  6123. }
  6124. if (iphase != 1 || q2[klm * q_dim + n].dval > toler)
  6125. {
  6126. kforce = 0;
  6127. goto L240;
  6128. }
  6129. L500:
  6130. iphase = 2;
  6131. for (j = 0; j < nklm; ++j)
  6132. cu[j] = 0.;
  6133. for (j = n; j < nk; ++j)
  6134. cu[j] = 1.;
  6135. memcpy ((void *) &cu[cu_dim], (void *) &cu[0], (size_t) nklm * sizeof (LDBLE));
  6136. for (i = 0; i < klm; ++i)
  6137. {
  6138. ii = q2[i * q_dim + n1].ival;
  6139. if (ii <= 0)
  6140. {
  6141. if (iu[cu_dim - ii - 1] == 0)
  6142. continue;
  6143. cu[cu_dim - ii - 1] = 0.;
  6144. }
  6145. else
  6146. {
  6147. if (iu[ii - 1] == 0)
  6148. continue;
  6149. cu[ii - 1] = 0.;
  6150. }
  6151. ++ia;
  6152. memcpy ((void *) &scratch_e[0], (void *) &(q2[ia * q_dim]), (size_t) n2 * sizeof (LDBLE));
  6153. memcpy ((void *) &(q2[ia * q_dim]), (void *) &(q2[i * q_dim]), (size_t) n2 * sizeof (LDBLE));
  6154. memcpy ((void *) &(q2[i * q_dim]), (void *) &scratch_e[0], (size_t) n2 * sizeof (LDBLE));
  6155. }
  6156. goto L160;
  6157. L590:
  6158. sum = 0.;
  6159. for (j = 0; j < n; ++j)
  6160. x[j] = 0.;
  6161. for (i = 0; i < klm; ++i)
  6162. res[i] = 0.;
  6163. for (i = 0; i < klm; ++i)
  6164. {
  6165. ii = q2[i * q_dim + n1].ival;
  6166. sn = 1.;
  6167. if (ii < 0)
  6168. {
  6169. ii = -ii;
  6170. sn = -1.;
  6171. }
  6172. if (ii <= n)
  6173. x[ii - 1] = sn * q2[i * q_dim + n].dval;
  6174. else
  6175. {
  6176. res[ii - n - 1] = sn * q2[i * q_dim + n].dval;
  6177. if (ii >= n1 && ii <= nk)
  6178. sum += q2[i * q_dim + n].dval;
  6179. }
  6180. }
  6181. *error = sum;
  6182. if ((check == 1) && (*kode == 0))
  6183. {
  6184. check_toler = (LDBLE)10. * toler;
  6185. if (kode_arg == 1)
  6186. {
  6187. for (i = 0; i < k; i++)
  6188. {
  6189. if (res_arg_e[i] < 0.0 && res[i] > check_toler)
  6190. *kode = 1;
  6191. else if (res_arg_e[i] > 0.0 && res[i] < -check_toler)
  6192. *kode = 1;
  6193. }
  6194. }
  6195. for (i = k; i < k + l; i++)
  6196. if (fabs (res[i]) > check_toler)
  6197. *kode = 1;
  6198. for (i = k + l; i < k + l + m; i++)
  6199. if (res[i] < -check_toler)
  6200. *kode = 1;
  6201. if (kode_arg == 1)
  6202. {
  6203. for (i = 0; i < n; i++)
  6204. {
  6205. if (x_arg_e[i] < 0.0 && x[i] > check_toler)
  6206. *kode = 1;
  6207. else if (x_arg_e[i] > 0.0 && x[i] < -check_toler)
  6208. *kode = 1;
  6209. }
  6210. }
  6211. if (*kode == 1)
  6212. return false;
  6213. }
  6214. //arr.PrintToFile("array_cl1.txt");
  6215. return true;
  6216. }
  6217. bool ModelEngine::CL1Space(int check, int n2d, int klm, int nklmd)
  6218. {
  6219. if (check == 1)
  6220. {
  6221. XArgENewMax(max(n2d, x_arg_e_max()));
  6222. Fill(x_arg_e, 0.0, x_arg_e_max());
  6223. ResArgENewMax(max(klm, res_arg_e_max()));
  6224. Fill(res_arg_e, 0.0, res_arg_e_max());
  6225. }
  6226. ScratchENewMax(max(nklmd, scratch_e_max()));
  6227. Fill(scratch_e, 0.0, scratch_e_max());
  6228. return true;
  6229. }
  6230. bool ModelEngine::CalcAllDonnan()
  6231. {
  6232. int i, j, k;
  6233. int count_g, count_charge;
  6234. bool converge;
  6235. //char name[MAX_LENGTH];
  6236. LDBLE new_g, f_psi, surf_chrg_eq, psi_avg, f_sinh, A_surf, ratio_aq;
  6237. LDBLE new_g2, f_psi2, surf_chrg_eq2, psi_avg2, dif;
  6238. LDBLE cz, cm, cp;
  6239. if (md->use.sur_p == NULL)
  6240. return true;
  6241. if (md->use.sur_p->type == CD_MUSIC)
  6242. return (CalcAllDonnanMusic ());
  6243. f_sinh = sqrt ((LDBLE)8000.0 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * (LDBLE)1000.0) * md->tk_x * md->mu_x);
  6244. cz = cm = 1.0;
  6245. cp = 1.0;
  6246. converge = true;
  6247. count_charge = 0;
  6248. Unknown *x;
  6249. ChargeGroup *cg_p;
  6250. Species *s_x;
  6251. Master *master;
  6252. SurfaceDiffLayer *sdl_p;
  6253. SpeciesDiffLayer *spdl_p;
  6254. for (j = 0; j < count_unknowns; j++)
  6255. {
  6256. x = (*unknown_list)[j];
  6257. master = (*x->master)[0];
  6258. if (x->type != SURFACE_CB)
  6259. continue;
  6260. md->surface_charge_ptr = x->surface_charge;
  6261. count_g = x->surface_charge->g->Count();
  6262. for (i = 0; i < count_g; i++)
  6263. {
  6264. cg_p = gd->charge_group[i];
  6265. cg_p->eq = 0.0;
  6266. }
  6267. for (i = 0; i < md->s_x->Count(); i++)
  6268. {
  6269. s_x = (*md->s_x)[i];
  6270. if (s_x->type > HPLUS)
  6271. continue;
  6272. for (k = 0; k < count_g; k++)
  6273. {
  6274. cg_p = gd->charge_group[k];
  6275. if (Equal(cg_p->z, s_x->z, md->G_TOL))
  6276. {
  6277. cg_p->eq += s_x->z * s_x->moles;
  6278. break;
  6279. }
  6280. }
  6281. }
  6282. A_surf = x->surface_charge->specific_area * x->surface_charge->grams;
  6283. f_psi = master->s->la * md->LOG_10;
  6284. surf_chrg_eq = A_surf * f_sinh * sinh (f_psi) / F_C_MOL;
  6285. dif = (LDBLE)1e-5;
  6286. f_psi2 = f_psi + dif;
  6287. surf_chrg_eq2 = A_surf * f_sinh * sinh (f_psi2) / F_C_MOL;
  6288. psi_avg = CalcPSIAvg(surf_chrg_eq);
  6289. psi_avg2 = CalcPSIAvg(surf_chrg_eq2);
  6290. ratio_aq = md->surface_charge_ptr->mass_water / md->mass_water_aq_x;
  6291. for (k = 0; k < count_g; k++)
  6292. {
  6293. sdl_p = (*x->surface_charge->g)[k];
  6294. cg_p = gd->charge_group[k];
  6295. sdl_p->charge = cg_p->z;
  6296. new_g = ratio_aq * (exp (-cg_p->z * psi_avg) - 1);
  6297. if (md->use.sur_p->only_counter_ions && ((surf_chrg_eq < 0 && cg_p->z < 0) || (surf_chrg_eq > 0 && cg_p->z > 0)))
  6298. new_g = -ratio_aq;
  6299. if (new_g <= -ratio_aq)
  6300. new_g = -ratio_aq + md->G_TOL * (LDBLE)1e-3;
  6301. new_g2 = ratio_aq * (exp (-cg_p->z * psi_avg2) - 1);
  6302. if (md->use.sur_p->only_counter_ions && ((surf_chrg_eq < 0 && cg_p->z < 0) || (surf_chrg_eq > 0 && cg_p->z > 0)))
  6303. new_g2 = -ratio_aq;
  6304. if (new_g2 <= -ratio_aq)
  6305. new_g2 = -ratio_aq + md->G_TOL * (LDBLE)1e-3;
  6306. if (fabs (new_g) >= 1)
  6307. {
  6308. if (fabs ((new_g - sdl_p->g) / new_g) > md->convergence_tolerance)
  6309. converge = false;
  6310. }
  6311. else
  6312. {
  6313. if (fabs (new_g - sdl_p->g) > md->convergence_tolerance)
  6314. converge = false;
  6315. }
  6316. sdl_p->g = new_g;
  6317. if (new_g != 0)
  6318. sdl_p->dg = (new_g2 - new_g) / dif;
  6319. else
  6320. sdl_p->dg = -cg_p->z;
  6321. for (i = 0; i < md->s_x->Count(); i++)
  6322. {
  6323. s_x = (*md->s_x)[i];
  6324. spdl_p = (*s_x->diff_layer)[count_charge];
  6325. if (Equal (cg_p->z, s_x->z, md->G_TOL))
  6326. {
  6327. spdl_p->charge = x->surface_charge;
  6328. spdl_p->count_g = k;
  6329. }
  6330. }
  6331. }
  6332. count_charge++;
  6333. }
  6334. return (converge);
  6335. }
  6336. bool ModelEngine::CalcGasPressures()
  6337. {
  6338. int i, j;
  6339. LDBLE lp;
  6340. ReactionToken *rxn_ptr;
  6341. GasComp *gas_comp_ptr;
  6342. Phase *phase_ptr;
  6343. // moles and partial pressures for gases
  6344. if (md->use.gas_p == NULL)
  6345. return true;
  6346. if (md->use.gas_p->type == VOLUME)
  6347. {
  6348. md->use.gas_p->total_p = 0;
  6349. md->use.gas_p->total_moles = 0;
  6350. }
  6351. for (i = 0; i < md->use.gas_p->comps->Count(); i++)
  6352. {
  6353. gas_comp_ptr = (*md->use.gas_p->comps)[i];
  6354. phase_ptr = (*md->use.gas_p->comps)[i]->phase;
  6355. if (phase_ptr->in)
  6356. {
  6357. lp = -phase_ptr->lk;
  6358. for(j = 1; j < phase_ptr->rxn_x->token_list->Count(); j++)
  6359. {
  6360. rxn_ptr = phase_ptr->rxn_x->token_list->Element(j);
  6361. lp += rxn_ptr->s->la * rxn_ptr->coef;
  6362. }
  6363. gas_comp_ptr->phase->p_soln_x = exp (lp * md->LOG_10);
  6364. if (md->use.gas_p->type == PRESSURE)
  6365. {
  6366. gas_comp_ptr->phase->moles_x = gas_comp_ptr->phase->p_soln_x * md->gas_unknown->moles / md->gas_unknown->gas_phase->total_p;
  6367. gas_comp_ptr->phase->fraction_x = gas_comp_ptr->phase->moles_x / md->gas_unknown->moles;
  6368. }
  6369. else
  6370. {
  6371. gas_comp_ptr->phase->moles_x = gas_comp_ptr->phase->p_soln_x * md->use.gas_p->volume / (R_LITER_ATM * md->tk_x);
  6372. md->use.gas_p->total_p += gas_comp_ptr->phase->p_soln_x;
  6373. md->use.gas_p->total_moles += gas_comp_ptr->phase->moles_x;
  6374. }
  6375. }
  6376. else
  6377. {
  6378. gas_comp_ptr->phase->moles_x = 0;
  6379. gas_comp_ptr->phase->fraction_x = 0;
  6380. }
  6381. }
  6382. return true;
  6383. }
  6384. bool ModelEngine::CalcSSFractions()
  6385. {
  6386. int i, k;
  6387. LDBLE moles, n_tot;
  6388. SS *s_s_ptr;
  6389. SSComp *ssc_p;
  6390. // moles and lambdas for solid solutions
  6391. if (md->s_s_unknown == NULL)
  6392. return true;
  6393. // Calculate mole fractions and log lambda and derivative factors
  6394. if (md->use.ssa_p == NULL)
  6395. return true;
  6396. for (i = 0; i < md->use.ssa_p->ss_list->Count(); i++)
  6397. {
  6398. s_s_ptr = (*md->use.ssa_p->ss_list)[i];
  6399. n_tot = 0;
  6400. for (k = 0; k < s_s_ptr->comps_list->Count(); k++)
  6401. {
  6402. ssc_p = (*s_s_ptr->comps_list)[k];
  6403. moles = ssc_p->moles;
  6404. if (ssc_p->moles < 0)
  6405. {
  6406. moles = (LDBLE)MIN_TOTAL_SS;
  6407. ssc_p->initial_moles = moles;
  6408. }
  6409. n_tot += moles;
  6410. }
  6411. s_s_ptr->total_moles = n_tot;
  6412. for (k = 0; k < s_s_ptr->comps_list->Count(); k++)
  6413. {
  6414. ssc_p = (*s_s_ptr->comps_list)[k];
  6415. moles = ssc_p->moles;
  6416. if (ssc_p->moles < 0)
  6417. moles = (LDBLE)MIN_TOTAL_SS;
  6418. ssc_p->fraction_x = moles / n_tot;
  6419. ssc_p->log10_fraction_x = log10 (moles / n_tot);
  6420. // all mb and jacobian items must be in x or phase to be static between models
  6421. ssc_p->phase->log10_fraction_x = ssc_p->log10_fraction_x;
  6422. }
  6423. if (s_s_ptr->a0 != 0.0 || s_s_ptr->a1 != 0)
  6424. SSBinary (s_s_ptr);
  6425. else
  6426. SSIdeal (s_s_ptr);
  6427. }
  6428. return true;
  6429. }
  6430. bool ModelEngine::ResetupMaster()
  6431. {
  6432. int i, j;
  6433. Master *master_ptr, *master_ptr0;
  6434. for (i = 0; i < count_unknowns; i++)
  6435. {
  6436. if ((*unknown_list)[i]->type != MB)
  6437. continue;
  6438. master_ptr0 = (*(*unknown_list)[i]->master)[0];
  6439. for (j = 0; j < (*unknown_list)[i]->master->Count(); j++)
  6440. {
  6441. master_ptr = (*(*unknown_list)[i]->master)[j];
  6442. if (j == 0 && master_ptr->s->primary == NULL)
  6443. master_ptr->s->rxn_s->CopyTo(master_ptr->rxn_secondary);
  6444. else if (master_ptr0->s->primary == NULL)
  6445. {
  6446. r_temp->Reset();
  6447. RewriteMasterToSecondary(master_ptr, master_ptr0);
  6448. master_ptr->rxn_secondary->Reset();
  6449. r_temp->CopyReactions(master_ptr->rxn_secondary);
  6450. }
  6451. }
  6452. }
  6453. return true;
  6454. }
  6455. bool ModelEngine::SetupMasterReaction(ListOfPointers<Master> *m, Reaction **r)
  6456. {
  6457. int j;
  6458. Master *master_ptr, *master_ptr0;
  6459. master_ptr0 = (*m)[0];
  6460. for (j = 0; j < m->Count(); j++)
  6461. {
  6462. master_ptr = (*m)[j];
  6463. if (master_ptr->s == gd->s_h2o)
  6464. return false;
  6465. if ((master_ptr->in || master_ptr->rewrite) && (master_ptr->s != gd->s_eminus && master_ptr->s != gd->s_hplus))
  6466. return false;
  6467. if (j == 0)
  6468. {
  6469. master_ptr->in = true;
  6470. master_ptr->rewrite = false;
  6471. if (master_ptr->s->primary == NULL)
  6472. master_ptr->s->rxn_s->CopyTo(master_ptr->rxn_secondary);
  6473. }
  6474. else
  6475. {
  6476. master_ptr->rewrite = true;
  6477. if (master_ptr0->s->primary == NULL)
  6478. {
  6479. r_temp->Reset();
  6480. RewriteMasterToSecondary(master_ptr, master_ptr0);
  6481. master_ptr->rxn_secondary->Reset();
  6482. r_temp->CopyReactions(master_ptr->rxn_secondary);
  6483. }
  6484. }
  6485. *master_ptr->pe_rxn = *r;
  6486. }
  6487. return true;
  6488. }
  6489. void ModelEngine::PrintTotals(FILE *f)
  6490. {
  6491. int i;
  6492. bool pure_water;
  6493. LDBLE EC;
  6494. pure_water = true;
  6495. PrintCentered(f, "Solution composition");
  6496. fprintf(f, "\t%-15s%12s%12s\n\n", "Elements", "Molality", "Moles");
  6497. for (i = 0; i < count_unknowns; i++)
  6498. {
  6499. if ((*unknown_list)[i] == md->alkalinity_unknown)
  6500. {
  6501. fprintf(f, "\t%-15s%12.3e%12.3e\n", (*unknown_list)[i]->total->name.CharPtr(), (double) ((*unknown_list)[i]->f / md->mass_water_aq_x), (double) (*unknown_list)[i]->f);
  6502. pure_water = false;
  6503. }
  6504. if ((*unknown_list)[i] == md->ph_unknown)
  6505. continue;
  6506. if ((*unknown_list)[i] == md->pe_unknown)
  6507. continue;
  6508. if ((*unknown_list)[i] == md->charge_balance_unknown)
  6509. {
  6510. fprintf(f, "\t%-15s%12.3e%12.3e", (*unknown_list)[i]->name.CharPtr(), (double) ((*unknown_list)[i]->sum / md->mass_water_aq_x), (double) (*unknown_list)[i]->sum);
  6511. fprintf(f, " Charge balance\n");
  6512. pure_water = false;
  6513. continue;
  6514. }
  6515. if ((*unknown_list)[i]->type == SOLUTION_PHASE_BOUNDARY)
  6516. {
  6517. fprintf(f, "\t%-15s%12.3e%12.3e", (*unknown_list)[i]->name.CharPtr(),
  6518. (double) ((*unknown_list)[i]->sum / md->mass_water_aq_x), (double) (*unknown_list)[i]->sum);
  6519. fprintf(f, " Equilibrium with %s\n",
  6520. (*unknown_list)[i]->p->name);
  6521. pure_water = FALSE;
  6522. continue;
  6523. }
  6524. if ((*unknown_list)[i]->type == MB)
  6525. {
  6526. fprintf(f, "\t%-15s%12.3e%12.3e\n", (*unknown_list)[i]->name.CharPtr(),
  6527. (double) ((*unknown_list)[i]->sum / md->mass_water_aq_x), (double) (*unknown_list)[i]->sum);
  6528. pure_water = FALSE;
  6529. }
  6530. }
  6531. if (pure_water == true)
  6532. {
  6533. fprintf(f, "\t%-15s\n", "Pure water");
  6534. }
  6535. /*
  6536. * Description of solution
  6537. */
  6538. fprintf(f, "\n");
  6539. PrintCentered(f, "Description of solution");
  6540. /*
  6541. * pH
  6542. */
  6543. fprintf(f, "%45s%7.3f ", "pH = ", (double) (-(gd->s_hplus->la)));
  6544. if (md->ph_unknown == NULL)
  6545. {
  6546. fprintf(f, "\n");
  6547. }
  6548. else if (md->ph_unknown == md->charge_balance_unknown)
  6549. {
  6550. fprintf(f, " Charge balance\n");
  6551. }
  6552. else if (md->ph_unknown->type == SOLUTION_PHASE_BOUNDARY)
  6553. {
  6554. fprintf(f, " Equilibrium with %s\n",
  6555. md->ph_unknown->p->name);
  6556. }
  6557. else if (md->ph_unknown->type == ALK)
  6558. {
  6559. fprintf(f, " Adjust alkalinity\n");
  6560. }
  6561. fprintf(f, "%45s%7.3f ", "pe = ", (double) (-(gd->s_eminus->la)));
  6562. if (md->pe_unknown == NULL)
  6563. {
  6564. fprintf(f, "\n");
  6565. }
  6566. else if (md->pe_unknown == md->charge_balance_unknown)
  6567. {
  6568. fprintf(f, " Charge balance\n");
  6569. }
  6570. else if (md->pe_unknown->type == SOLUTION_PHASE_BOUNDARY)
  6571. {
  6572. fprintf(f, " Equilibrium with %s\n",
  6573. md->pe_unknown->p->name);
  6574. }
  6575. else if (md->pe_unknown->type == MH)
  6576. {
  6577. fprintf(f, " Adjusted to redox equilibrium\n");
  6578. }
  6579. EC = CalcSC ();
  6580. if (EC > 0) {
  6581. fprintf(f, "%45s%i\n", "Specific Conductance (uS/cm, 25 oC) = ",
  6582. (int) EC);
  6583. }
  6584. fprintf(f, "%45s%7.3f\n", "Activity of water = ",
  6585. exp (gd->s_h2o->la * md->LOG_10));
  6586. fprintf(f, "%45s%11.3e\n", "Ionic strength = ",
  6587. (double) md->mu_x);
  6588. fprintf(f, "%45s%11.3e\n", "Mass of water (kg) = ",
  6589. (double) md->mass_water_aq_x);
  6590. if (md->alkalinity_unknown == NULL)
  6591. {
  6592. fprintf(f, "%45s%11.3e\n",
  6593. "Total alkalinity (eq/kg) = ",
  6594. (double) (md->total_alkalinity / md->mass_water_aq_x));
  6595. }
  6596. if (md->carbon_unknown == NULL)
  6597. {
  6598. fprintf(f, "%45s%11.3e\n", "Total carbon (mol/kg) = ",
  6599. (double) (md->total_carbon / md->mass_water_aq_x));
  6600. }
  6601. fprintf(f, "%45s%11.3e\n", "Total CO2 (mol/kg) = ",
  6602. (double) (md->total_co2 / md->mass_water_aq_x));
  6603. fprintf(f, "%45s%7.3f\n", "Temperature (deg C) = ",
  6604. (double) md->tc_x);
  6605. fprintf(f, "%45s%11.3e\n", "Electrical balance (eq) = ",
  6606. (double) md->cb_x);
  6607. fprintf(f, "%45s%6.2f\n",
  6608. "Percent error, 100*(Cat-|An|)/(Cat+|An|) = ",
  6609. (double) (100 * md->cb_x / md->total_ions_x));
  6610. fprintf(f, "%45s%3d\n", "Iterations = ", md->iterations);
  6611. fprintf(f, "%45s%e\n", "Total H = ", (double) md->total_h_x);
  6612. fprintf(f, "%45s%e\n", "Total O = ", (double) md->total_o_x);
  6613. fprintf(f, "\n");
  6614. }
  6615. LDBLE ModelEngine::CalcSC()
  6616. {
  6617. int i;
  6618. LDBLE lm, a, z, Dw, SC, ff;
  6619. SpeciesInfo *s, *s_b;
  6620. SC = 0;
  6621. for (i = 0; i < md->species_info_list->Count(); i++)
  6622. {
  6623. s = (*md->species_info_list)[i];
  6624. if (s->s->type == EX)
  6625. continue;
  6626. if (s->s->type == SURF)
  6627. continue;
  6628. if (s->s == gd->s_h2o)
  6629. continue;
  6630. if ((Dw = s->s->dw) == 0)
  6631. continue;
  6632. if ((z = fabs(s->s->z)) == 0)
  6633. continue;
  6634. if (i > 0)
  6635. {
  6636. s_b = (*md->species_info_list)[i - 1];
  6637. if(s->s->name == s_b->s->name)
  6638. continue;
  6639. }
  6640. lm = s->s->lm;
  6641. if (lm > -9)
  6642. {
  6643. ff = (md->mu_x < (LDBLE).36 * z ? (LDBLE)0.6 / sqrt(z) : sqrt(md->mu_x) / z);
  6644. a = Under(lm + ff * s->s->lg);
  6645. SC += a * z * z * Dw;
  6646. }
  6647. }
  6648. SC *= (LDBLE)1e7 * F_C_MOL * F_C_MOL / (R_KJ_DEG_MOL * (LDBLE)298160.0);
  6649. return SC;
  6650. }
  6651. void ModelEngine::PrintCentered(FILE *f, String str)
  6652. {
  6653. int i, l, l1, l2;
  6654. String token;
  6655. l = (int) str.Length();
  6656. l1 = (79 - l) / 2;
  6657. l2 = 79 - l - l1;
  6658. for (i = 0; i < l1; i++)
  6659. token += '-';
  6660. token += str;
  6661. for (i = 0; i < l2; i++)
  6662. token += '-';
  6663. fprintf (f, "%s\n\n", token);
  6664. }
  6665. void ModelEngine::PrintSpecies(FILE *f)
  6666. {
  6667. int i;
  6668. String name, name1;
  6669. Master *master_ptr;
  6670. LDBLE min;
  6671. LDBLE lm;
  6672. min = -1000;
  6673. PrintCentered(f, "Distribution of species");
  6674. fprintf(f, " %-20s%12s%12s%10s%10s%10s\n", " ", " ", " ", "Log ", "Log ", "Log ");
  6675. fprintf(f, " %-20s%12s%12s%10s%10s%10s\n\n", "Species", "Molality", "Activity", "Molality", "Activity", "Gamma");
  6676. gd->s_h2o->lm = gd->s_h2o->la;
  6677. name = gd->s_hplus->secondary->e->name;
  6678. SpeciesInfo *s;
  6679. for (i = 0; i < md->species_info_list->Count(); i++)
  6680. {
  6681. s = (*md->species_info_list)[i];
  6682. if (s->s->type == EX)
  6683. continue;
  6684. if (s->s->type == SURF)
  6685. continue;
  6686. if (s->master_s->secondary != NULL)
  6687. {
  6688. master_ptr = s->master_s->secondary;
  6689. name1 = s->master_s->secondary->e->name;
  6690. }
  6691. else
  6692. {
  6693. master_ptr = s->master_s->primary;
  6694. name1 = s->master_s->primary->e->name;
  6695. }
  6696. if (name1 != name)
  6697. {
  6698. name = name1;
  6699. fprintf(f, "%-14s%12.3e\n", name.CharPtr(), (double) (master_ptr->total / md->mass_water_aq_x));
  6700. min = md->censor * master_ptr->total / md->mass_water_aq_x;
  6701. if (min > 0)
  6702. {
  6703. min = log10 (min);
  6704. }
  6705. else
  6706. {
  6707. min = -1000.;
  6708. }
  6709. }
  6710. if (s->s->lm > min)
  6711. {
  6712. if (s->s == gd->s_h2o)
  6713. {
  6714. lm = log10 (gd->s_h2o->moles / md->mass_water_aq_x);
  6715. }
  6716. else
  6717. {
  6718. lm = s->s->lm;
  6719. }
  6720. fprintf(f, " %-20s%12.3e%12.3e%10.3f%10.3f%10.3f\n", s->s->name.CharPtr(),
  6721. (double) ((s->s->moles) / md->mass_water_aq_x),
  6722. (double) Under(s->s->lm + s->s->lg),
  6723. (double) lm,
  6724. (double) (s->s->lm + s->s->lg),
  6725. (double) s->s->lg);
  6726. }
  6727. }
  6728. fprintf(f, "\n");
  6729. }
  6730. void ModelEngine::PrintResults(String filename)
  6731. {
  6732. FILE *f = fopen(filename.CharPtr(), "wt");
  6733. PrintTotals(f);
  6734. PrintSpecies(f);
  6735. fclose(f);
  6736. }
  6737. bool ModelEngine::Prepare()
  6738. {
  6739. if (md->use.sol_p == NULL)
  6740. return false;
  6741. Solution *sol_p = md->use.sol_p;
  6742. if (!Clear())
  6743. return false;
  6744. SetupUnknowns();
  6745. if (md->state == INITIAL_SOLUTION)
  6746. if (!ConvertUnits(sol_p))
  6747. return false;
  6748. if (!SetupSolution())
  6749. return false;
  6750. if (!SetupExchange())
  6751. return false;
  6752. if (!SetupSurface())
  6753. return false;
  6754. if (!SetupPurePhases())
  6755. return false;
  6756. if (!SetupGasPhases())
  6757. return false;
  6758. if (!SetupSSAssemblage())
  6759. return false;
  6760. if (!SetupRelatedSurface())
  6761. return false;
  6762. if (!TidyRedox())
  6763. return false;
  6764. ArrNewMax((md->max_unknowns + 1) * md->max_unknowns);
  6765. ResidualNewMax(md->max_unknowns);
  6766. DeltaNewMax(md->max_unknowns);
  6767. /*
  6768. arr_max = (md->max_unknowns + 1) * md->max_unknowns;
  6769. if (arr_max > arr_capacity)
  6770. {
  6771. delete [] arr;
  6772. arr = NULL;
  6773. arr_capacity = arr_max;
  6774. arr = new LDBLE [arr_capacity];
  6775. }
  6776. residual_max = md->max_unknowns;
  6777. if (residual_max > residual_capacity)
  6778. {
  6779. delete [] residual;
  6780. residual = NULL;
  6781. residual_capacity = residual_max;
  6782. residual = new LDBLE [residual_capacity];
  6783. }
  6784. delta_max = md->max_unknowns;
  6785. if (delta_max > delta_capacity)
  6786. {
  6787. delete [] delta;
  6788. delta = NULL;
  6789. delta_capacity = delta_max;
  6790. delta = new LDBLE [delta_capacity];
  6791. }
  6792. */
  6793. if (!BuildModel())
  6794. return false;
  6795. return true;
  6796. }
  6797. bool ModelEngine::BuildPurePhases()
  6798. {
  6799. //
  6800. // Includes calculation of inverse saturation index in sum_mb.
  6801. // Puts coefficients in iap and mass balance equations for each phase.
  6802. //
  6803. if (md->pure_phase_unknown == NULL) //ToDo: CHECK THIS!!!!
  6804. return true;
  6805. int i;
  6806. bool stop;
  6807. int j, k, l;
  6808. char token[DEFAULT_STRING_LENGTH];
  6809. char *ptr;
  6810. Master *master_ptr, *m2, *m2_0;
  6811. ReactionToken *rxn_ptr;
  6812. Unknown *x2;
  6813. // Build into sums the logic to calculate inverse saturation indices for pure phases
  6814. // Calculate inverse saturation index
  6815. Unknown *u;
  6816. for (i = 0; i < count_unknowns; i++)
  6817. {
  6818. u = (*unknown_list)[i];
  6819. if (u->type != PP || u->p->rxn_x->token_list->Count() <= 0)
  6820. continue;
  6821. // the code below is "strange", because if md->pure_phase_unknown was NULL, this function do not "execute"
  6822. if (md->pure_phase_unknown == NULL)
  6823. md->pure_phase_unknown = u;
  6824. StoreMB(&u->p->lk, &u->f, 1.0);
  6825. StoreMB(&u->si, &u->f, 1.0);
  6826. for (j = 1; j < u->p->rxn_x->token_list->Count(); j++)
  6827. {
  6828. rxn_ptr = u->p->rxn_x->token_list->Element(j);
  6829. StoreMB(&rxn_ptr->s->la, &u->f, -rxn_ptr->coef);
  6830. }
  6831. }
  6832. for (i = 0; i < count_unknowns; i++)
  6833. {
  6834. u = (*unknown_list)[i];
  6835. // rxn_x is null if an element in phase is not in solution
  6836. if (u->type != PP || u->p->rxn_x == NULL)
  6837. continue;
  6838. // Put coefficients into IAP equations
  6839. for (j = 1; j < u->p->rxn_x->token_list->Count(); j++)
  6840. {
  6841. rxn_ptr = u->p->rxn_x->token_list->Element(j);
  6842. if (rxn_ptr->s->secondary != NULL && rxn_ptr->s->secondary->in)
  6843. master_ptr = rxn_ptr->s->secondary;
  6844. else
  6845. master_ptr = rxn_ptr->s->primary;
  6846. if (master_ptr == NULL || master_ptr->u == NULL)
  6847. continue;
  6848. StoreJacob0(u->number, master_ptr->u->number, rxn_ptr->coef);
  6849. }
  6850. //Put coefficients into mass balance equations
  6851. eos_list->Clear();
  6852. parent_count = 0;
  6853. if (!u->pure_phase->add_formula.IsEmpty())
  6854. u->pure_phase->add_formula.Copy(token);
  6855. else
  6856. u->p->formula.Copy(token);
  6857. ptr = &token[0];
  6858. GetElementsInSpecies(&ptr, 1.0);
  6859. // Go through elements in phase
  6860. ChangeHydrogenInEOSList(0);
  6861. ElementOfSpecies *eos;
  6862. for (j = 0; j < eos_list->Count(); j++)
  6863. {
  6864. eos = (*eos_list)[j];
  6865. if (eos->e->name == "H" && md->mass_hydrogen_unknown != NULL)
  6866. {
  6867. StoreJacob0 (md->mass_hydrogen_unknown->number, u->number, -eos->coef);
  6868. StoreSumDeltas (&delta[i], &md->mass_hydrogen_unknown->delta, eos->coef);
  6869. }
  6870. else if (eos->e->name == "O" && md->mass_oxygen_unknown != NULL)
  6871. {
  6872. StoreJacob0 (md->mass_oxygen_unknown->number, u->number, -eos->coef);
  6873. StoreSumDeltas (&delta[i], &md->mass_oxygen_unknown->delta, eos->coef);
  6874. }
  6875. else
  6876. {
  6877. master_ptr = eos->e->primary;
  6878. if (!master_ptr->in)
  6879. master_ptr = master_ptr->s->secondary;
  6880. if (master_ptr == NULL || !master_ptr->in)
  6881. return false;
  6882. else if (master_ptr->in)
  6883. {
  6884. StoreJacob0 (master_ptr->u->number, u->number, -eos->coef);
  6885. StoreSumDeltas (&delta[i], &master_ptr->u->delta, eos->coef);
  6886. }
  6887. else if (master_ptr->rewrite)
  6888. {
  6889. stop = false;
  6890. for (k = 0; k < count_unknowns; k++)
  6891. {
  6892. x2 = (*unknown_list)[k];
  6893. if (x2->type != MB)
  6894. continue;
  6895. for (l = 0; l < x2->master->Count(); l++)
  6896. {
  6897. m2 = (*x2->master)[l];
  6898. if (m2 == master_ptr)
  6899. {
  6900. m2_0 = (*x2->master)[0];
  6901. StoreJacob0 (m2_0->u->number, u->number, -eos->coef);
  6902. StoreSumDeltas (&delta[i], &m2_0->u->delta, eos->coef);
  6903. stop = true;
  6904. break;
  6905. }
  6906. }
  6907. if (stop)
  6908. break;
  6909. }
  6910. }
  6911. }
  6912. }
  6913. }
  6914. return true;
  6915. }
  6916. bool ModelEngine::SetInitialMoles(int i)
  6917. {
  6918. int j, k;
  6919. PurePhase *pp;
  6920. GasComp *gc_p;
  6921. SS *ss_p;
  6922. SSComp *ssc_p;
  6923. PPAssemblage *ppa;
  6924. GasPhase *gas;
  6925. SSAssemblage *ssa;
  6926. // Pure phase assemblage
  6927. ppa = md->use.ppa_list->Element(i);
  6928. if (ppa != NULL)
  6929. {
  6930. for (j = 0; j < ppa->pure_phases->Count(); j++)
  6931. {
  6932. pp = (*ppa->pure_phases)[j];
  6933. pp->initial_moles = pp->moles;
  6934. if (pp->initial_moles < 0)
  6935. pp->initial_moles = 0;
  6936. }
  6937. }
  6938. // Gas phase
  6939. gas = md->use.gas_list->Element(i);
  6940. if (gas != NULL)
  6941. {
  6942. for (j = 0; j < gas->comps->Count(); j++)
  6943. {
  6944. gc_p = (*gas->comps)[j];
  6945. gc_p->initial_moles = gc_p->moles;
  6946. }
  6947. }
  6948. // Solid solutions
  6949. ssa = md->use.ssa_list->Element(i);
  6950. if (ssa != NULL)
  6951. {
  6952. for (k = 0; k < ssa->ss_list->Count(); k++)
  6953. {
  6954. ss_p = (*ssa->ss_list)[k];
  6955. for (j = 0; j < ss_p->comps_list->Count(); j++)
  6956. {
  6957. ssc_p = (*ss_p->comps_list)[j];
  6958. ssc_p->init_moles = ssc_p->moles;
  6959. }
  6960. }
  6961. }
  6962. return true;
  6963. }
  6964. bool ModelEngine::RunReactions(int i, LDBLE step_fraction)
  6965. {
  6966. CONVERGE_RESULT converge;
  6967. md->run_reactions_iterations = 0;
  6968. converge = SetAndRunWrapper (i, step_fraction, i);
  6969. if (converge == MASS_BALANCE_CR)
  6970. throw EModelEngine(1, RUN_REACTIONS_MEF);
  6971. md->run_reactions_iterations += md->iterations;
  6972. md->iterations = md->run_reactions_iterations;
  6973. return true;
  6974. }
  6975. CONVERGE_RESULT ModelEngine::SetAndRunWrapper(int i, LDBLE step_fraction, int nsaver)
  6976. {
  6977. int j, max_try;
  6978. int old_itmax;
  6979. bool old_diag;
  6980. LDBLE old_tol, old_min_value, old_step, old_pe, old_pp_column_scale;
  6981. LDBLE small_pe_step, small_step;
  6982. bool ppa_saved, ssa_saved;
  6983. CONVERGE_RESULT converge;
  6984. /*
  6985. struct pp_assemblage *pp_assemblage_save = NULL;
  6986. struct s_s_assemblage *s_s_assemblage_save = NULL;
  6987. struct kinetics *kinetics_save = NULL;
  6988. */
  6989. /* 0 -- normal */
  6990. /* 1 -- try smaller step size, more iterations */
  6991. /* 2 -- try diagonal scaling */
  6992. /* 3 -- try smaller tolerance */
  6993. /* 4 -- try alternate scaling */
  6994. small_pe_step = 5.;
  6995. small_step = 10.;
  6996. converge = NOT_CONVERGED_CR;
  6997. old_diag = md->diagonal_scale;
  6998. old_itmax = md->itmax;
  6999. old_tol = md->ineq_tol;
  7000. old_step = md->step_size;
  7001. old_pe = md->pe_step_size;
  7002. old_min_value = md->min_value;
  7003. old_pp_column_scale = md->pp_column_scale;
  7004. if (md->state == REACTION)
  7005. {
  7006. if (!SetReaction(i))
  7007. throw exception();
  7008. }
  7009. if (md->use.ppa_p != NULL)
  7010. {
  7011. md->use.ppa_p->CopyTo(&ppa_save_1);
  7012. ppa_saved = true;
  7013. }
  7014. else
  7015. ppa_saved = false;
  7016. if (md->use.ssa_p != NULL)
  7017. {
  7018. md->use.ssa_p->CopyTo(&ssa_save_1);
  7019. ssa_saved = true;
  7020. }
  7021. else
  7022. ssa_saved = false;
  7023. max_try = 11;
  7024. for (j = 0; j < max_try; j++)
  7025. {
  7026. if (j == 1)
  7027. {
  7028. if (md->pe_step_size <= small_pe_step && md->step_size <= small_step)
  7029. continue;
  7030. md->itmax *= 2;
  7031. md->step_size = small_step;
  7032. md->pe_step_size = small_pe_step;
  7033. }
  7034. else if (j == 2)
  7035. {
  7036. md->itmax *= 2;
  7037. md->diagonal_scale = !md->diagonal_scale;
  7038. }
  7039. else if (j == 3)
  7040. {
  7041. md->itmax *= 2;
  7042. md->ineq_tol /= 10.;
  7043. }
  7044. else if (j == 4)
  7045. {
  7046. md->itmax *= 2;
  7047. md->ineq_tol *= 10.;
  7048. }
  7049. else if (j == 5)
  7050. {
  7051. md->itmax *= 2;
  7052. md->diagonal_scale = !md->diagonal_scale;
  7053. md->ineq_tol /= 10.;
  7054. }
  7055. else if (j == 6)
  7056. {
  7057. md->itmax *= 2;
  7058. md->pp_column_scale = (LDBLE)1e-10;
  7059. }
  7060. else if (j == 7)
  7061. {
  7062. md->itmax *= 2;
  7063. md->pp_column_scale = (LDBLE)1e-10;
  7064. md->diagonal_scale = !md->diagonal_scale;
  7065. }
  7066. else if (j == 8)
  7067. {
  7068. md->itmax *= 2;
  7069. md->min_value *= 10;
  7070. }
  7071. else if (j == 9)
  7072. {
  7073. md->aqueous_only = 5;
  7074. }
  7075. else if (j == 10)
  7076. {
  7077. md->negative_concentrations = true;
  7078. }
  7079. if (j > 0)
  7080. {
  7081. if (ppa_saved)
  7082. ppa_save_1.CopyTo(md->use.ppa_p);
  7083. if (ssa_saved)
  7084. ssa_save_1.CopyTo(md->use.ssa_p);
  7085. }
  7086. converge = SetAndRun(i, nsaver, step_fraction);
  7087. md->diagonal_scale = old_diag;
  7088. md->itmax = old_itmax;
  7089. md->ineq_tol = old_tol;
  7090. md->step_size = old_step;
  7091. md->pe_step_size = old_pe;
  7092. md->min_value = old_min_value;
  7093. md->pp_column_scale = old_pp_column_scale;
  7094. md->aqueous_only = 0;
  7095. md->negative_concentrations = false;
  7096. if (converge == OK_CR || converge == CONVERGED_CR || converge == MASS_BALANCE_CR)
  7097. break;
  7098. }
  7099. if (converge == NOT_CONVERGED_CR)
  7100. throw EModelEngine(2, SET_AND_RUN_WRAPPER_MEF);
  7101. return converge;
  7102. }
  7103. CONVERGE_RESULT ModelEngine::SetAndRun(int i, int nsaver, LDBLE step_fraction)
  7104. {
  7105. //
  7106. // i --user number for soln, reaction, etc.
  7107. // use_mix --integer flag
  7108. // state == TRANSPORT: DISP, STAG, NOMIX
  7109. // state == REACTION: TRUE, FALSE
  7110. // use_kinetics --true or false flag to calculate kinetic reactions
  7111. // nsaver --user number to store solution
  7112. // step_fraction--fraction of irreversible reaction to add
  7113. //
  7114. //int n, n1, n2;
  7115. CONVERGE_RESULT converge;
  7116. if (md->state == REACTION)
  7117. {
  7118. if (!SetReaction (i))
  7119. throw exception();
  7120. }
  7121. // Take step
  7122. if (md->state >= REACTION)
  7123. {
  7124. if (Step(step_fraction) == MASS_BALANCE_CR)
  7125. return MASS_BALANCE_CR;
  7126. // Always use solution, exchange, and surface -1
  7127. md->use.sol_p = md->use.sol_list->Element(1); //md->use.SearchSolution(-1);
  7128. if (md->use.exc_p != NULL)
  7129. md->use.exc_p = md->use.exc_list->Element(1); //md->use.SearchExchange(-1);
  7130. if (md->use.sur_p != NULL)
  7131. md->use.sur_p = md->use.sur_list->Element(1); //md->use.SearchSurface(-1);
  7132. }
  7133. if (md->use.sur_p != NULL)
  7134. md->dl_type_x = md->use.sur_p->dl_type;
  7135. if (md->use.sur_p != NULL && md->dl_type_x != NO_DL)
  7136. converge = ExecuteSurfaceModel();
  7137. else
  7138. {
  7139. #ifdef DEBUG_MOHID
  7140. //d.debug_status = true;
  7141. #endif
  7142. if (!Prepare())
  7143. throw exception();
  7144. if (!KTemp(md->use.sol_p->tc))
  7145. throw exception();
  7146. #ifdef DEBUG_MOHID
  7147. if (d.debug_status)
  7148. fprintf(d.set_f, "called_by_set_and_run: %d\n", d.set_count++);
  7149. #endif
  7150. if (!Set(false))
  7151. throw exception();
  7152. converge = ExecuteModel();
  7153. #ifdef DEBUG_MOHID
  7154. //d.debug_status = true;
  7155. #endif
  7156. }
  7157. SumSpecies();
  7158. return (converge);
  7159. }
  7160. CONVERGE_RESULT ModelEngine::Step(LDBLE step_fraction)
  7161. {
  7162. //
  7163. // zero global solution, add solution or mixture, add exchange,
  7164. // add surface, add gas phase, add solid solutions,
  7165. // set temperature, and add reaction.
  7166. // Ensure all elements
  7167. // included in any of these are present in small amounts.
  7168. // Save result as n_user -1.
  7169. //
  7170. //LDBLE difftemp;
  7171. int step_number;
  7172. // Zero out global solution data
  7173. md->ResetXVariables(); //xsolution_zero ();
  7174. // Set reaction to zero
  7175. md->step_x = 0.0;
  7176. step_number = md->reaction_step;
  7177. if (!AddSolution(md->use.sol_p, 1.0, 1.0))
  7178. throw exception();
  7179. // Exchange
  7180. if (md->use.exc_p != NULL && !AddExchange(md->use.exc_p))
  7181. throw exception();
  7182. // Surface
  7183. if (md->use.sur_p != NULL && !AddSurface(md->use.sur_p))
  7184. throw exception();
  7185. // Gases
  7186. if (md->use.gas_p != NULL && !AddGasPhase(md->use.gas_p))
  7187. throw exception();
  7188. // Pure phases and solid solutions are added to avoid zero or negative concentrations
  7189. // Pure phases
  7190. if (md->use.ppa_p != NULL)
  7191. {
  7192. md->use.ppa_p->CopyTo(&ppa_save_2);
  7193. if (!AddPPAssemblage(md->use.ppa_p))
  7194. throw exception();
  7195. }
  7196. // Solid solutions
  7197. if (md->use.ssa_p != NULL)
  7198. {
  7199. md->use.ssa_p->CopyTo(&ssa_save_2);
  7200. if (!AddSSAssemblage(md->use.ssa_p))
  7201. throw exception();
  7202. }
  7203. // Check that elements are available for gas components, pure phases, and solid solutions
  7204. if (md->use.gas_p != NULL)
  7205. GasPhaseCheck(md->use.gas_p);
  7206. if (md->use.ppa_p != NULL)
  7207. PPAssemblageCheck(md->use.ppa_p);
  7208. if (md->use.ssa_p != NULL)
  7209. SSAssemblageCheck(md->use.ssa_p);
  7210. // Check that element moles are >= zero
  7211. if (SolutionCheck() == MASS_BALANCE_CR)
  7212. {
  7213. // reset moles and deltas
  7214. if (md->use.ppa_p != NULL)
  7215. ppa_save_2.CopyTo(md->use.ppa_p);
  7216. if (md->use.ssa_p != NULL)
  7217. ssa_save_2.CopyTo(md->use.ssa_p);
  7218. return (MASS_BALANCE_CR);
  7219. }
  7220. // Copy global into solution n_user = -1
  7221. SaveSolution(md->use.sol_list->Element(1)); //ToDo: CHECK how will be done this
  7222. StepSaveSurf(md->use.sur_list->Element(1)); //ToDo: Criar estas funушes
  7223. StepSaveExch(md->use.exc_list->Element(1)); //ToDo: Criar estas funушes
  7224. return (OK_CR);
  7225. }
  7226. void ModelEngine::SaveSolution(Solution *dest)
  7227. {
  7228. int i; //, j, n;
  7229. //int count_mass_balance, count_master_activity;
  7230. int max_mass_balance, max_master_activity;
  7231. Master *m; //, *mi;
  7232. MasterActivity *ma;
  7233. Conc *c;
  7234. max_mass_balance = MAX_MASS_BALANCE;
  7235. max_master_activity = MAX_MASS_BALANCE;
  7236. dest->tc = md->tc_x;
  7237. dest->ph = md->ph_x;
  7238. dest->solution_pe = md->solution_pe_x;
  7239. dest->mu = md->mu_x;
  7240. //d.PrintSpeciesInfoToFile("SaveSolution-1", md->species_info_list, md, gd);
  7241. dest->ah2o = md->ah2o_x;
  7242. dest->density = md->density_x;
  7243. dest->total_h = md->total_h_x;
  7244. dest->total_o = md->total_o_x;
  7245. dest->cb = md->cb_x;
  7246. dest->mass_water = md->mass_water_aq_x;
  7247. dest->total_alk = md->total_alkalinity;
  7248. dest->units = _mol_kgw_;
  7249. dest->InitPE();
  7250. dest->default_pe = 0;
  7251. dest->totals->Clear();
  7252. dest->ma->Clear();
  7253. for (i = 0; i < gd->master_list.Count(); i++)
  7254. {
  7255. m = gd->master_list[i];
  7256. if (m->s->type == EX || m->s->type == SURF || m->s->type == SURF_PSI)
  7257. continue;
  7258. if (m->s == gd->s_hplus || m->s == gd->s_h2o)
  7259. continue;
  7260. // Save list of log activities
  7261. if (m->in || m->rewrite)
  7262. {
  7263. ma = dest->ma->AddNew();
  7264. ma->description = m->name;
  7265. ma->la = m->s->la;
  7266. }
  7267. if (m->total <= MIN_TOTAL)
  7268. {
  7269. m->total = 0.0;
  7270. m->total_primary = 0.0;
  7271. continue;
  7272. }
  7273. // Save list of concentrations
  7274. c = dest->totals->AddNew();
  7275. c->name = m->name;
  7276. c->input_conc = m->total;
  7277. c->moles = m->total;
  7278. c->units = dest->units;
  7279. c->equation_name = "";
  7280. c->n_pe = 0;
  7281. c->p = NULL;
  7282. c->phase_si = 0.0;
  7283. c->as = "";
  7284. c->gfw = 0.0;
  7285. }
  7286. dest->species_gamma->Clear();
  7287. }
  7288. bool ModelEngine::CheckPPAssemblage(PPAssemblage *ppa)
  7289. {
  7290. //
  7291. // Check list of all elements in pure_phase assemblage to see
  7292. // if all are in model. Return true if all are present,
  7293. // Return false if one or more is missing.
  7294. //
  7295. int j;
  7296. Master *m;
  7297. for (j = 0; j < ppa->eos_list->Count(); j++)
  7298. {
  7299. m = (*ppa->eos_list)[j]->e->primary;
  7300. if (m->s == gd->s_h2o || m->s == gd->s_hplus)
  7301. continue;
  7302. if (m->total > MIN_TOTAL)
  7303. continue;
  7304. return false;
  7305. }
  7306. return true;
  7307. }
  7308. CONVERGE_RESULT ModelEngine::SolutionCheck()
  7309. {
  7310. //
  7311. // Check for missing elements
  7312. //
  7313. int i;
  7314. Master *m;
  7315. // Check that all elements are in solution for phases with zero mass
  7316. for (i = 0; i < gd->master_list.Count(); i++)
  7317. {
  7318. m = gd->master_list[i];
  7319. if (m->total >= 0.0)
  7320. continue;
  7321. if (m->total > -MIN_TOTAL)
  7322. {
  7323. m->total = 0;
  7324. continue;
  7325. }
  7326. if (m->s == gd->s_eminus || m->s == gd->s_h2o || m->s == gd->s_hplus || m->s == gd->s_h3oplus)
  7327. {
  7328. m->total = 0;
  7329. continue;
  7330. }
  7331. return (MASS_BALANCE_CR);
  7332. }
  7333. return (OK_CR);
  7334. }
  7335. //-----------------------------------------------------------------------------------------------------------
  7336. bool ModelEngine::SetupSolution()
  7337. {
  7338. //
  7339. // Fills in data in unknown structure for the solution
  7340. //
  7341. int x_index, j;
  7342. Master *m;
  7343. Conc *conc;
  7344. String token, token2, token_list;
  7345. Unknown *x;
  7346. Solution *sol_p = md->use.sol_p;
  7347. count_unknowns = 0;
  7348. for (x_index = 0; x_index < sol_p->totals->Count(); x_index++)
  7349. {
  7350. x = (*unknown_list)[count_unknowns];
  7351. conc = (*sol_p->totals)[x_index];
  7352. token = conc->name(0, " ");
  7353. if (conc->input_conc <= 0.0 && token != "H(1)" && token != "E")
  7354. continue;
  7355. m = gd->master_list.Search(&token, true);
  7356. if (m == NULL)
  7357. return false;
  7358. if (m->type != AQ)
  7359. return false;
  7360. conc->name.Trim(_all_);
  7361. int p = conc->name.Find(" ");
  7362. if (p != -1)
  7363. token_list = conc->name(p + 1);
  7364. else
  7365. token_list = "";
  7366. // Store list of master species pointers, set master[i].in and master[i].rxn for list
  7367. GetMasterList(token_list, m, x->master);
  7368. SetupMasterReaction(x->master, &(*md->pe_x)[conc->n_pe]->rxn);
  7369. // Set default unknown data
  7370. x->type = MB;
  7371. x->name = conc->name;
  7372. x->total = conc;
  7373. for (j = 0; j < x->master->Count(); j++)
  7374. {
  7375. m = (*x->master)[j];
  7376. m->u = x;
  7377. }
  7378. x->moles = conc->moles;
  7379. #ifdef DEBUG_MOHID
  7380. if (d.debug_status)
  7381. fprintf(d.setup_solution_f, "SetupSolution-1) %d %20.20e\n", x_index, x->moles);
  7382. #endif
  7383. token.ToLower();
  7384. // Set pointers
  7385. if (token.Find("alk", true) != -1)
  7386. {
  7387. if (md->alkalinity_unknown == NULL)
  7388. {
  7389. x->type = ALK;
  7390. md->alkalinity_unknown = x;
  7391. }
  7392. else
  7393. return false;
  7394. }
  7395. else if (token == "c" || token == "c(4)")
  7396. {
  7397. if (md->carbon_unknown == NULL)
  7398. md->carbon_unknown = x;
  7399. else
  7400. return false;
  7401. }
  7402. else if (token == "h(1)")
  7403. {
  7404. if (md->ph_unknown == NULL)
  7405. md->ph_unknown = x;
  7406. else
  7407. return false;
  7408. }
  7409. else if (token == "e")
  7410. {
  7411. if (md->pe_unknown == NULL)
  7412. md->pe_unknown = x;
  7413. else
  7414. return false;
  7415. }
  7416. // Charge balance unknown
  7417. if (!conc->equation_name.IsEmpty())
  7418. {
  7419. if (conc->charge)
  7420. {
  7421. if (md->charge_balance_unknown == NULL)
  7422. {
  7423. md->charge_balance_unknown = x;
  7424. x->type = CB;
  7425. if (md->charge_balance_unknown == md->ph_unknown)
  7426. x->moles = sol_p->cb;
  7427. }
  7428. else
  7429. return false;
  7430. }
  7431. else
  7432. {
  7433. conc->p = gd->phase_list.Search(&conc->equation_name, true);
  7434. if (conc->p == NULL)
  7435. return false;
  7436. x->type = SOLUTION_PHASE_BOUNDARY;
  7437. x->p = conc->p;
  7438. x->si = conc->phase_si;
  7439. if (md->solution_phase_boundary_unknown == NULL)
  7440. md->solution_phase_boundary_unknown = x;
  7441. }
  7442. }
  7443. //conc->x = x;
  7444. count_unknowns++;
  7445. }
  7446. // Set mb_unknown
  7447. if (count_unknowns > 0)
  7448. md->mb_unknown = (*unknown_list)[0];
  7449. // Special for alkalinity
  7450. if (md->alkalinity_unknown != NULL)
  7451. {
  7452. if (md->carbon_unknown != NULL)
  7453. {
  7454. // pH adjusted to obtain given alkalinity
  7455. if (md->ph_unknown == NULL)
  7456. {
  7457. md->ph_unknown = md->alkalinity_unknown;
  7458. m = gd->master_list.Search("H(1)", true);
  7459. if (!md->alkalinity_unknown->master->Store(0, m))
  7460. return false;
  7461. m->in = true;
  7462. m->rewrite = false;
  7463. m->u = md->ph_unknown;
  7464. if (!md->ph_unknown->master->Store(0, m))
  7465. return false;
  7466. md->ph_unknown->name = "H(1)";
  7467. }
  7468. else
  7469. return false;
  7470. }
  7471. else
  7472. {
  7473. m = (*md->alkalinity_unknown->master)[0]->s->secondary;
  7474. if (m != NULL)
  7475. {
  7476. m->in = true;
  7477. m->rewrite = false;
  7478. m->u = md->alkalinity_unknown;
  7479. }
  7480. else
  7481. return false;
  7482. }
  7483. }
  7484. // Ionic strength
  7485. md->mu_unknown = (*unknown_list)[count_unknowns];
  7486. md->mu_unknown->number = count_unknowns++;
  7487. md->mu_unknown->name = "Mu";
  7488. md->mu_unknown->type = MU;
  7489. md->mu_unknown->moles = 0.0;
  7490. // Activity of water
  7491. md->ah2o_unknown = (*unknown_list)[count_unknowns];
  7492. md->ah2o_unknown->number = count_unknowns++;
  7493. md->ah2o_unknown->name = "A(H2O)";
  7494. md->ah2o_unknown->type = AH2O;
  7495. md->ah2o_unknown->master->Clear();
  7496. md->ah2o_unknown->master->AddNew(gd->master_list.Search("O", true));
  7497. (*md->ah2o_unknown->master)[0]->u = md->ah2o_unknown;
  7498. md->ah2o_unknown->moles = 0.0;
  7499. if (md->state >= REACTION)
  7500. {
  7501. // Reaction: pH for charge balance
  7502. md->ph_unknown = (*unknown_list)[count_unknowns];
  7503. md->ph_unknown->number = count_unknowns++;
  7504. md->ph_unknown->name = "pH";
  7505. md->ph_unknown->type = CB;
  7506. md->ph_unknown->moles = sol_p->cb;
  7507. md->ph_unknown->master->Clear();
  7508. md->ph_unknown->master->AddNew(gd->s_hplus->primary);
  7509. (*md->ph_unknown->master)[0]->u = md->ph_unknown;
  7510. md->charge_balance_unknown = md->ph_unknown;
  7511. //Reaction: pe for total hydrogen
  7512. md->pe_unknown = (*unknown_list)[count_unknowns];
  7513. md->pe_unknown->number = count_unknowns++;
  7514. md->mass_hydrogen_unknown = md->pe_unknown;
  7515. md->mass_hydrogen_unknown->name = "Hydrogen";
  7516. md->mass_hydrogen_unknown->type = MH;
  7517. md->mass_hydrogen_unknown->moles = sol_p->total_h - 2 * sol_p->total_o;
  7518. md->mass_hydrogen_unknown->master->Clear();
  7519. md->mass_hydrogen_unknown->master->AddNew(gd->s_eminus->primary);
  7520. (*md->mass_hydrogen_unknown->master)[0]->u = md->mass_hydrogen_unknown;
  7521. // Reaction H2O for total oxygen
  7522. md->mass_oxygen_unknown = (*unknown_list)[count_unknowns];
  7523. md->mass_oxygen_unknown->number = count_unknowns++;
  7524. md->mass_oxygen_unknown->name = "Oxygen";
  7525. md->mass_oxygen_unknown->type = MH2O;
  7526. md->mass_oxygen_unknown->moles = sol_p->total_o;
  7527. md->mass_oxygen_unknown->master->Clear();
  7528. md->mass_oxygen_unknown->master->AddNew(gd->s_h2o->primary);
  7529. }
  7530. // Validity tests
  7531. if (md->ph_unknown != NULL && md->ph_unknown == md->charge_balance_unknown && md->alkalinity_unknown != NULL)
  7532. return false;
  7533. if (md->alkalinity_unknown != NULL && (md->alkalinity_unknown->type == CB || md->alkalinity_unknown->type == SOLUTION_PHASE_BOUNDARY))
  7534. return false;
  7535. return true;
  7536. }
  7537. //-----------------------------------------------------------------------------------------------------------
  7538. bool ModelEngine::SetupExchange()
  7539. {
  7540. //
  7541. // Fill in data for exchanger in unknowns structures
  7542. //
  7543. int i, j;
  7544. Master *m_p;
  7545. Use *use;
  7546. ExchComp *excc_p;
  7547. ElementOfSpecies *eos_p;
  7548. Unknown *u;
  7549. use = &md->use;
  7550. if (use->exc_p == NULL)
  7551. return true;
  7552. for (j = 0; j < use->exc_p->comps->Count(); j++)
  7553. {
  7554. excc_p = (*use->exc_p->comps)[j];
  7555. for (i = 0; i < excc_p->totals->Count(); i++)
  7556. {
  7557. eos_p = (*excc_p->totals)[i];
  7558. // Find master species
  7559. if ((m_p = eos_p->e->master) == false)
  7560. return false;
  7561. if (m_p->type != EX)
  7562. continue;
  7563. // Check for data already given
  7564. if (m_p->in || m_p->rewrite) //ToDo: Check if the "rewrite" check is necessary or if is wrong
  7565. (*unknown_list)[m_p->u->number]->moles += eos_p->coef;
  7566. else
  7567. {
  7568. // Set flags
  7569. m_p->in = true;
  7570. m_p->rewrite = false;
  7571. // Set unknown data
  7572. u = (*unknown_list)[count_unknowns++];
  7573. u->type = EXCH;
  7574. u->exch_comp = (*use->exc_p->comps)[j];
  7575. u->name = eos_p->e->name;
  7576. u->moles = eos_p->coef;
  7577. u->master->Clear();
  7578. u->master->AddNew(m_p);
  7579. (*u->master)[0]->u = u;
  7580. }
  7581. }
  7582. }
  7583. return true;
  7584. }
  7585. //-----------------------------------------------------------------------------------------------------------
  7586. bool ModelEngine::SetupSurface()
  7587. {
  7588. //
  7589. // Fill in data for surface assemblage in unknown structure
  7590. //
  7591. int i, j, plane;
  7592. int mb_unknown_number, type;
  7593. Use *use;
  7594. SurfaceComp *sc_p;
  7595. ElementOfSpecies *eos_p;
  7596. Master *m_p;
  7597. Unknown *u, *u_prior, *u_p, **u_target, *u2;
  7598. String token,
  7599. mass_balance_name,
  7600. cb_suffix,
  7601. psi_suffix,
  7602. name1,
  7603. name2;
  7604. use = &md->use;
  7605. if (use->sur_p == NULL)
  7606. return true;
  7607. for (i = 0; i < use->sur_p->comps->Count(); i++)
  7608. {
  7609. sc_p = (*use->sur_p->comps)[i];
  7610. // Find master species for each surface, setup unknown structure
  7611. for (j = 0; j < sc_p->totals->Count(); j++)
  7612. {
  7613. eos_p = (*sc_p->totals)[j];
  7614. if ((m_p = eos_p->e->master) == NULL)
  7615. return false;
  7616. if (m_p->type != SURF)
  7617. continue;
  7618. // Check that data not already given
  7619. if (m_p->in || m_p->rewrite)
  7620. return false;
  7621. // Set flags
  7622. sc_p->master = m_p;
  7623. sc_p->name = m_p->name;
  7624. m_p->in = true;
  7625. m_p->rewrite = false;
  7626. // Setup mass balance unknown
  7627. u = (*unknown_list)[count_unknowns];
  7628. u_prior = (*unknown_list)[count_unknowns - 1];
  7629. u->type = SURFACE;
  7630. u->name = eos_p->e->name;
  7631. u->number = count_unknowns++;
  7632. u->surface_comp = sc_p;
  7633. u->master->Clear();
  7634. u->master->AddNew(m_p);
  7635. (*u->master)[0]->u = u;
  7636. u->moles = eos_p->coef;
  7637. if (md->surface_unknown == NULL)
  7638. md->surface_unknown = u;
  7639. u->potential_unknown = NULL;
  7640. if (use->sur_p->type == DDL)
  7641. {
  7642. // Setup surface-potential unknown
  7643. token = m_p->e->name;
  7644. u_p = FindSurfaceChargeUnknown (&token, SURF_PSI);
  7645. if (u_p != NULL)
  7646. u_prior->potential_unknown = u_p;
  7647. else
  7648. {
  7649. // Find master species
  7650. token.Replace("_CB", "_psi");
  7651. m_p = gd->master_list.Search(&token, true);
  7652. m_p->in = true;
  7653. m_p->rewrite = false;
  7654. // Find surface charge structure
  7655. u = (*unknown_list)[count_unknowns];
  7656. u_prior = (*unknown_list)[count_unknowns - 1];
  7657. u->type = SURFACE_CB;
  7658. u->surface_charge = (*use->sur_p->charge)[sc_p->charge];
  7659. u->related_moles = u->surface_charge->grams;
  7660. u->mass_water = (*use->sur_p->charge)[sc_p->charge]->mass_water;
  7661. token.Replace("_psi", "_CB");
  7662. u->name = token;
  7663. u->master->Clear();
  7664. u->master->AddNew(m_p);
  7665. (*u->master)[0]->u = u;
  7666. u->moles = 0.0;
  7667. u_prior->potential_unknown = u;
  7668. u->surface_comp = u_prior->surface_comp;
  7669. count_unknowns++;
  7670. }
  7671. }
  7672. else if (use->sur_p->type == CD_MUSIC)
  7673. {
  7674. // Setup 3 surface-potential unknowns
  7675. mb_unknown_number = count_unknowns - 1;
  7676. mass_balance_name = token;
  7677. for (plane = SURF_PSI; plane <= SURF_PSI2; plane++)
  7678. {
  7679. cb_suffix = "_CB";
  7680. psi_suffix = "_psi";
  7681. u_target = NULL;
  7682. type = SURFACE_CB;
  7683. switch (plane)
  7684. {
  7685. case SURF_PSI:
  7686. type = SURFACE_CB;
  7687. u_target = &(*unknown_list)[mb_unknown_number]->potential_unknown;
  7688. break;
  7689. case SURF_PSI1:
  7690. cb_suffix += "b";
  7691. psi_suffix += "b";
  7692. type = SURFACE_CB1;
  7693. u_target = &(*unknown_list)[mb_unknown_number]->potential_unknown1;
  7694. break;
  7695. case SURF_PSI2:
  7696. cb_suffix += "d";
  7697. psi_suffix += "d";
  7698. type = SURFACE_CB2;
  7699. u_target = &(*unknown_list)[mb_unknown_number]->potential_unknown2;
  7700. break;
  7701. }
  7702. token = m_p->e->name;
  7703. u_p = FindSurfaceChargeUnknown (token, plane);
  7704. if (u_p != NULL)
  7705. *u_target = u_p;
  7706. else
  7707. {
  7708. // Find master species
  7709. token.Replace(cb_suffix, psi_suffix);
  7710. m_p = gd->master_list.Search(&token, true);
  7711. m_p->in = true;
  7712. m_p->rewrite = false;
  7713. // Find surface charge structure
  7714. u = (*unknown_list)[count_unknowns];
  7715. u->type = type;
  7716. u->surface_charge = (*use->sur_p->charge)[sc_p->charge];
  7717. u->related_moles = u->surface_charge->grams;
  7718. u->mass_water = (*use->sur_p->charge)[sc_p->charge]->mass_water;
  7719. token.Replace(psi_suffix, cb_suffix);
  7720. u->name = token;
  7721. u->master->Clear();
  7722. u->master->AddNew(m_p);
  7723. // Find surface charge structure
  7724. if (plane == SURF_PSI)
  7725. (*unknown_list)[mb_unknown_number]->potential_unknown = u;
  7726. else if (plane == SURF_PSI1)
  7727. (*unknown_list)[mb_unknown_number]->potential_unknown1 = u;
  7728. else if (plane == SURF_PSI2)
  7729. (*unknown_list)[mb_unknown_number]->potential_unknown2 = u;
  7730. (*u->master)[0]->u = u;
  7731. u->moles = 0.0;
  7732. u->surface_comp = (*unknown_list)[mb_unknown_number]->surface_comp;
  7733. count_unknowns++;
  7734. }
  7735. }
  7736. // Add SURFACE unknown to a list for SURF_PSI
  7737. u_p = FindSurfaceChargeUnknown(token, SURF_PSI);
  7738. u_p->comp_unknowns->AddNew((*unknown_list)[mb_unknown_number]);
  7739. }
  7740. }
  7741. }
  7742. // check related phases
  7743. if (use->sur_p->related_phases)
  7744. {
  7745. for (i = 0; i < count_unknowns; i++)
  7746. {
  7747. u = (*unknown_list)[i];
  7748. if (u->type != SURFACE_CB)
  7749. continue;
  7750. for (j = 0; j < count_unknowns; j++)
  7751. {
  7752. u2 = (*unknown_list)[j];
  7753. if (u2->type != SURFACE)
  7754. continue;
  7755. if (u2->potential_unknown != u)
  7756. continue;
  7757. if (u2->surface_comp->phase_name != u->surface_comp->phase_name)
  7758. return false;
  7759. }
  7760. }
  7761. }
  7762. return true;
  7763. }
  7764. //-----------------------------------------------------------------------------------------------------------
  7765. Unknown *ModelEngine::FindSurfaceChargeUnknown (String *str, int plane)
  7766. {
  7767. //
  7768. // Makes name for the potential unknown and returns in str_ptr
  7769. // Returns NULL if this unknown not in unknown list else
  7770. // returns a pointer to the potential unknown
  7771. //
  7772. int i;
  7773. str->Replace("_", " ");
  7774. *str = (*str)(0, " ");
  7775. if (plane == SURF_PSI)
  7776. (*str) += "_CB";
  7777. else if (plane == SURF_PSI1)
  7778. (*str) += "_CBb";
  7779. else if (plane == SURF_PSI2)
  7780. (*str) += "_CBd";
  7781. for (i = 0; i < count_unknowns; i++)
  7782. {
  7783. if ((*unknown_list)[i]->name == *str)
  7784. return (*unknown_list)[i];
  7785. }
  7786. return NULL;
  7787. }
  7788. //-----------------------------------------------------------------------------------------------------------
  7789. bool ModelEngine::SetupPurePhases()
  7790. {
  7791. //
  7792. // Fills in data for pure_phase assemglage in unknown structure
  7793. //
  7794. int i;
  7795. Use *use;
  7796. Unknown *u;
  7797. PurePhase *pp;
  7798. use = &md->use;
  7799. if (use->ppa_p == NULL)
  7800. return true;
  7801. for (i = 0; i < use->ppa_p->pure_phases->Count(); i++)
  7802. {
  7803. pp = (*use->ppa_p->pure_phases)[i];
  7804. u = (*unknown_list)[count_unknowns++];
  7805. u->type = PP;
  7806. u->name = pp->name;
  7807. u->moles = pp->moles;
  7808. u->p = pp->phase;
  7809. u->si = pp->si;
  7810. u->delta = pp->delta;
  7811. u->pure_phase = pp;
  7812. u->dissolve_only = pp->dissolve_only;
  7813. if (md->pure_phase_unknown == NULL)
  7814. md->pure_phase_unknown = u;
  7815. }
  7816. return true;
  7817. }
  7818. //-----------------------------------------------------------------------------------------------------------
  7819. bool ModelEngine::SetupGasPhases()
  7820. {
  7821. //
  7822. // Fill in data for gas phase unknown (sum of partial pressures) in unknown structure
  7823. //
  7824. int i;
  7825. Use *use;
  7826. Unknown *u;
  7827. GasComp *gc_p;
  7828. use = &md->use;
  7829. if (use->gas_p == NULL)
  7830. return true;
  7831. // One for total moles in gas
  7832. u = (*unknown_list)[count_unknowns++];
  7833. u->type = GAS_MOLES;
  7834. u->name = "gas moles";
  7835. u->moles = 0.0;
  7836. for (i = 0; i < use->gas_p->comps->Count(); i++)
  7837. {
  7838. gc_p = (*use->gas_p->comps)[i];
  7839. u->moles += gc_p->moles;
  7840. }
  7841. if (u->moles <= 0)
  7842. u->moles = (LDBLE)MIN_TOTAL;
  7843. u->ln_moles = log (u->moles);
  7844. u->gas_phase = use->gas_p;
  7845. md->gas_unknown = u;
  7846. return true;
  7847. }
  7848. //-----------------------------------------------------------------------------------------------------------
  7849. bool ModelEngine::SetupSSAssemblage()
  7850. {
  7851. //
  7852. // Fill in data for solid solution unknowns (sum of partial pressures) in unknown structure
  7853. //
  7854. int i, j;
  7855. Use *use;
  7856. SS *ss_p;
  7857. SSComp *ssc_p;
  7858. Unknown *u;
  7859. use = &md->use;
  7860. if (use->ssa_p == NULL)
  7861. return true;
  7862. // One for each component in each solid solution
  7863. md->s_s_unknown = NULL;
  7864. for (j = 0; j < use->ssa_p->ss_list->Count(); j++)
  7865. {
  7866. ss_p = (*use->ssa_p->ss_list)[j];
  7867. for (i = 0; i < ss_p->comps_list->Count(); i++)
  7868. {
  7869. ssc_p = (*ss_p->comps_list)[i];
  7870. u = (*unknown_list)[count_unknowns];
  7871. u->type = S_S_MOLES;
  7872. u->name = ssc_p->name;
  7873. if (ssc_p->moles <= 0)
  7874. ssc_p->moles = (LDBLE)MIN_TOTAL_SS;
  7875. u->moles = ssc_p->moles;
  7876. ssc_p->initial_moles = u->moles;
  7877. u->ln_moles = log(u->moles);
  7878. u->s_s = ss_p;
  7879. u->s_s_comp = ssc_p;
  7880. u->s_s_comp_number = i;
  7881. u->p = ssc_p->phase;
  7882. u->number = count_unknowns++;
  7883. u->p->dn = ssc_p->dn;
  7884. u->p->dnb = ssc_p->dnb;
  7885. u->p->dnc = ssc_p->dnc;
  7886. u->p->log10_fraction_x = ssc_p->log10_fraction_x;
  7887. u->p->log10_lambda = ssc_p->log10_lambda;
  7888. if (md->s_s_unknown == NULL)
  7889. md->s_s_unknown = u;
  7890. }
  7891. }
  7892. return true;
  7893. }
  7894. //-----------------------------------------------------------------------------------------------------------
  7895. bool ModelEngine::SetupRelatedSurface()
  7896. {
  7897. //
  7898. // Fill in data for surface assemblage in unknown structure
  7899. //
  7900. int i, k;
  7901. Use *use;
  7902. Unknown *u, *u2, *u_prior;
  7903. SurfaceComp *sc_p;
  7904. //struct surface_comp *comp_ptr;
  7905. use = &md->use;
  7906. if (use->sur_p == NULL || !use->sur_p->related_phases)
  7907. return true;
  7908. for (i = 0; i < count_unknowns; i++)
  7909. {
  7910. u = (*unknown_list)[i];
  7911. if (i > 0)
  7912. u_prior = (*unknown_list)[i - 1];
  7913. if (u->type == SURFACE && !u->surface_comp->phase_name.IsEmpty())
  7914. {
  7915. for (k = count_unknowns - 1; k >= 0; k--)
  7916. {
  7917. u2 = (*unknown_list)[k];
  7918. if (u2->type != PP)
  7919. continue;
  7920. if (u2->p->name == u->surface_comp->phase_name)
  7921. break;
  7922. }
  7923. if (k == -1)
  7924. continue;
  7925. sc_p = u->surface_comp;
  7926. u->phase_unknown = u2;
  7927. u->moles = u2->moles * sc_p->phase_proportion;
  7928. }
  7929. else if (u->type == SURFACE_CB && !u_prior->surface_comp->phase_name.IsEmpty())
  7930. {
  7931. for (k = count_unknowns - 1; k >= 0; k--)
  7932. {
  7933. u2 = (*unknown_list)[k];
  7934. if (u2->type != PP)
  7935. continue;
  7936. if (u2->p->name == u->surface_comp->phase_name)
  7937. break;
  7938. }
  7939. if (k == -1)
  7940. continue;
  7941. sc_p = u->surface_comp;
  7942. u->phase_unknown = u2;
  7943. u->related_moles = u2->moles * sc_p->phase_proportion;
  7944. }
  7945. }
  7946. return true;
  7947. }
  7948. //-----------------------------------------------------------------------------------------------------------
  7949. bool ModelEngine::TidyRedox()
  7950. {
  7951. //
  7952. // Write pe redox reactions (rxn in struct pe_data) in terms of master species
  7953. // defined in analytical data
  7954. //
  7955. int i, j;
  7956. String *tok1, *tok2;
  7957. //Keep valences of oxygen and hydrogen in model, if not already in
  7958. Master *m;
  7959. for (i = 0; i < gd->master_list.Count(); i++)
  7960. {
  7961. m = gd->master_list[i];
  7962. if (m->primary == true && (m->s == gd->s_hplus || m->s == gd->s_h2o))
  7963. {
  7964. j = i + 1;
  7965. while (j < gd->master_list.Count() && gd->master_list[j]->e->primary == m)
  7966. {
  7967. if (!gd->master_list[j]->in && gd->master_list[j]->s != m->s)
  7968. {
  7969. gd->master_list[j]->rewrite = true;
  7970. *(gd->master_list[j]->pe_rxn) = *(m->pe_rxn);
  7971. }
  7972. j++;
  7973. }
  7974. }
  7975. }
  7976. //Writes equations for e- for each redox couple used in solution n
  7977. PEData *pe_ptr;
  7978. for (i = 0; i < md->pe_x->Count(); i++)
  7979. {
  7980. pe_ptr = (*md->pe_x)[i];
  7981. if (pe_ptr->name.Compare("pe", true) == 0)
  7982. gd->s_eminus->rxn->CopyTo(pe_ptr->rxn);
  7983. else
  7984. {
  7985. String t = pe_ptr->name;
  7986. t.Replace("/", " ");
  7987. token.SetString(t);
  7988. tok1 = token.NextToken();
  7989. tok2 = token.NextToken();
  7990. Master *m1 = gd->master_list.Search(tok1, true);
  7991. Master *m2 = gd->master_list.Search(tok2, true);
  7992. if (m1 != NULL && m2 != NULL)
  7993. {
  7994. r_temp->Reset();
  7995. if (!RewriteMasterToSecondary(m1, m2))
  7996. return false;
  7997. r_temp->TRXNSwap("e-");
  7998. }
  7999. else
  8000. return false;
  8001. if (!CheckIn())
  8002. return false;
  8003. else
  8004. r_temp->CopyTo(pe_ptr->rxn);
  8005. }
  8006. }
  8007. //Rewrite equations to master species that are "in" the model
  8008. for (i = 0; i < md->pe_x->Count(); i++)
  8009. {
  8010. pe_ptr = (*md->pe_x)[i];
  8011. r_temp->Reset();
  8012. r_temp->AddTRXN(pe_ptr->rxn, 1.0, false);
  8013. if (!WriteMassActionEqnX())
  8014. return false;
  8015. else
  8016. r_temp->CopyTo(pe_ptr->rxn);
  8017. }
  8018. return true;
  8019. }
  8020. //-----------------------------------------------------------------------------------------------------------
  8021. bool ModelEngine::BuildModel()
  8022. {
  8023. //
  8024. // Guts of prep. Determines species in model, rewrites equations,
  8025. // builds lists for mass balance and jacobian sums.
  8026. //
  8027. int i;
  8028. LDBLE coef_e;
  8029. String h2o("H2O");
  8030. if (gd->s_hplus == NULL || gd->s_eminus == NULL || gd->s_h2o == NULL)
  8031. return false;
  8032. // All these "lists" are "created" on initialization of ModelData instance
  8033. // Insted of "allocate" the space, is used the functions Add and AddNew that take
  8034. // care of allocation if necessary
  8035. // ToDo: verify if this is ok...
  8036. // Originally: "Make space for lists of pointers to species in the model"
  8037. md->s_x->Clear();
  8038. md->sum_mb1->Clear();
  8039. md->sum_mb2->Clear();
  8040. md->sum_jacob0->Clear();
  8041. md->sum_jacob1->Clear();
  8042. md->sum_jacob2->Clear();
  8043. md->sum_delta->Clear();
  8044. md->species_info_list->Clear();
  8045. // Pick species in the model, determine reaction for model, build jacobian
  8046. ComputeGFW(h2o, md->gfw_water);
  8047. md->gfw_water *= (LDBLE)0.001;
  8048. #ifdef DEBUG_MOHID
  8049. if (d.debug_status)
  8050. fprintf(d.buildmodel_f, "1) %20.20e\n", md->gfw_water);
  8051. #endif
  8052. Species *s;
  8053. for (i = 0; i < gd->species_list.Count(); i++)
  8054. {
  8055. s = gd->species_list[i];
  8056. if (s->type > H2O && s->type != EX && s->type != SURF)
  8057. continue;
  8058. s->in = false;
  8059. //s->rewrite = false;
  8060. r_temp->Reset();
  8061. r_temp->AddTRXN(s->rxn_s, 1.0, false);
  8062. // Check if species is in model
  8063. s->in = CheckIn();
  8064. if (s->in)
  8065. {
  8066. // for isotopes, activity of water is for 1H and 16O
  8067. // ToDo: Probably this will not be used until "isotopes" are seted in program
  8068. if (s->gflag == 9)
  8069. md->gfw_water = (LDBLE)18.0 / (LDBLE)1000.0;
  8070. s->lg = 0.0;
  8071. md->s_x->AddNew(s);
  8072. // Write mass action equation for current model
  8073. if (!WriteMassActionEqnX())
  8074. return false;
  8075. if (s->type == SURF)
  8076. {
  8077. if (!AddPotentialFactor()) //add_potential_factor ();
  8078. return false;
  8079. if (!AddCDMusicFactors(i))
  8080. return false; //add_cd_music_factors (i);
  8081. }
  8082. r_temp->CopyTo(s->rxn_x);
  8083. // Determine mass balance equations, build sums for mass balance, build sums for jacobian
  8084. r_temp->Reset();
  8085. r_temp->AddTRXN(s->rxn_s, 1.0, false);
  8086. if (s->e_sec_list->Count() <= 0)
  8087. WriteMBEqnX();
  8088. else
  8089. {
  8090. eos_list->Clear();
  8091. CopyToTempEOSList(s->e_sec_list, 1.0);
  8092. }
  8093. if (s->type == SURF)
  8094. {
  8095. if (!AddPotentialFactor())
  8096. return false;
  8097. if (!AddCDMusicFactors(i))
  8098. return false;
  8099. if (!AddSurfaceChargeBalance())
  8100. return false;
  8101. if (!AddCDMusicChargeBalances(i))
  8102. return false;
  8103. }
  8104. if (s->type < EMINUS)
  8105. MBForSpeciesAQ(i);
  8106. else if (s->type == EX)
  8107. MBForSpeciesEX(i);
  8108. else if (s->type == SURF)
  8109. MBForSpeciesSURF(i);
  8110. BuildMBSums ();
  8111. BuildJacobianSums (i);
  8112. // Build list of species for summing and printing
  8113. if (s->e_sec_list->Count() <= 0)
  8114. WriteMBForSpeciesList(i);
  8115. else
  8116. {
  8117. eos_list->Clear();
  8118. CopyToTempEOSList(s->e_sec_list, 1.0);
  8119. }
  8120. BuildSpeciesList(i);
  8121. #ifdef DEBUG_MOHID
  8122. if (d.debug_status)
  8123. fprintf(d.buildmodel_f, "2) number:%d %20.20e\n", s->number, s->la);
  8124. #endif
  8125. }
  8126. }
  8127. // Sum diffuse layer water into hydrogen and oxygen mass balances
  8128. Unknown *x;
  8129. if (md->dl_type_x != NO_DL && md->state >= REACTION)
  8130. {
  8131. for (i = 0; i < count_unknowns; i++)
  8132. {
  8133. x = (*unknown_list)[i];
  8134. if (x->type == SURFACE_CB)
  8135. {
  8136. if (md->mass_oxygen_unknown != NULL)
  8137. StoreMB (&x->mass_water, &md->mass_oxygen_unknown->f, 1 / md->gfw_water);
  8138. }
  8139. }
  8140. }
  8141. // Rewrite phases to current master species
  8142. Phase *phases;
  8143. for (i = 0; i < gd->phase_list.Count(); i++)
  8144. {
  8145. phases = gd->phase_list[i];
  8146. r_temp->Reset();
  8147. r_temp->AddPhaseTRXN(phases->rxn_s, 1.0, false);
  8148. r_temp->TRXNReverseK();
  8149. phases->in = CheckIn();
  8150. if (phases->in)
  8151. {
  8152. //Replace e- in original equation with default redox reaction
  8153. if (!GetReactionCoef(r_temp, "e-", coef_e)) //trxn_find_coef ("e-", 1);
  8154. return false;
  8155. if (!Equal(coef_e, 0.0, TOLERANCE))
  8156. r_temp->AddTRXN((*md->pe_x)[md->default_pe_x]->rxn, coef_e, true);
  8157. if (!WriteMassActionEqnX())
  8158. return false;
  8159. r_temp->TRXNReverseK();
  8160. r_temp->CopyTo(phases->rxn_x);
  8161. WritePhaseSysTotal(i);
  8162. }
  8163. }
  8164. BuildSolutionPhaseBoundaries ();
  8165. BuildPurePhases ();
  8166. BuildMinExch ();
  8167. BuildMinSurface ();
  8168. BuildGasPhase ();
  8169. BuildSSAssemblage ();
  8170. md->species_info_list->SortByMasterOnly(gd->s_hplus);
  8171. #ifdef DEBUG_MOHID
  8172. if (d.debug_status)
  8173. {
  8174. Species *d_s;
  8175. for(int d_i = 0; d_i < md->s_x->Count(); d_i++)
  8176. {
  8177. d_s = (*md->s_x)[d_i];
  8178. fprintf(d.buildmodel_f, "3) %d %20.20e\n", d_s->number, d_s->la);
  8179. }
  8180. }
  8181. #endif
  8182. SaveModel (); //ToDo: it's doing nothing for now
  8183. return true;
  8184. }
  8185. //-----------------------------------------------------------------------------------------------------------
  8186. LDBLE ModelEngine::SSRoot(LDBLE a0, LDBLE a1, LDBLE kc, LDBLE kb, LDBLE xcaq, LDBLE xbaq)
  8187. {
  8188. int i;
  8189. LDBLE x0, y0, x1, y1, xb, miny;
  8190. // Bracket answer
  8191. x0 = 0.0;
  8192. x1 = 0.0;
  8193. y0 = SSF (x0, a0, a1, kc, kb, xcaq, xbaq);
  8194. miny = fabs (y0);
  8195. for (i = 1; i <= 10; i++)
  8196. {
  8197. x1 = (LDBLE) i / 10;
  8198. y1 = SSF (x1, a0, a1, kc, kb, xcaq, xbaq);
  8199. if (fabs (y1) < miny)
  8200. miny = fabs (y1);
  8201. if (y0 * y1 < 0)
  8202. break;
  8203. else
  8204. {
  8205. x0 = x1;
  8206. y0 = y1;
  8207. }
  8208. }
  8209. // Interval halve
  8210. if (i > 10)
  8211. xb = 0.0;
  8212. else
  8213. xb = SSHalve (a0, a1, x0, x1, kc, kb, xcaq, xbaq);
  8214. return (xb);
  8215. }
  8216. //-----------------------------------------------------------------------------------------------------------
  8217. LDBLE ModelEngine::SSF(LDBLE xb, LDBLE a0, LDBLE a1, LDBLE kc, LDBLE kb, LDBLE xcaq, LDBLE xbaq)
  8218. {
  8219. //
  8220. // Need root of this function to determine xb
  8221. //
  8222. LDBLE lb, lc, f, xc, r;
  8223. xc = 1 - xb;
  8224. if (xb == 0)
  8225. xb = (LDBLE)1e-20;
  8226. if (xc == 0)
  8227. xc = (LDBLE)1e-20;
  8228. lc = exp ((a0 - a1 * (-4 * xb + 3)) * xb * xb);
  8229. lb = exp ((a0 + a1 * (4 * xb - 1)) * xc * xc);
  8230. r = lc * kc / (lb * kb);
  8231. f = xcaq * (xb / r + xc) + xbaq * (xb + r * xc) - 1;
  8232. return (f);
  8233. }
  8234. //-----------------------------------------------------------------------------------------------------------
  8235. LDBLE ModelEngine::SSHalve(LDBLE a0, LDBLE a1, LDBLE x0, LDBLE x1, LDBLE kc, LDBLE kb, LDBLE xcaq, LDBLE xbaq)
  8236. {
  8237. int i;
  8238. LDBLE x, y0, dx, y;
  8239. y0 = SSF (x0, a0, a1, kc, kb, xcaq, xbaq);
  8240. dx = (x1 - x0);
  8241. // Loop for interval halving
  8242. for (i = 0; i < 100; i++)
  8243. {
  8244. dx *= 0.5;
  8245. x = x0 + dx;
  8246. y = SSF(x, a0, a1, kc, kb, xcaq, xbaq);
  8247. if (dx < 1e-8 || y == 0)
  8248. break;
  8249. if (y0 * y >= 0)
  8250. {
  8251. x0 = x;
  8252. y0 = y;
  8253. }
  8254. }
  8255. return (x0 + dx);
  8256. }
  8257. //-----------------------------------------------------------------------------------------------------------
  8258. LDBLE ModelEngine::CalcPSIAvg(LDBLE surf_chrg_eq)
  8259. {
  8260. //
  8261. // calculate the average (F * Psi / RT) that lets the DL charge counter the surface charge
  8262. //
  8263. int i, iter, count_g;
  8264. LDBLE fd, fd1, p, temp, ratio_aq;
  8265. count_g = md->surface_charge_ptr->g->Count();
  8266. ratio_aq = md->surface_charge_ptr->mass_water / md->mass_water_aq_x;
  8267. p = 0;
  8268. if (surf_chrg_eq == 0)
  8269. return (0.0);
  8270. else if (surf_chrg_eq < 0)
  8271. p = (LDBLE)-0.5 * log (-surf_chrg_eq * ratio_aq / md->mu_x + (LDBLE)1);
  8272. else if (surf_chrg_eq > 0)
  8273. p = (LDBLE)0.5 * log (surf_chrg_eq * ratio_aq / md->mu_x + (LDBLE)1);
  8274. // Optimize p in SS{s_x[i]->moles * z_i * g(p)} = -surf_chrg_eq
  8275. // g(p) = exp(-p * z_i) * ratio_aq
  8276. // Elsewhere in PHREEQC, g is the excess, after subtraction of conc's for p = 0:
  8277. // g(p) = (exp(-p *z_i) - 1) * ratio_aq
  8278. iter = 0;
  8279. ChargeGroup *cg;
  8280. do
  8281. {
  8282. fd = surf_chrg_eq;
  8283. fd1 = 0.0;
  8284. for (i = 1; i < count_g; i++)
  8285. {
  8286. cg = gd->charge_group[i];
  8287. if (md->use.sur_p->type == CD_MUSIC)
  8288. temp = exp (-cg->z * p);
  8289. else // multiply with ratio_aq for multiplier options cp and cm in calc_all_donnan (not used now)... */
  8290. temp = exp (-cg->z * p) * ratio_aq;
  8291. if (md->use.sur_p->only_counter_ions && ((surf_chrg_eq < 0 && cg->z < 0) || (surf_chrg_eq > 0 && cg->z > 0)))
  8292. temp = 0.0;
  8293. fd += cg->eq * temp;
  8294. fd1 -= cg->z * cg->eq * temp;
  8295. }
  8296. fd /= -fd1;
  8297. p += (fd > 1) ? 1 : ((fd < -1) ? -1 : fd);
  8298. if (fabs (p) < md->G_TOL)
  8299. p = 0.0;
  8300. iter++;
  8301. if (iter > 50)
  8302. throw exception(); //ToDo: create exceptions manager
  8303. }
  8304. while (fabs (fd) > 1e-12 && p != 0.0);
  8305. return (p);
  8306. }
  8307. //-----------------------------------------------------------------------------------------------------------
  8308. void ModelEngine::SSBinary (SS *s_s_ptr)
  8309. {
  8310. LDBLE nb, nc, n_tot, xb, xc, dnb, dnc, a0, a1;
  8311. LDBLE xb2, xb3, xb4, xc2, xc3;
  8312. LDBLE xb1, xc1;
  8313. SSComp *ss0, *ss1;
  8314. // component 0 is major component
  8315. // component 1 is minor component
  8316. // xb is the mole fraction of second component (formerly trace)
  8317. // xc is the mole fraction of first component (formerly major)
  8318. // Calculate mole fractions and log lambda and derivative factors
  8319. n_tot = s_s_ptr->total_moles;
  8320. ss0 = (*s_s_ptr->comps_list)[0];
  8321. ss1 = (*s_s_ptr->comps_list)[1];
  8322. nc = ss0->moles;
  8323. xc = nc / n_tot;
  8324. nb = ss1->moles;
  8325. xb = nb / n_tot;
  8326. a0 = s_s_ptr->a0;
  8327. a1 = s_s_ptr->a1;
  8328. if (s_s_ptr->miscibility && xb > s_s_ptr->xb1 && xb < s_s_ptr->xb2)
  8329. {
  8330. // In miscibility gap
  8331. xb1 = s_s_ptr->xb1;
  8332. xc1 = (LDBLE)1.0 - xb1;
  8333. ss0->fraction_x = xc1;
  8334. ss0->log10_fraction_x = log10 (xc1);
  8335. ss0->phase->log10_fraction_x = ss0->log10_fraction_x;
  8336. ss1->fraction_x = xb1;
  8337. ss1->log10_fraction_x = log10 (xb1);
  8338. ss1->phase->log10_fraction_x = ss1->log10_fraction_x;
  8339. ss0->log10_lambda = xb1 * xb1 * (a0 - a1 * (3 - 4 * xb1)) / md->LOG_10;
  8340. ss0->phase->log10_lambda = ss0->log10_lambda;
  8341. ss1->log10_lambda = xc1 * xc1 * (a0 + a1 * (4 * xb1 - 1)) / md->LOG_10;
  8342. ss1->phase->log10_lambda = ss1->log10_lambda;
  8343. ss0->dnb = 0;
  8344. ss0->dnc = 0;
  8345. ss1->dnb = 0;
  8346. ss1->dnc = 0;
  8347. ss0->phase->dnb = 0;
  8348. ss0->phase->dnc = 0;
  8349. ss1->phase->dnb = 0;
  8350. ss1->phase->dnc = 0;
  8351. }
  8352. else
  8353. {
  8354. // Not in miscibility gap
  8355. ss0->fraction_x = xc;
  8356. ss0->log10_fraction_x = log10 (xc);
  8357. ss0->phase->log10_fraction_x =
  8358. ss0->log10_fraction_x;
  8359. ss1->fraction_x = xb;
  8360. ss1->log10_fraction_x = log10 (xb);
  8361. ss1->phase->log10_fraction_x = ss1->log10_fraction_x;
  8362. ss0->log10_lambda = xb * xb * (a0 - a1 * (3 - 4 * xb)) / md->LOG_10;
  8363. ss0->phase->log10_lambda = ss0->log10_lambda;
  8364. ss1->log10_lambda = xc * xc * (a0 + a1 * (4 * xb - 1)) / md->LOG_10;
  8365. ss1->phase->log10_lambda = ss1->log10_lambda;
  8366. xc2 = xc * xc;
  8367. xc3 = xc2 * xc;
  8368. xb2 = xb * xb;
  8369. xb3 = xb2 * xb;
  8370. xb4 = xb3 * xb;
  8371. // used derivation that did not substitute x2 = 1-x1
  8372. // first component, df1/dn1
  8373. dnc = 2 * a0 * xb2 + 12 * a1 * xc * xb2 + 6 * a1 * xb2;
  8374. ss0->phase->dnc = -xb / nc + dnc / n_tot;
  8375. // first component, df1/dn2
  8376. dnb = 1 - 2 * a0 * xb + 2 * a0 * xb2 + 8 * a1 * xc * xb - 12 * a1 * xc * xb2 - 2 * a1 * xb + 2 * a1 * xb2;
  8377. ss0->phase->dnb = dnb / n_tot;
  8378. // second component, df2/dn1
  8379. dnc = 1 - 2 * a0 * xc + 2 * a0 * xc2 - 8 * a1 * xb * xc + 12 * a1 * xb * xc2 + 2 * a1 * xc - 2 * a1 * xc2;
  8380. ss1->phase->dnc = dnc / n_tot;
  8381. // second component, df2/dn2
  8382. dnb = 2 * a0 * xc2 + 12 * a1 * xb * xc2 - 6 * a1 * xc2;
  8383. ss1->phase->dnb = -xc / nb + dnb / n_tot;
  8384. }
  8385. }
  8386. //-----------------------------------------------------------------------------------------------------------
  8387. void ModelEngine::SSIdeal(SS *s_s_ptr)
  8388. {
  8389. int k, j;
  8390. LDBLE n_tot, n_tot1;
  8391. // component 0 is major component
  8392. // component 1 is minor component
  8393. // xb is the mole fraction of second component (formerly trace)
  8394. // xc is the mole fraction of first component (formerly major)
  8395. // Calculate mole fractions and log lambda and derivative factors
  8396. n_tot = s_s_ptr->total_moles;
  8397. // Ideal solid solution
  8398. s_s_ptr->dn = (LDBLE)1.0 / n_tot;
  8399. SSComp *ssc_k, *ssc_j;
  8400. for (k = 0; k < s_s_ptr->comps_list->Count(); k++)
  8401. {
  8402. ssc_k = (*s_s_ptr->comps_list)[k];
  8403. n_tot1 = 0;
  8404. for (j = 0; j < s_s_ptr->comps_list->Count(); j++)
  8405. {
  8406. ssc_j = (*s_s_ptr->comps_list)[j];
  8407. if (j != k)
  8408. n_tot1 += ssc_j->moles;
  8409. }
  8410. ssc_k->log10_lambda = 0;
  8411. ssc_k->phase->log10_lambda = 0;
  8412. ssc_k->dnb = -(n_tot1) / ssc_k->moles * n_tot;
  8413. ssc_k->phase->dnb = ssc_k->dnb;
  8414. ssc_k->dn = s_s_ptr->dn;
  8415. ssc_k->phase->dn = s_s_ptr->dn;
  8416. }
  8417. }
  8418. //-----------------------------------------------------------------------------------------------------------
  8419. bool ModelEngine::CalcInitG()
  8420. {
  8421. int i, j, k;
  8422. int count_g, count_charge;
  8423. LDBLE la;
  8424. if (md->use.sur_p == NULL)
  8425. return true;
  8426. // calculate g for each surface
  8427. Unknown *u;
  8428. SurfaceDiffLayer *new_g, *g_p;
  8429. SpeciesDiffLayer *dl_p;
  8430. Species *s;
  8431. count_charge = 0;
  8432. for (j = 0; j < count_unknowns; j++)
  8433. {
  8434. u = (*unknown_list)[j];
  8435. if (u->type != SURFACE_CB)
  8436. continue;
  8437. md->surface_charge_ptr = u->surface_charge;
  8438. count_g = 0;
  8439. if (u->surface_charge->g != NULL)
  8440. count_g = u->surface_charge->g->Count();
  8441. if (count_g == 0)
  8442. {
  8443. new_g = u->surface_charge->g->AddNew();
  8444. new_g->charge = 0.0;
  8445. new_g->g = 0.0;
  8446. new_g->dg = 0.0;
  8447. la = (*u->master)[0]->s->la;
  8448. md->xd = exp (-2 * la * md->LOG_10);
  8449. md->alpha = sqrt (EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * (LDBLE)1000.0) * (LDBLE)1000.0 * md->tk_x * (LDBLE)0.5);
  8450. }
  8451. // calculate g for given surface for each species
  8452. count_g = 1;
  8453. for (i = 0; i < md->s_x->Count(); i++)
  8454. {
  8455. s = (*md->s_x)[i];
  8456. if (s->type > HPLUS)
  8457. continue;
  8458. for (k = 0; k < count_g; k++)
  8459. {
  8460. g_p = (*u->surface_charge->g)[k];
  8461. if (Equal(g_p->charge, s->z, md->G_TOL))
  8462. {
  8463. dl_p = (*s->diff_layer)[count_charge];
  8464. dl_p->charge = u->surface_charge;
  8465. dl_p->count_g = k;
  8466. dl_p->g_moles = 0.0;
  8467. dl_p->dg_g_moles = 0.0;
  8468. break;
  8469. }
  8470. }
  8471. if (k >= count_g)
  8472. {
  8473. g_p = u->surface_charge->g->AddNew();
  8474. // save g for charge
  8475. g_p->charge = s->z;
  8476. if (u->surface_charge->grams > 0.0)
  8477. {
  8478. g_p->g = 2 * md->alpha * sqrt (md->mu_x) * (pow ((LDBLE)md->xd, (LDBLE)(s->z / 2.0)) - 1) * md->surface_charge_ptr->grams * md->surface_charge_ptr->specific_area / F_C_MOL;
  8479. g_p->dg = -s->z;
  8480. if ((md->use.sur_p->only_counter_ions) && g_p->g < 0)
  8481. {
  8482. g_p->g = 0;
  8483. g_p->dg = 0;
  8484. }
  8485. }
  8486. else
  8487. {
  8488. g_p->g = 0.0;
  8489. g_p->dg = -s->z;
  8490. }
  8491. /* save g for species */
  8492. dl_p = (*s->diff_layer)[count_g];
  8493. dl_p->charge = u->surface_charge;
  8494. dl_p->count_g = count_g;
  8495. dl_p->g_moles = 0.0;
  8496. dl_p->dg_g_moles = 0.0;
  8497. count_g++;
  8498. }
  8499. }
  8500. count_charge++;
  8501. u->surface_charge->g->SetNewCapacity(count_g);
  8502. }
  8503. return true;
  8504. }
  8505. //-----------------------------------------------------------------------------------------------------------
  8506. bool ModelEngine::CalcAllG()
  8507. {
  8508. int i, j, k;
  8509. bool converge, converge1;
  8510. int count_g, count_charge;
  8511. LDBLE new_g, xd1;
  8512. LDBLE epsilon, la;
  8513. Unknown *u;
  8514. SurfaceDiffLayer *sdl_p;
  8515. SpeciesDiffLayer *spdl_p;
  8516. Species *s;
  8517. if (md->use.sur_p == NULL)
  8518. return (OK);
  8519. // calculate g for each surface
  8520. epsilon = md->convergence_tolerance;
  8521. if (md->convergence_tolerance >= 1e-8)
  8522. md->G_TOL = (LDBLE)1e-9;
  8523. else
  8524. md->G_TOL = (LDBLE)1e-10;
  8525. converge = true;
  8526. count_charge = 0;
  8527. for (j = 0; j < count_unknowns; j++)
  8528. {
  8529. u = (*unknown_list)[j];
  8530. if (u->type != SURFACE_CB)
  8531. continue;
  8532. md->surface_charge_ptr = u->surface_charge;
  8533. sdl_p = (*u->surface_charge->g)[0];
  8534. sdl_p->charge = 0.0;
  8535. sdl_p->g = 0.0;
  8536. sdl_p->dg = 0.0;
  8537. count_g = 1;
  8538. la = (*u->master)[0]->s->la;
  8539. md->xd = exp (-2 * la * md->LOG_10);
  8540. md->alpha = sqrt (EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * (LDBLE)1000.0) * (LDBLE)1000.0 * md->tk_x * (LDBLE)0.5);
  8541. // calculate g for given surface for each species
  8542. for (i = 0; i < md->s_x->Count(); i++)
  8543. {
  8544. s = (*md->s_x)[i];
  8545. if (s->type > HPLUS)
  8546. continue;
  8547. for (k = 0; k < count_g; k++)
  8548. {
  8549. sdl_p = (*u->surface_charge->g)[k];
  8550. if (Equal(sdl_p->charge, s->z, md->G_TOL))
  8551. {
  8552. spdl_p = (*s->diff_layer)[count_charge];
  8553. spdl_p->charge = u->surface_charge;
  8554. spdl_p->count_g = k;
  8555. break;
  8556. }
  8557. }
  8558. if (k < count_g)
  8559. continue;
  8560. if (u->surface_charge->grams > 0.0)
  8561. {
  8562. md->z = s->z;
  8563. if (
  8564. (md->use.sur_p->only_counter_ions) ||
  8565. (((la > 0) && (md->z < 0)) ||
  8566. ((la < 0) &&
  8567. (md->z > 0))))
  8568. {
  8569. if (md->xd > 0.1)
  8570. new_g = QRombMidPnt (1.0, md->xd);
  8571. else if (md->xd > 0.01)
  8572. new_g = QRombMidPnt((LDBLE)1.0, (LDBLE)0.1) + QRombMidPnt((LDBLE)0.1, md->xd);
  8573. else if (md->xd > 0.001)
  8574. new_g = QRombMidPnt((LDBLE)1.0, (LDBLE)0.1) + QRombMidPnt((LDBLE)0.1, (LDBLE)0.01) + QRombMidPnt((LDBLE)0.01, md->xd);
  8575. else if (md->xd > 0.0001)
  8576. new_g = QRombMidPnt((LDBLE)1.0, (LDBLE)0.1) + QRombMidPnt((LDBLE)0.1, (LDBLE)0.01) + QRombMidPnt((LDBLE)0.01, (LDBLE).001) + QRombMidPnt((LDBLE)0.001, md->xd);
  8577. else if (md->xd > 0.00001)
  8578. new_g = QRombMidPnt((LDBLE)1.0, (LDBLE)0.1) + QRombMidPnt((LDBLE)0.1, (LDBLE)0.01) + QRombMidPnt((LDBLE)0.01, (LDBLE).001) + QRombMidPnt((LDBLE)0.001, (LDBLE).0001) + QRombMidPnt((LDBLE)0.0001, md->xd);
  8579. else if (md->xd > 0.000001)
  8580. new_g = QRombMidPnt((LDBLE)1.0, (LDBLE)0.1) + QRombMidPnt((LDBLE)0.1, (LDBLE)0.01) + QRombMidPnt((LDBLE)0.01, (LDBLE).001) + QRombMidPnt((LDBLE)0.001, (LDBLE).0001) + QRombMidPnt((LDBLE)0.0001, (LDBLE).00001) + QRombMidPnt((LDBLE)0.00001, md->xd);
  8581. else if (md->xd > 0.0000001)
  8582. new_g = QRombMidPnt((LDBLE)1.0, (LDBLE)0.1) + QRombMidPnt((LDBLE)0.1, (LDBLE)0.01) + QRombMidPnt((LDBLE)0.01, (LDBLE).001) + QRombMidPnt((LDBLE)0.001, (LDBLE).0001) + QRombMidPnt((LDBLE)0.0001, (LDBLE).00001) + QRombMidPnt ((LDBLE)0.00001, (LDBLE).000001) + QRombMidPnt((LDBLE)0.000001, md->xd);
  8583. else if (md->xd > 0.00000001)
  8584. new_g = QRombMidPnt((LDBLE)1.0, (LDBLE)0.1) + QRombMidPnt((LDBLE)0.1, (LDBLE)0.01) + QRombMidPnt((LDBLE)0.01, (LDBLE).001) + QRombMidPnt((LDBLE)0.001, (LDBLE).0001) + QRombMidPnt((LDBLE)0.0001, (LDBLE).00001) + QRombMidPnt ((LDBLE)0.00001, (LDBLE).000001) + QRombMidPnt((LDBLE)0.000001, (LDBLE).0000001) + QRombMidPnt((LDBLE)0.0000001, md->xd);
  8585. else
  8586. new_g = QRombMidPnt((LDBLE)1.0, (LDBLE)0.1) + QRombMidPnt((LDBLE)0.1, (LDBLE)0.01) + QRombMidPnt((LDBLE)0.01, (LDBLE).001) + QRombMidPnt((LDBLE)0.001, (LDBLE).0001) + QRombMidPnt((LDBLE)0.0001, (LDBLE).00001) + QRombMidPnt ((LDBLE)0.00001, (LDBLE).000001) + QRombMidPnt((LDBLE)0.000001, (LDBLE).0000001) + QRombMidPnt((LDBLE)0.0000001, (LDBLE).00000001) + QRombMidPnt((LDBLE)0.00000001, md->xd);
  8587. }
  8588. else
  8589. new_g = 0;
  8590. }
  8591. else
  8592. new_g = 0.0;
  8593. if (md->use.sur_p->only_counter_ions && new_g < 0)
  8594. new_g = 0;
  8595. sdl_p = (*u->surface_charge->g)[count_g];
  8596. sdl_p->charge = s->z;
  8597. converge1 = true;
  8598. if ((fabs (new_g) >= 1.) && (fabs ((new_g - sdl_p->g) / new_g) > epsilon))
  8599. converge1 = false;
  8600. else if (fabs (new_g - sdl_p->g) > epsilon)
  8601. converge1 = false;
  8602. if (converge1 == false)
  8603. converge = false;
  8604. sdl_p->g = new_g;
  8605. if (new_g == 0)
  8606. sdl_p->dg = 0;
  8607. else
  8608. {
  8609. if (u->surface_charge->grams > 0.0)
  8610. {
  8611. sdl_p->dg = md->surface_charge_ptr->grams * md->surface_charge_ptr->specific_area * md->alpha * GFunction(md->xd) / F_C_MOL;
  8612. sdl_p->dg *= (LDBLE)-2. / (exp (la * md->LOG_10) * exp (la * md->LOG_10));
  8613. if ((md->xd - 1) < 0.0)
  8614. sdl_p->dg *= -1.0;
  8615. if (fabs (sdl_p->dg) < 1e-8)
  8616. {
  8617. xd1 = exp ((LDBLE)-2 * (LDBLE)1e-3 * md->LOG_10);
  8618. new_g = QRombMidPnt(1.0, xd1);
  8619. sdl_p->dg = new_g / (LDBLE).001;
  8620. }
  8621. }
  8622. else
  8623. sdl_p->dg = 0.0;
  8624. }
  8625. spdl_p = (*s->diff_layer)[count_charge];
  8626. spdl_p->charge = u->surface_charge;
  8627. spdl_p->count_g = count_g;
  8628. count_g++;
  8629. }
  8630. count_charge++;
  8631. }
  8632. return (converge);
  8633. }
  8634. //-----------------------------------------------------------------------------------------------------------
  8635. LDBLE ModelEngine::QRombMidPnt(LDBLE x1, LDBLE x2)
  8636. {
  8637. LDBLE ss, dss;
  8638. LDBLE sv[MAX_QUAD + 2], h[MAX_QUAD + 2];
  8639. int j;
  8640. h[0] = 1.0;
  8641. sv[0] = MidPnt (x1, x2, 1);
  8642. for (j = 1; j < MAX_QUAD; j++)
  8643. {
  8644. sv[j] = MidPnt (x1, x2, j + 1);
  8645. h[j] = h[j - 1] / (LDBLE)9.0;
  8646. if (fabs (sv[j] - sv[j - 1]) <= md->G_TOL * fabs (sv[j]))
  8647. {
  8648. sv[j] *= md->surface_charge_ptr->grams * md->surface_charge_ptr->specific_area * md->alpha / F_C_MOL;
  8649. if ((x2 - 1) < 0.0)
  8650. sv[j] *= -1.0;
  8651. return (sv[j]);
  8652. }
  8653. if (j >= K_POLY - 1)
  8654. {
  8655. Polint(&h[j - K_POLY], &sv[j - K_POLY], K_POLY, 0.0, &ss, &dss);
  8656. if (fabs (dss) <= md->G_TOL * fabs (ss) || fabs (dss) < md->G_TOL)
  8657. {
  8658. ss *= md->surface_charge_ptr->grams * md->surface_charge_ptr->specific_area * md->alpha / F_C_MOL;
  8659. if ((x2 - 1) < 0.0)
  8660. ss *= -1.0;
  8661. return (ss);
  8662. }
  8663. }
  8664. }
  8665. throw exception();
  8666. }
  8667. //-----------------------------------------------------------------------------------------------------------
  8668. LDBLE ModelEngine::MidPnt(LDBLE x1, LDBLE x2, int n)
  8669. {
  8670. LDBLE xv, tnm, sum, del, ddel;
  8671. static LDBLE sv;
  8672. int it, j;
  8673. if (n == 1)
  8674. {
  8675. sv = (x2 - x1) * GFunction ((LDBLE)0.5 * (x1 + x2));
  8676. return (sv);
  8677. }
  8678. else
  8679. {
  8680. for (it = 1, j = 1; j < n - 1; j++)
  8681. it *= 3;
  8682. tnm = (LDBLE) it;
  8683. del = (x2 - x1) / (3 * tnm);
  8684. ddel = del + del;
  8685. xv = x1 + (LDBLE)0.5 * del;
  8686. sum = 0.0;
  8687. for (j = 1; j <= it; j++)
  8688. {
  8689. sum += GFunction (xv);
  8690. xv += ddel;
  8691. sum += GFunction (xv);
  8692. xv += del;
  8693. }
  8694. sv = (sv + (x2 - x1) * sum / tnm) / (LDBLE)3.0;
  8695. return sv;
  8696. }
  8697. }
  8698. //-----------------------------------------------------------------------------------------------------------
  8699. void ModelEngine::Polint(LDBLE * xa, LDBLE * ya, int n, LDBLE xv, LDBLE * yv, LDBLE * dy)
  8700. {
  8701. int i, m, ns;
  8702. LDBLE den, dif, dift, ho, hp, w;
  8703. LDBLE *c, *d;
  8704. ns = 1;
  8705. dif = fabs (xv - xa[1]);
  8706. c = NULL;
  8707. d = NULL;
  8708. try
  8709. {
  8710. c = new LDBLE [n + 1];
  8711. d = new LDBLE [n + 1];
  8712. }
  8713. catch(...)
  8714. {
  8715. delete [] c;
  8716. delete [] d;
  8717. throw;
  8718. }
  8719. for (i = 1; i <= n; i++)
  8720. {
  8721. dift = fabs (xv - xa[i]);
  8722. if (dift < dif)
  8723. {
  8724. ns = i;
  8725. dif = dift;
  8726. }
  8727. c[i] = ya[i];
  8728. d[i] = ya[i];
  8729. }
  8730. *yv = ya[ns--];
  8731. for (m = 1; m < n; m++)
  8732. {
  8733. for (i = 1; i <= n - m; i++)
  8734. {
  8735. ho = xa[i] - xv;
  8736. hp = xa[i + m] - xv;
  8737. w = c[i + 1] - d[i];
  8738. if ((den = ho - hp) == 0.0)
  8739. throw exception();
  8740. den = w / den;
  8741. d[i] = hp * den;
  8742. c[i] = ho * den;
  8743. }
  8744. if (2 * ns < (n - m))
  8745. *dy = c[ns + 1];
  8746. else
  8747. *dy = d[ns--];
  8748. *yv += *dy;
  8749. }
  8750. delete [] c;
  8751. delete [] d;
  8752. }
  8753. //-----------------------------------------------------------------------------------------------------------
  8754. LDBLE ModelEngine::GFunction (LDBLE x_value)
  8755. {
  8756. LDBLE sum, return_value;
  8757. int i, j;
  8758. LDBLE ln_x_value;
  8759. SurfaceCharge *sc_p;
  8760. SurfaceDiffLayer *sdl_p;
  8761. if (Equal(x_value, 1.0, md->G_TOL * 100))
  8762. return (0.0);
  8763. sum = 0.0;
  8764. ln_x_value = log(x_value);
  8765. sc_p = (*md->use.sur_p->charge)[0];
  8766. for (j = 0; j < sc_p->g->Count(); j++)
  8767. {
  8768. sdl_p = (*sc_p->g)[j];
  8769. sdl_p->psi_to_z = exp (ln_x_value * sdl_p->charge) - (LDBLE)1.0;
  8770. }
  8771. Species *s;
  8772. for (i = 0; i < md->s_x->Count(); i++)
  8773. {
  8774. s = (*md->s_x)[i];
  8775. if (s->type < H2O && s->z != 0.0)
  8776. {
  8777. for (j = 0; j < sc_p->g->Count(); j++)
  8778. {
  8779. sdl_p = (*sc_p->g)[j];
  8780. if (sdl_p->charge == s->z)
  8781. {
  8782. sum += s->moles * sdl_p->psi_to_z;
  8783. break;
  8784. }
  8785. }
  8786. }
  8787. }
  8788. if (sum < 0.0)
  8789. throw exception();
  8790. return_value = (exp (ln_x_value * md->z) - 1) / sqrt ((x_value * x_value * md->mass_water_aq_x * sum));
  8791. return (return_value);
  8792. }
  8793. //-----------------------------------------------------------------------------------------------------------
  8794. bool ModelEngine::CalcInitDonnanMusic()
  8795. {
  8796. int i, j, k;
  8797. int count_g, count_charge;
  8798. //char name[MAX_LENGTH];
  8799. LDBLE psi_avg, f_sinh, ratio_aq;
  8800. Species *s;
  8801. Unknown *u;
  8802. SurfaceDiffLayer *sdl_p;
  8803. SpeciesDiffLayer *spdl_p;
  8804. if (md->use.sur_p == NULL)
  8805. return true;
  8806. f_sinh = sqrt ((LDBLE)8000.0 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * (LDBLE)1000.0) * md->tk_x);
  8807. if (md->convergence_tolerance >= 1e-8)
  8808. {
  8809. md->G_TOL = (LDBLE)1e-9;
  8810. }
  8811. else
  8812. {
  8813. md->G_TOL = (LDBLE)1e-10;
  8814. }
  8815. // sum eq of each charge number in solution...
  8816. gd->charge_group.Clear();
  8817. ChargeGroup *cg_p = gd->charge_group.AddNew();
  8818. cg_p->z = 0.0;
  8819. cg_p->eq = 0.0;
  8820. count_g = 1;
  8821. for (i = 0; i < md->s_x->Count(); i++)
  8822. {
  8823. s = (*md->s_x)[i];
  8824. if (s->type > HPLUS)
  8825. continue;
  8826. for (k = 0; k < count_g; k++)
  8827. {
  8828. if (Equal(cg_p->z, s->z, md->G_TOL))
  8829. {
  8830. cg_p->eq += s->z * s->moles;
  8831. break;
  8832. }
  8833. }
  8834. if (k >= count_g)
  8835. {
  8836. cg_p = gd->charge_group.AddNew();
  8837. cg_p->z = s->z;
  8838. cg_p->eq = s->z * s->moles;
  8839. count_g++;
  8840. }
  8841. }
  8842. // calculate g for each surface...
  8843. count_charge = 0;
  8844. for (j = 0; j < count_unknowns; j++)
  8845. {
  8846. u = (*unknown_list)[j];
  8847. if (u->type != SURFACE_CB)
  8848. continue;
  8849. md->surface_charge_ptr = u->surface_charge;
  8850. u->surface_charge->g->Clear();
  8851. u->surface_charge->g->AddNew(count_g);
  8852. // find psi_avg that matches surface charge...
  8853. psi_avg = CalcPSIAvg(0);
  8854. // fill in g's
  8855. ratio_aq = md->surface_charge_ptr->mass_water / md->mass_water_aq_x;
  8856. for (k = 0; k < count_g; k++)
  8857. {
  8858. sdl_p = (*u->surface_charge->g)[k];
  8859. cg_p = gd->charge_group[k];
  8860. sdl_p->charge = cg_p->z;
  8861. sdl_p->g = exp (cg_p->z * psi_avg) - 1;
  8862. // save g for species
  8863. for (i = 0; i < md->s_x->Count(); i++)
  8864. {
  8865. s = (*md->s_x)[i];
  8866. if (Equal(cg_p->z, s->z, md->G_TOL))
  8867. {
  8868. spdl_p = (*s->diff_layer)[count_charge];
  8869. spdl_p->charge = u->surface_charge;
  8870. spdl_p->count_g = k;
  8871. spdl_p->g_moles = 0.0;
  8872. spdl_p->dg_g_moles = 0.0;
  8873. }
  8874. }
  8875. }
  8876. count_charge++;
  8877. }
  8878. return true;
  8879. }
  8880. //-----------------------------------------------------------------------------------------------------------
  8881. bool ModelEngine::GasPhaseCheck(GasPhase *gas_phase_ptr)
  8882. {
  8883. //
  8884. // Check for missing elements
  8885. //
  8886. int i, j;
  8887. LDBLE coef;
  8888. GasComp *gas_comp_ptr;
  8889. Master *master_ptr;
  8890. if (gas_phase_ptr == NULL)
  8891. return true;
  8892. // Check that all elements are in solution for phases with zero mass
  8893. coef = 1.0;
  8894. for (i = 0; i < gas_phase_ptr->comps->Count(); i++)
  8895. {
  8896. gas_comp_ptr = (*gas_phase_ptr->comps)[i];
  8897. eos_list->Clear();
  8898. parent_count = 0;
  8899. if (gas_comp_ptr->moles <= 0.0)
  8900. {
  8901. CopyToTempEOSList(gas_comp_ptr->phase->eos_list, coef);
  8902. for (j = 0; j < eos_list->Count(); j++)
  8903. {
  8904. master_ptr = (*eos_list)[j]->e->primary;
  8905. if (master_ptr->s == gd->s_hplus)
  8906. continue;
  8907. else if (master_ptr->s == gd->s_h2o)
  8908. continue;
  8909. else if (master_ptr->total > MIN_TOTAL)
  8910. continue;
  8911. else
  8912. {
  8913. return false;
  8914. }
  8915. }
  8916. }
  8917. }
  8918. return true;
  8919. }
  8920. //-----------------------------------------------------------------------------------------------------------
  8921. bool ModelEngine::PPAssemblageCheck (PPAssemblage *pp_assemblage_ptr)
  8922. {
  8923. //
  8924. // Check for missing elements
  8925. //
  8926. int i, j, k;
  8927. char token[MAX_LENGTH];
  8928. char *ptr;
  8929. PurePhase *pure_phase_ptr;
  8930. Master *master_ptr;
  8931. if (CheckPPAssemblage(pp_assemblage_ptr))
  8932. return true;
  8933. // Check that all elements are in solution for phases with zero mass
  8934. for (j = 0; j < pp_assemblage_ptr->pure_phases->Count(); j++)
  8935. {
  8936. pure_phase_ptr = (*pp_assemblage_ptr->pure_phases)[j];
  8937. eos_list->Clear();
  8938. parent_count = 0;
  8939. if (pure_phase_ptr->moles <= 0.0)
  8940. {
  8941. pure_phase_ptr->delta = 0.0;
  8942. if (!pure_phase_ptr->add_formula.IsEmpty())
  8943. {
  8944. pure_phase_ptr->add_formula.Copy(token);
  8945. ptr = &(token[0]);
  8946. if (!GetElementsInSpecies(&ptr, 1.0))
  8947. return false;
  8948. }
  8949. else
  8950. CopyToTempEOSList(pure_phase_ptr->phase->eos_list, 1.0);
  8951. for (i = 0; i < eos_list->Count(); i++)
  8952. {
  8953. master_ptr = (*eos_list)[i]->e->primary;
  8954. if (master_ptr->s == gd->s_hplus)
  8955. continue;
  8956. else if (master_ptr->s == gd->s_h2o)
  8957. continue;
  8958. else if (master_ptr->total > MIN_TOTAL)
  8959. continue;
  8960. else
  8961. {
  8962. sprintf (error_string,
  8963. "Element %s is contained in %s (which has 0.0 mass),"
  8964. "\t\nbut is not in solution or other phases.",
  8965. master_ptr->name.CharPtr(), pure_phase_ptr->phase->name.CharPtr());
  8966. //printf ("\nPHREEQC WARNING: %s\n", error_string);
  8967. //ToDo: Preparar para colocar a mensagem
  8968. // Make la's of all master species for the element small, so SI will be small
  8969. // and no mass transfer will be calculated
  8970. for (k = 0; k < gd->master_list.Count(); k++)
  8971. {
  8972. Master *m = gd->master_list[k];
  8973. if (m->e->primary == master_ptr)
  8974. {
  8975. m->s->la = (LDBLE)-9999.999;
  8976. }
  8977. }
  8978. }
  8979. }
  8980. }
  8981. }
  8982. return true;
  8983. }
  8984. //-----------------------------------------------------------------------------------------------------------
  8985. bool ModelEngine::SSAssemblageCheck(SSAssemblage *s_s_assemblage_ptr)
  8986. {
  8987. //
  8988. // Check for missing elements
  8989. //
  8990. int i, j, l;
  8991. Master *master_ptr;
  8992. SS *ss_p;
  8993. SSComp *ssc_p;
  8994. if (s_s_assemblage_ptr == NULL)
  8995. return true;
  8996. // Check that all elements are in solution for phases with zero mass
  8997. for (i = 0; i < s_s_assemblage_ptr->ss_list->Count(); i++)
  8998. {
  8999. ss_p = (*s_s_assemblage_ptr->ss_list)[i];
  9000. for (j = 0; j < ss_p->comps_list->Count(); j++)
  9001. {
  9002. ssc_p = (*ss_p->comps_list)[j];
  9003. eos_list->Clear();
  9004. parent_count = 0;
  9005. if (ssc_p->moles <= 0.0)
  9006. {
  9007. CopyToTempEOSList(ssc_p->phase->eos_list, 1.0);
  9008. for (l = 0; l < eos_list->Count(); l++)
  9009. {
  9010. master_ptr = (*eos_list)[l]->e->primary;
  9011. if (master_ptr->s == gd->s_hplus)
  9012. continue;
  9013. else if (master_ptr->s == gd->s_h2o)
  9014. continue;
  9015. else if (master_ptr->total > MIN_TOTAL_SS)
  9016. continue;
  9017. else
  9018. return false;
  9019. }
  9020. }
  9021. }
  9022. }
  9023. return true;
  9024. }
  9025. //-----------------------------------------------------------------------------------------------------------
  9026. bool ModelEngine::CalcInitDonnan()
  9027. {
  9028. int i, j, k;
  9029. int count_g, count_charge;
  9030. //char name[MAX_LENGTH];
  9031. LDBLE f_psi, surf_chrg_eq, psi_avg, f_sinh, A_surf, ratio_aq, la;
  9032. SurfaceDiffLayer *sdl_p;
  9033. SpeciesDiffLayer *spdl_p;
  9034. if (md->use.sur_p == NULL)
  9035. return true;
  9036. if (md->use.sur_p->type == CD_MUSIC)
  9037. return CalcInitDonnanMusic();
  9038. f_sinh = sqrt ((LDBLE)8000.0 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * (LDBLE)1000.0) * md->tk_x * md->mu_x);
  9039. if (md->convergence_tolerance >= 1e-8)
  9040. md->G_TOL = (LDBLE)1e-9;
  9041. else
  9042. md->G_TOL = (LDBLE)1e-13;
  9043. // sum eq of each charge number in solution...
  9044. gd->charge_group.Clear();
  9045. ChargeGroup *cg_p = gd->charge_group.AddNew();
  9046. cg_p->z = 0.0;
  9047. cg_p->eq = 0.0;
  9048. count_g = 1;
  9049. Species *s;
  9050. for (i = 0; i < md->s_x->Count(); i++)
  9051. {
  9052. s = (*md->s_x)[i];
  9053. if (s->type > HPLUS)
  9054. continue;
  9055. for (k = 0; k < count_g; k++)
  9056. {
  9057. if (Equal(cg_p->z, s->z, md->G_TOL))
  9058. {
  9059. cg_p->eq += s->z * s->moles;
  9060. break;
  9061. }
  9062. }
  9063. if (k >= count_g)
  9064. {
  9065. cg_p = gd->charge_group.AddNew();
  9066. cg_p->z = s->z;
  9067. cg_p->eq = s->z * s->moles;
  9068. count_g++;
  9069. }
  9070. }
  9071. // calculate g for each surface...
  9072. count_charge = 0;
  9073. Unknown *u;
  9074. for (j = 0; j < count_unknowns; j++)
  9075. {
  9076. u = (*unknown_list)[j];
  9077. if (u->type != SURFACE_CB)
  9078. continue;
  9079. md->surface_charge_ptr = u->surface_charge;
  9080. u->surface_charge->g->Clear();
  9081. u->surface_charge->g->AddNew(count_g);
  9082. // find surface charge from potential...
  9083. A_surf = u->surface_charge->specific_area * u->surface_charge->grams;
  9084. la = (*u->master)[0]->s->la;
  9085. f_psi = la * md->LOG_10;
  9086. surf_chrg_eq = A_surf * f_sinh * sinh (f_psi) / F_C_MOL;
  9087. // find psi_avg that matches surface charge...
  9088. psi_avg = CalcPSIAvg(0);
  9089. // fill in g's
  9090. ratio_aq = md->surface_charge_ptr->mass_water / md->mass_water_aq_x;
  9091. for (k = 0; k < count_g; k++)
  9092. {
  9093. sdl_p = (*u->surface_charge->g)[k];
  9094. cg_p = gd->charge_group[k];
  9095. sdl_p->charge = cg_p->z;
  9096. sdl_p->g = ratio_aq * (exp (-cg_p->z * psi_avg) - 1);
  9097. if (md->use.sur_p->only_counter_ions && ((surf_chrg_eq < 0 && cg_p->z < 0) || (surf_chrg_eq > 0 && cg_p->z > 0)))
  9098. sdl_p->g = -ratio_aq;
  9099. if (sdl_p->g != 0)
  9100. sdl_p->dg = -A_surf * f_sinh * cosh (f_psi) / (cg_p->eq * F_C_MOL);
  9101. else
  9102. sdl_p->dg = -cg_p->z;
  9103. // save g for species
  9104. for (i = 0; i < md->s_x->Count(); i++)
  9105. {
  9106. s = (*md->s_x)[i];
  9107. if (Equal(cg_p->z, s->z, md->G_TOL))
  9108. {
  9109. spdl_p = (*s->diff_layer)[count_charge];
  9110. spdl_p->charge = u->surface_charge;
  9111. spdl_p->count_g = k;
  9112. spdl_p->g_moles = 0.0;
  9113. spdl_p->dg_g_moles = 0.0;
  9114. }
  9115. }
  9116. }
  9117. count_charge++;
  9118. }
  9119. return true;
  9120. }
  9121. //-----------------------------------------------------------------------------------------------------------
  9122. bool ModelEngine::CalcAllDonnanMusic()
  9123. /* ---------------------------------------------------------------------- */
  9124. {
  9125. int i, j, k;
  9126. int count_g, count_charge;
  9127. bool converge;
  9128. //char name[MAX_LENGTH];
  9129. LDBLE new_g, f_psi, surf_chrg_eq, psi_avg, f_sinh, A_surf, ratio_aq;
  9130. LDBLE cz, cm, cp, la;
  9131. if (md->use.sur_p == NULL)
  9132. return true;
  9133. f_sinh = sqrt ((LDBLE)8000.0 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * (LDBLE)1000.0) * md->tk_x * md->mu_x);
  9134. cz = cm = 1.0;
  9135. cp = 1.0;
  9136. // calculate g for each surface...
  9137. converge = true;
  9138. count_charge = 0;
  9139. Unknown *u;
  9140. for (j = 0; j < count_unknowns; j++)
  9141. {
  9142. u = (*unknown_list)[j];
  9143. if (u->type != SURFACE_CB)
  9144. continue;
  9145. md->surface_charge_ptr = u->surface_charge;
  9146. // sum eq of each charge number in solution...
  9147. count_g = u->surface_charge->g->Count();
  9148. for (i = 0; i < count_g; i++)
  9149. gd->charge_group[i]->eq = 0.0;
  9150. Species *s_x;
  9151. for (i = 0; i < md->s_x->Count(); i++)
  9152. {
  9153. s_x = (*md->s_x)[i];
  9154. if (s_x->type > HPLUS)
  9155. continue;
  9156. for (k = 0; k < count_g; k++)
  9157. {
  9158. if (Equal(gd->charge_group[k]->z, s_x->z, md->G_TOL))
  9159. {
  9160. gd->charge_group[k]->eq += s_x->z * s_x->moles;
  9161. break;
  9162. }
  9163. }
  9164. }
  9165. // find surface charge from potential...
  9166. A_surf = u->surface_charge->specific_area * u->surface_charge->grams;
  9167. la = (*(*unknown_list)[j + 2]->master)[0]->s->la;
  9168. f_psi = la * md->LOG_10; // -FPsi/RT
  9169. f_psi = f_psi / 2;
  9170. surf_chrg_eq = A_surf * f_sinh * sinh (f_psi) / F_C_MOL;
  9171. psi_avg = CalcPSIAvg(surf_chrg_eq);
  9172. // fill in g's
  9173. ratio_aq = md->surface_charge_ptr->mass_water / md->mass_water_aq_x;
  9174. SurfaceDiffLayer *sdl_p;
  9175. for (k = 0; k < count_g; k++)
  9176. {
  9177. sdl_p = (*u->surface_charge->g)[k];
  9178. sdl_p->charge = gd->charge_group[k]->z;
  9179. new_g = exp (gd->charge_group[k]->z * psi_avg) - 1;
  9180. if (new_g < -ratio_aq)
  9181. new_g = -ratio_aq + md->G_TOL * (LDBLE)1e-5;
  9182. if (fabs (new_g) >= 1)
  9183. {
  9184. if (fabs ((new_g - sdl_p->g) / new_g) > md->convergence_tolerance)
  9185. converge = FALSE;
  9186. }
  9187. else
  9188. {
  9189. if (fabs (new_g - sdl_p->g) > md->convergence_tolerance)
  9190. converge = FALSE;
  9191. }
  9192. sdl_p->g = new_g;
  9193. // save g for species
  9194. SpeciesDiffLayer *spdl_p;
  9195. for (i = 0; i < md->s_x->Count(); i++)
  9196. {
  9197. s_x = (*md->s_x)[i];
  9198. spdl_p = (*s_x->diff_layer)[count_charge];
  9199. if (Equal(gd->charge_group[k]->z, s_x->z, md->G_TOL))
  9200. {
  9201. spdl_p->charge = u->surface_charge;
  9202. spdl_p->count_g = k;
  9203. }
  9204. }
  9205. }
  9206. count_charge++;
  9207. }
  9208. return (converge);
  9209. }
  9210. //-----------------------------------------------------------------------------------------------------------
  9211. CONVERGE_RESULT ModelEngine::ExecuteSurfaceModel()
  9212. {
  9213. //
  9214. // Use extra iterative loop to converge on g_factors
  9215. //
  9216. int i;
  9217. bool result;
  9218. //struct solution *solution_ptr;
  9219. LDBLE prev_aq_x;
  9220. // Allocate space for g factors for diffuse layer in surface complexation
  9221. if (md->dl_type_x != NO_DL)
  9222. {
  9223. Species *s;
  9224. for (i = 0; i < gd->species_list.Count(); i++)
  9225. {
  9226. s = gd->species_list[i];
  9227. s->diff_layer->Clear();
  9228. }
  9229. }
  9230. SurfaceCharge *sc_p;
  9231. if (md->state >= REACTION && md->dl_type_x != NO_DL)
  9232. {
  9233. md->mass_water_bulk_x = md->use.sol_p->mass_water;
  9234. for (i = 0; i < md->use.sur_p->charge->Count(); i++)
  9235. {
  9236. sc_p = (*md->use.sur_p->charge)[i];
  9237. md->mass_water_bulk_x += sc_p->mass_water;
  9238. if (md->use.sur_p->debye_lengths > 0)
  9239. sc_p->mass_water = 0.0;
  9240. }
  9241. }
  9242. if (!Prepare())
  9243. return ERROR_CR;
  9244. if (!KTemp(md->tc_x))
  9245. return ERROR_CR;
  9246. if (md->use.sur_p->dl_type == DONNAN_DL)
  9247. {
  9248. if (!InitialSurfaceWater ())
  9249. return ERROR_CR;
  9250. if (!CalcInitDonnan ())
  9251. return ERROR_CR;
  9252. }
  9253. else
  9254. if (!CalcInitG ())
  9255. return ERROR_CR;
  9256. /*
  9257. if (md->state >= REACTION && md->use.sur_p->new_def == FALSE)
  9258. {
  9259. set (FALSE);
  9260. }
  9261. else
  9262. {
  9263. set (TRUE);
  9264. }
  9265. */
  9266. Set(true);
  9267. if (ExecuteModel() == ERROR_CR)
  9268. return (ERROR_CR);
  9269. md->g_iterations = 0;
  9270. if (md->use.sur_p->dl_type == DONNAN_DL)
  9271. {
  9272. do
  9273. {
  9274. md->g_iterations++;
  9275. prev_aq_x = md->mass_water_aq_x;
  9276. if(!KTemp(md->tc_x))
  9277. return ERROR_CR;
  9278. result = Gammas(md->mu_x);
  9279. //d.PrintGammasToFile("ExecuteSurfaceModel-1", md->s_x);
  9280. if(!result)
  9281. return ERROR_CR;
  9282. if(!Molalities(true))
  9283. return ERROR_CR;
  9284. if(!MBSums ())
  9285. return ERROR_CR;
  9286. if(ExecuteModel() == ERROR_CR)
  9287. return (ERROR_CR);
  9288. if (!md->use.sur_p->related_phases) //&& !md->use.sur_p->related_rate)
  9289. if (!InitialSurfaceWater())
  9290. return ERROR_CR;
  9291. }
  9292. while ((!CalcAllDonnan() || fabs(1 - prev_aq_x / md->mass_water_aq_x) > 1e-6) && md->g_iterations < md->itmax);
  9293. }
  9294. else
  9295. {
  9296. do
  9297. {
  9298. md->g_iterations++;
  9299. if (!KTemp(md->tc_x))
  9300. return ERROR_CR;
  9301. result = Gammas(md->mu_x);
  9302. //d.PrintGammasToFile("ExecuteSurfaceModel-2", md->s_x);
  9303. if (!result)
  9304. return ERROR_CR;
  9305. if (!Molalities(true))
  9306. return ERROR_CR;
  9307. if (!MBSums())
  9308. return ERROR_CR;
  9309. if (ExecuteModel() == ERROR_CR)
  9310. return (ERROR_CR);
  9311. if (!md->use.sur_p->related_phases)
  9312. InitialSurfaceWater();
  9313. }
  9314. while (!CalcAllG() && md->g_iterations < md->itmax);
  9315. }
  9316. if (md->g_iterations >= md->itmax)
  9317. return ERROR_CR;
  9318. return OK_CR;
  9319. }
  9320. //-----------------------------------------------------------------------------------------------------------
  9321. bool ModelEngine::AddSolution(Solution *sol_p, LDBLE extensive, LDBLE intensive)
  9322. {
  9323. //
  9324. // Accumulate solution data in master->totals and _x variables.
  9325. //
  9326. // extensive is multiplication factor for solution
  9327. // intensive is fraction of all multiplication factors for all solutions
  9328. //
  9329. int i;
  9330. String value, comment;
  9331. // Add solution to global variables
  9332. md->tc_x += sol_p->tc * intensive;
  9333. md->ph_x += sol_p->ph * intensive;
  9334. md->solution_pe_x += sol_p->solution_pe * intensive;
  9335. md->mu_x += sol_p->mu * intensive;
  9336. md->ah2o_x += sol_p->ah2o * intensive;
  9337. md->density_x += sol_p->density * intensive;
  9338. md->total_h_x += sol_p->total_h * extensive;
  9339. md->total_o_x += sol_p->total_o * extensive;
  9340. md->cb_x += sol_p->cb * extensive;
  9341. md->mass_water_aq_x += sol_p->mass_water * extensive;
  9342. #ifdef DEBUG_AddSolution
  9343. d.addsolution_count++;
  9344. fprintf(d.addsolution_f, "%d 1 tc_x: %.20e ph_x: %.20e solution_pe_x: %.20e ", d.addsolution_count, md->tc_x, md->ph_x, md->solution_pe_x);
  9345. fprintf(d.addsolution_f, "mu_x: %.20e ah2o_x: %.20e density_x: %.20e ", md->mu_x, md->ah2o_x, md->density_x);
  9346. fprintf(d.addsolution_f, "total_h_x: %.20e total_o_x: %.20e cb_x: %.20e mass_water_aq_x: %.20e\n", md->total_h_x, md->total_o_x, md->cb_x, md->mass_water_aq_x);
  9347. #endif
  9348. Master *m;
  9349. Conc *c;
  9350. // Copy totals data into primary master species
  9351. for (i = 0; i < sol_p->totals->Count(); i++)
  9352. {
  9353. c = (*md->use.sol_p->totals)[i];
  9354. m = MasterPrimarySearch(c->name);
  9355. if (m == NULL)
  9356. return false;
  9357. m->total += c->moles * extensive;
  9358. #ifdef DEBUG_AddSolution
  9359. fprintf(d.addsolution_f, "%d 2 c->name: %s c->moles: %.20e extensive: %.20e m->total: %.20e\n", d.addsolution_count, c->name.CharPtr(), c->moles, extensive, m->total);
  9360. #endif
  9361. }
  9362. // Accumulate initial guesses for activities
  9363. MasterActivity *ma;
  9364. for (i = 0; i < md->use.sol_p->ma->Count(); i++)
  9365. {
  9366. ma = (*md->use.sol_p->ma)[i];
  9367. if (!ma->description.IsEmpty())
  9368. {
  9369. m = gd->master_list.Search(&ma->description, true);
  9370. if (m != NULL)
  9371. {
  9372. m->s->la += ma->la * intensive;
  9373. #ifdef DEBUG_AddSolution
  9374. fprintf(d.addsolution_f, "%d 3 ma->description: %s ma->la: %.20e intensive: %.20e m->s->la: %.20e\n", d.addsolution_count, ma->description.CharPtr(), ma->la, intensive, m->s->la);
  9375. #endif
  9376. }
  9377. }
  9378. }
  9379. return true;
  9380. }
  9381. //-----------------------------------------------------------------------------------------------------------
  9382. Master *ModelEngine::MasterPrimarySearch(String str)
  9383. {
  9384. char *ptr, token[DEFAULT_STR_LENGTH];
  9385. String element;
  9386. str.Copy(token);
  9387. ptr = &token[0];
  9388. // find element name
  9389. GetElement(&ptr, element);
  9390. // return master species
  9391. return gd->master_list.Search(&element, true);
  9392. }
  9393. //-----------------------------------------------------------------------------------------------------------
  9394. /*
  9395. bool ModelEngine::AddReaction (Irrev *irrev_ptr, int step_number, LDBLE step_fraction)
  9396. {
  9397. //
  9398. // Add irreversible reaction
  9399. //
  9400. int i;
  9401. char c;
  9402. Master *master_ptr;
  9403. // Calculate and save reaction
  9404. if (irrev_ptr->elts->Count() <= 0)
  9405. {
  9406. if (!ReactionCalc(irrev_ptr))
  9407. return false;
  9408. }
  9409. // Step size
  9410. if (!md->incremental_reactions)
  9411. {
  9412. if (irrev_ptr->count_steps > 0)
  9413. {
  9414. if (step_number > irrev_ptr->count_steps)
  9415. md->step_x = irrev_ptr->steps[irrev_ptr->count_steps - 1];
  9416. else
  9417. md->step_x = irrev_ptr->steps[step_number - 1];
  9418. }
  9419. else if (irrev_ptr->count_steps < 0)
  9420. {
  9421. if (step_number > -irrev_ptr->count_steps)
  9422. md->step_x = irrev_ptr->steps[0];
  9423. else
  9424. md->step_x = irrev_ptr->steps[0] * ((LDBLE) step_number) / ((LDBLE) (-irrev_ptr->count_steps));
  9425. }
  9426. else
  9427. md->step_x = 0.0;
  9428. }
  9429. else
  9430. {
  9431. // Incremental reactions
  9432. if (irrev_ptr->count_steps > 0)
  9433. {
  9434. if (step_number > irrev_ptr->count_steps)
  9435. md->step_x = irrev_ptr->steps[irrev_ptr->count_steps - 1];
  9436. else
  9437. md->step_x = irrev_ptr->steps[step_number - 1];
  9438. }
  9439. else if (irrev_ptr->count_steps < 0)
  9440. {
  9441. if (step_number > -irrev_ptr->count_steps)
  9442. md->step_x = 0;
  9443. else
  9444. md->step_x = irrev_ptr->steps[0] / ((LDBLE) (-irrev_ptr->count_steps));
  9445. }
  9446. else
  9447. md->step_x = 0.0;
  9448. }
  9449. // Convert units
  9450. //ToDo: Alterar essa parte
  9451. c = irrev_ptr->units[0];
  9452. if (c == 'm')
  9453. {
  9454. md->step_x *= 1e-3;
  9455. }
  9456. else if (c == 'u')
  9457. {
  9458. md->step_x *= 1e-6;
  9459. }
  9460. else if (c == 'n')
  9461. {
  9462. md->step_x *= 1e-9;
  9463. }
  9464. // Add reaction to totals
  9465. ElementOfSpecies *eos_p;
  9466. for (i = 0; i < irrev_ptr->elts->Count(); i++)
  9467. {
  9468. eos_p = (*irrev_ptr->elts)[i];
  9469. master_ptr = eos_p->e->primary;
  9470. if (master_ptr->s == gd->s_hplus)
  9471. md->total_h_x += eos_p->coef * md->step_x * step_fraction;
  9472. else if (master_ptr->s == gd->s_h2o)
  9473. md->total_o_x += eos_p->coef * md->step_x * step_fraction;
  9474. else
  9475. master_ptr->total += eos_p->coef * md->step_x * step_fraction;
  9476. }
  9477. return true;
  9478. }
  9479. */
  9480. //-----------------------------------------------------------------------------------------------------------
  9481. bool ModelEngine::AddExchange (Exchange *exchange_ptr)
  9482. {
  9483. //
  9484. // Accumulate exchange data in master->totals and _x variables.
  9485. //
  9486. int i, j;
  9487. Master *master_ptr;
  9488. if (exchange_ptr == NULL)
  9489. return true;
  9490. #ifdef DEBUG_AddExchange
  9491. d.addexchange_count++;
  9492. #endif
  9493. // Add element concentrations on exchanger to master species totals
  9494. ExchComp *ec_p;
  9495. ElementOfSpecies *eos_p;
  9496. for (i = 0; i < exchange_ptr->comps->Count(); i++)
  9497. {
  9498. ec_p = (*exchange_ptr->comps)[i];
  9499. for (j = 0; j < ec_p->totals->Count(); j++)
  9500. {
  9501. eos_p = (*ec_p->totals)[j];
  9502. master_ptr = eos_p->e->primary;
  9503. if (master_ptr->s == gd->s_hplus)
  9504. {
  9505. md->total_h_x += eos_p->coef;
  9506. #ifdef DEBUG_AddExchange
  9507. fprintf(d.addexchange_f, "%d 1 i: %d j: %d master_ptr->s->name: %s ", d.addexchange_count, i, j, master_ptr->s->name.CharPtr());
  9508. fprintf(d.addexchange_f, "eos_p->coef: %.20e md->total_h_x: %.20e\n", eos_p->coef, md->total_h_x);
  9509. #endif
  9510. }
  9511. else if (master_ptr->s == gd->s_h2o)
  9512. {
  9513. md->total_o_x += eos_p->coef;
  9514. #ifdef DEBUG_AddExchange
  9515. fprintf(d.addexchange_f, "%d 2 i: %d j: %d master_ptr->s->name: %s ", d.addexchange_count, i, j, master_ptr->s->name.CharPtr());
  9516. fprintf(d.addexchange_f, "eos_p->coef: %.20e md->total_o_x: %.20e\n", eos_p->coef, md->total_o_x);
  9517. #endif
  9518. }
  9519. else
  9520. {
  9521. master_ptr->total += eos_p->coef;
  9522. #ifdef DEBUG_AddExchange
  9523. fprintf(d.addexchange_f, "%d 3 i: %d j: %d master_ptr->s->name: %s ", d.addexchange_count, i, j, master_ptr->s->name.CharPtr());
  9524. fprintf(d.addexchange_f, "eos_p->coef: %.20e master_ptr->total: %.20e\n", eos_p->coef, master_ptr->total);
  9525. #endif
  9526. }
  9527. }
  9528. }
  9529. if (exchange_ptr->new_def)
  9530. {
  9531. for (i = 0; i < gd->master_list.Count(); i++)
  9532. {
  9533. master_ptr = gd->master_list[i];
  9534. if (master_ptr->type == EX && master_ptr->total > 0)
  9535. {
  9536. master_ptr->s->la = log10 ((LDBLE)0.1 * master_ptr->total);
  9537. #ifdef DEBUG_AddExchange
  9538. fprintf(d.addexchange_f, "%d 4 i: %d master_ptr->name: %s master_ptr->total: %.20e master_ptr->s->la: %.20e\n", d.addexchange_count, i, master_ptr->name.CharPtr(), master_ptr->total, master_ptr->s->la);
  9539. #endif
  9540. }
  9541. }
  9542. }
  9543. else
  9544. {
  9545. for (i = 0; i < exchange_ptr->comps->Count(); i++)
  9546. {
  9547. ec_p = (*exchange_ptr->comps)[i];
  9548. ec_p->master->s->la = ec_p->la;
  9549. md->cb_x += ec_p->charge_balance;
  9550. #ifdef DEBUG_AddExchange
  9551. fprintf(d.addexchange_f, "%d 4 i: %d ec_p->name: %s ec_p->master->s->la: %.20e ec_p->la: %.20e ec_p->charge_balance: %.20e md->cb_x: %.20e\n", d.addexchange_count, i, ec_p->name.CharPtr(), ec_p->master->s->la, ec_p->la, ec_p->charge_balance, md->cb_x);
  9552. #endif
  9553. }
  9554. }
  9555. return true;
  9556. }
  9557. //-----------------------------------------------------------------------------------------------------------
  9558. bool ModelEngine::AddSurface(Surface *surface_ptr)
  9559. {
  9560. //
  9561. // Accumulate surface data in master->totals and _x variables.
  9562. //
  9563. int i, j;
  9564. Master *master_ptr;
  9565. if (surface_ptr == NULL)
  9566. return true;
  9567. // Add element concentrations on surface to master species totals
  9568. md->dl_type_x = surface_ptr->dl_type;
  9569. SurfaceComp *sc_p;
  9570. ElementOfSpecies *eos_p;
  9571. SurfaceCharge *sch_p;
  9572. for (i = 0; i < surface_ptr->comps->Count(); i++)
  9573. {
  9574. sc_p = (*surface_ptr->comps)[i];
  9575. if (surface_ptr->type == NO_EDL)
  9576. md->cb_x += sc_p->cb;
  9577. if (!surface_ptr->new_def)
  9578. sc_p->master->s->la = sc_p->la;
  9579. // Add surface and specifically sorbed elements
  9580. for (j = 0; j < sc_p->totals->Count(); j++)
  9581. {
  9582. eos_p = (*sc_p->totals)[j];
  9583. master_ptr = eos_p->e->primary;
  9584. if (master_ptr == NULL)
  9585. return false;
  9586. if (master_ptr->s == gd->s_hplus)
  9587. md->total_h_x += eos_p->coef;
  9588. else if (master_ptr->s == gd->s_h2o)
  9589. md->total_o_x += eos_p->coef;
  9590. else
  9591. master_ptr->total += eos_p->coef;
  9592. }
  9593. }
  9594. if (surface_ptr->type != DDL && surface_ptr->type != CD_MUSIC)
  9595. return true;
  9596. for (i = 0; i < surface_ptr->charge->Count(); i++)
  9597. {
  9598. sch_p = (*surface_ptr->charge)[i];
  9599. if (surface_ptr->type == DDL || surface_ptr->type == CD_MUSIC)
  9600. md->cb_x += sch_p->charge_balance;
  9601. if (!surface_ptr->new_def)
  9602. {
  9603. master_ptr = SurfaceGetPSIMaster(sch_p->name, SURF_PSI);
  9604. master_ptr->s->la = sch_p->la_psi;
  9605. }
  9606. // Add diffuse layer elements (including water in Debye layer)
  9607. if (surface_ptr->dl_type != NO_DL && !surface_ptr->new_def)
  9608. {
  9609. for (j = 0; j < sch_p->diffuse_layer_totals->Count(); j++)
  9610. {
  9611. eos_p = (*sch_p->diffuse_layer_totals)[j];
  9612. master_ptr = eos_p->e->primary;
  9613. if (master_ptr->s == gd->s_hplus)
  9614. md->total_h_x += eos_p->coef;
  9615. else if (master_ptr->s == gd->s_h2o)
  9616. md->total_o_x += eos_p->coef;
  9617. else
  9618. master_ptr->total += eos_p->coef;
  9619. }
  9620. }
  9621. }
  9622. return true;
  9623. }
  9624. //-----------------------------------------------------------------------------------------------------------
  9625. bool ModelEngine::AddGasPhase(GasPhase *gas_phase_ptr)
  9626. {
  9627. // Accumulate gas data in master->totals and _x variables.
  9628. int i;
  9629. GasComp *gas_comp_ptr;
  9630. Master *master_ptr;
  9631. ElementOfSpecies *eos_p;
  9632. if (gas_phase_ptr == NULL)
  9633. return true;
  9634. // calculate reaction
  9635. eos_list->Clear();
  9636. parent_count = 0;
  9637. for (i = 0; i < gas_phase_ptr->comps->Count(); i++)
  9638. {
  9639. gas_comp_ptr = (*gas_phase_ptr->comps)[i];
  9640. CopyToTempEOSList(gas_comp_ptr->phase->eos_list, gas_comp_ptr->moles);
  9641. }
  9642. // Sort elements in reaction and combine
  9643. CombineElements();
  9644. // Add gas elements to totals
  9645. for (i = 0; i < eos_list->Count(); i++)
  9646. {
  9647. eos_p = (*eos_list)[i];
  9648. master_ptr = eos_p->e->primary;
  9649. if (master_ptr->s == gd->s_hplus)
  9650. md->total_h_x += eos_p->coef;
  9651. else if (master_ptr->s == gd->s_h2o)
  9652. md->total_o_x += eos_p->coef;
  9653. else
  9654. master_ptr->total += eos_p->coef;
  9655. }
  9656. return (OK);
  9657. }
  9658. //-----------------------------------------------------------------------------------------------------------
  9659. /*
  9660. bool ModelEngine::ReactionCalc(Irrev *irrev_ptr)
  9661. {
  9662. //
  9663. // Go through irreversible reaction initially to
  9664. // determine a list of elements and amounts in
  9665. // the reaction.
  9666. //
  9667. int i; //, j;
  9668. //LDBLE coef;
  9669. char token[DEFAULT_STR_LENGTH], *ptr;
  9670. Phase *phase_ptr;
  9671. NameCoef *nc_p;
  9672. ElementOfSpecies *eos_p;
  9673. // Go through list and generate list of elements and coefficient of elements in reaction
  9674. eos_list->Clear();
  9675. parent_count = 0;
  9676. for (i = 0; i < irrev_ptr->list->Count(); i++)
  9677. {
  9678. nc_p = (*irrev_ptr->list)[i];
  9679. phase_ptr = gd->phase_list.Search(nc_p->name, true);
  9680. // Reactant is a pure phase, copy formula into token
  9681. if (phase_ptr != NULL)
  9682. CopyToTempEOSList(phase_ptr->eos_list, nc_p->coef);
  9683. else
  9684. {
  9685. nc_p->name.Copy(token);
  9686. ptr = &(token[0]);
  9687. GetElementsInSpecies(&ptr, nc_p->coef);
  9688. }
  9689. }
  9690. // Check that all elements are in database
  9691. for (i = 0; i < eos_list->Count(); i++)
  9692. {
  9693. eos_p = (*eos_list)[i];
  9694. if (eos_p->e->master == NULL)
  9695. return false;
  9696. }
  9697. SaveEOSList(irrev_ptr->elts);
  9698. return true;
  9699. }
  9700. */
  9701. //-----------------------------------------------------------------------------------------------------------
  9702. bool ModelEngine::SumDiffuseLayer(SurfaceCharge *surface_charge_ptr1)
  9703. {
  9704. int i, j, count_g;
  9705. LDBLE mass_water_surface;
  9706. LDBLE molality, moles_excess, moles_surface;
  9707. SurfaceCharge *sc_p;
  9708. Species *s;
  9709. SpeciesDiffLayer *sdl_p;
  9710. SurfaceDiffLayer *surdl_p;
  9711. if (md->use.sur_p == NULL)
  9712. return true;
  9713. // Find position of component in list of components
  9714. i = 0;
  9715. for (j = 0; j < md->use.sur_p->charge->Count(); j++)
  9716. {
  9717. sc_p = (*md->use.sur_p->charge)[j];
  9718. if (sc_p == surface_charge_ptr1)
  9719. {
  9720. i = j;
  9721. break;
  9722. }
  9723. }
  9724. if (j >= md->use.sur_p->charge->Count())
  9725. return false;
  9726. // Loop through all surface components, calculate each H2O surface (diffuse layer),
  9727. // H2O aq, and H2O bulk (diffuse layers plus aqueous).
  9728. eos_list->Clear();
  9729. parent_count = 0;
  9730. mass_water_surface = surface_charge_ptr1->mass_water;
  9731. for (j = 0; j < md->s_x->Count(); j++)
  9732. {
  9733. s = (*md->s_x)[j];
  9734. sdl_p = (*s->diff_layer)[i];
  9735. if (s->type > HPLUS)
  9736. continue;
  9737. molality = Under(s->lm);
  9738. count_g = sdl_p->count_g;
  9739. surdl_p = (*surface_charge_ptr1->g)[count_g];
  9740. moles_excess = md->mass_water_aq_x * molality * surdl_p->g;
  9741. moles_surface = mass_water_surface * molality + moles_excess;
  9742. // Accumulate elements in diffuse layer
  9743. CopyToTempEOSList(s->eos_list, moles_surface);
  9744. }
  9745. CopyToTempEOSList(gd->s_h2o->eos_list, mass_water_surface / md->gfw_water);
  9746. CombineElements();
  9747. return true;
  9748. }
  9749. //-----------------------------------------------------------------------------------------------------------
  9750. void ModelEngine::CopyUse(int i)
  9751. {
  9752. int index;
  9753. Solution *copy_sol, *ori_sol;
  9754. PPAssemblage *copy_ppa, *ori_ppa;
  9755. Exchange *copy_exc, *ori_exc;
  9756. Surface *copy_sur, *ori_sur;
  9757. GasPhase *copy_gas, *ori_gas;
  9758. SSAssemblage *copy_ssa, *ori_ssa;
  9759. Use *use = &md->use;
  9760. Solution *temp;
  9761. if (use->sol_list->Count() <= i)
  9762. {
  9763. for (index = use->sol_list->Count(); index <= i; index++)
  9764. {
  9765. temp = use->sol_list->AddNew();
  9766. temp->gd = gd;
  9767. temp->InitPE();
  9768. }
  9769. }
  9770. copy_sol = (*use->sol_list)[i];
  9771. ori_sol = (*use->sol_list)[use->sol_number];
  9772. ori_sol->CopyTo(copy_sol);
  9773. // Find pure phase assemblage
  9774. if (use->ppa_in)
  9775. {
  9776. if (use->ppa_list->Count() <= i)
  9777. {
  9778. for (index = use->ppa_list->Count(); index <= i; index++)
  9779. use->ppa_list->AddNew();
  9780. }
  9781. copy_ppa = (*use->ppa_list)[i];
  9782. ori_ppa = (*use->ppa_list)[use->ppa_number];
  9783. ori_ppa->CopyTo(copy_ppa);
  9784. }
  9785. // Find exchange
  9786. if (use->exc_in)
  9787. {
  9788. if (use->exc_list->Count() <= i)
  9789. {
  9790. for (index = use->exc_list->Count(); index <= i; index++)
  9791. use->exc_list->AddNew();
  9792. }
  9793. copy_exc = (*use->exc_list)[i];
  9794. ori_exc = (*use->exc_list)[use->exc_number];
  9795. ori_exc->CopyTo(copy_exc);
  9796. }
  9797. // Find surface
  9798. md->dl_type_x = NO_DL;
  9799. if (use->sur_in)
  9800. {
  9801. if (use->sur_list->Count() <= i)
  9802. {
  9803. for (index = use->sur_list->Count(); index <= i; index++)
  9804. use->sur_list->AddNew();
  9805. }
  9806. copy_sur = (*use->sur_list)[i];
  9807. ori_sur = (*use->sur_list)[use->sur_number];
  9808. ori_sur->CopyTo(copy_sur);
  9809. }
  9810. // Find gas
  9811. if (use->gas_in)
  9812. {
  9813. if (use->gas_list->Count() <= i)
  9814. {
  9815. for (index = use->gas_list->Count(); index <= i; index++)
  9816. use->gas_list->AddNew();
  9817. }
  9818. copy_gas = (*use->gas_list)[i];
  9819. ori_gas = (*use->gas_list)[use->gas_number];
  9820. ori_gas->CopyTo(copy_gas);
  9821. }
  9822. // Find solid solution
  9823. if (use->ssa_in)
  9824. {
  9825. if (use->ssa_list->Count() <= i)
  9826. {
  9827. for (index = use->ssa_list->Count(); index <= i; index++)
  9828. use->ssa_list->AddNew();
  9829. }
  9830. copy_ssa = (*use->ssa_list)[i];
  9831. ori_ssa = (*use->ssa_list)[use->ssa_number];
  9832. ori_ssa->CopyTo(copy_ssa);
  9833. }
  9834. }
  9835. //-----------------------------------------------------------------------------------------------------------
  9836. bool ModelEngine::SetReaction(int i)
  9837. {
  9838. md->use.sol_p = md->use.sol_list->Element(i);
  9839. if (md->use.sol_p == NULL)
  9840. return false; //ToDo: must stop program. Severe error
  9841. // Find pure phase assemblage
  9842. if (md->use.ppa_in)
  9843. {
  9844. md->use.ppa_p = md->use.ppa_list->Element(i);
  9845. if (md->use.ppa_p == NULL)
  9846. return false; //ToDo: must stop program. Severe error
  9847. }
  9848. // Find exchange
  9849. if (md->use.exc_in)
  9850. {
  9851. md->use.exc_p = md->use.exc_list->Element(i);
  9852. if (md->use.exc_p == NULL)
  9853. return false; //ToDo: must stop program. Severe error
  9854. }
  9855. // Find surface
  9856. md->dl_type_x = NO_DL;
  9857. if (md->use.sur_in)
  9858. {
  9859. md->use.sur_p = md->use.sur_list->Element(i);
  9860. if (md->use.sur_p == NULL)
  9861. return false; //ToDo: must stop program. Severe error
  9862. }
  9863. // Find gas
  9864. if (md->use.gas_in)
  9865. {
  9866. md->use.gas_p = md->use.gas_list->Element(i);
  9867. if (md->use.gas_p == NULL)
  9868. return false; //ToDo: must stop program. Severe error
  9869. }
  9870. // Find s_s_assemblage
  9871. if (md->use.ssa_in)
  9872. {
  9873. md->use.ssa_p = md->use.ssa_list->Element(i);
  9874. if (md->use.ssa_p == NULL)
  9875. return false; //ToDo: must stop program. Severe error
  9876. }
  9877. return true;
  9878. }
  9879. //-----------------------------------------------------------------------------------------------------------
  9880. void ModelEngine::StepSaveSurf(Surface *dest)
  9881. {
  9882. //
  9883. // Save surface for intermediate calculation
  9884. // Amt of surface may have changed due to reaction or surface related
  9885. // to kinetic reactant.
  9886. //
  9887. // input: n_user is user solution number of target
  9888. //
  9889. int i, j, k;
  9890. Surface *surface_ptr;
  9891. SurfaceComp *sc_p;
  9892. ElementOfSpecies *eos_p;
  9893. /*
  9894. * Malloc space for solution data
  9895. */
  9896. if (md->use.sur_p == NULL)
  9897. return;
  9898. //surface_duplicate (md->use.sur_p->n_user, n_user);
  9899. md->use.sur_p->CopyTo(dest);
  9900. surface_ptr = dest;
  9901. Master *master;
  9902. for (i = 0; i < gd->master_list.Count(); i++)
  9903. {
  9904. master = gd->master_list[i];
  9905. if (master->s->type != SURF)
  9906. continue;
  9907. for (j = 0; j < surface_ptr->comps->Count(); j++)
  9908. {
  9909. sc_p = (*surface_ptr->comps)[j];
  9910. for (k = 0; k < sc_p->totals->Count(); k++)
  9911. {
  9912. eos_p = (*sc_p->totals)[k];
  9913. if (eos_p->e == master->e)
  9914. {
  9915. if (master->total <= MIN_TOTAL)
  9916. eos_p->coef = (LDBLE)MIN_TOTAL;
  9917. else
  9918. eos_p->coef = master->total;
  9919. break;
  9920. }
  9921. }
  9922. }
  9923. }
  9924. return;
  9925. }
  9926. //-----------------------------------------------------------------------------------------------------------
  9927. void ModelEngine::StepSaveExch(Exchange *dest)
  9928. {
  9929. //
  9930. // Save solution composition into structure solution with user number
  9931. // n_user.
  9932. //
  9933. // input: n_user is user solution number of target
  9934. //
  9935. int i, j, k;
  9936. bool found;
  9937. Exchange *exchange_ptr;
  9938. ExchComp *ec_p;
  9939. ElementOfSpecies *eos_p;
  9940. /*
  9941. * Malloc space for solution data
  9942. */
  9943. if (md->use.exc_p == NULL)
  9944. return;
  9945. md->use.exc_p->CopyTo(dest); //exchange_duplicate (use.exchange_ptr->n_user, n_user);
  9946. exchange_ptr = dest; //exchange_bsearch (n_user, &n);
  9947. Master *master;
  9948. for (i = 0; i < gd->master_list.Count(); i++)
  9949. {
  9950. master = gd->master_list[i];
  9951. if (master->s->type != EX)
  9952. continue;
  9953. found = false;
  9954. for (j = 0; j < exchange_ptr->comps->Count(); j++)
  9955. {
  9956. ec_p = (*exchange_ptr->comps)[j];
  9957. for (k = 0; k < ec_p->totals->Count(); k++)
  9958. {
  9959. eos_p = (*ec_p->totals)[k];
  9960. if (eos_p->e == master->e)
  9961. {
  9962. if (!found)
  9963. {
  9964. found = true;
  9965. if (master->total <= MIN_TOTAL)
  9966. eos_p->coef = (LDBLE)MIN_TOTAL;
  9967. else
  9968. eos_p->coef = master->total;
  9969. break;
  9970. }
  9971. else
  9972. eos_p->coef = 0;
  9973. }
  9974. }
  9975. }
  9976. }
  9977. return;
  9978. }
  9979. //-----------------------------------------------------------------------------------------------------------
  9980. bool ModelEngine::AddPPAssemblage(PPAssemblage *pp_assemblage_ptr)
  9981. {
  9982. /*
  9983. * Add a small amount of each phase if necessary to insure
  9984. * all elements exist in solution.
  9985. */
  9986. int i, j;
  9987. LDBLE amount_to_add, total;
  9988. char token[DEFAULT_STR_LENGTH];
  9989. char *ptr;
  9990. PurePhase *pure_phase_ptr;
  9991. Master *master_ptr;
  9992. ElementOfSpecies *eos_p;
  9993. if (CheckPPAssemblage(pp_assemblage_ptr))
  9994. return true;
  9995. #ifdef DEBUG_AddPPAssemblage
  9996. d.addppassemblage_count++;
  9997. #endif
  9998. /*
  9999. * Go through list and generate list of elements and
  10000. * coefficient of elements in reaction
  10001. */
  10002. eos_list->Clear();
  10003. parent_count = 0;
  10004. /*
  10005. * Check that all elements are in solution for phases with greater than zero mass
  10006. */
  10007. for (j = 0; j < pp_assemblage_ptr->pure_phases->Count(); j++)
  10008. {
  10009. pure_phase_ptr = (*pp_assemblage_ptr->pure_phases)[j];
  10010. #ifdef DEBUG_AddPPAssemblage
  10011. fprintf(d.addppassemblage_f, "%d 1 pure_phase_ptr->name: %s ", d.addppassemblage_count, pure_phase_ptr->name.CharPtr());
  10012. #endif
  10013. eos_list->Clear();
  10014. parent_count = 0;
  10015. amount_to_add = 0.0;
  10016. pure_phase_ptr->delta = 0.0;
  10017. if (!pure_phase_ptr->add_formula.IsEmpty())
  10018. {
  10019. pure_phase_ptr->add_formula.Copy(token);
  10020. ptr = &(token[0]);
  10021. GetElementsInSpecies(&ptr, 1.0);
  10022. #ifdef DEBUG_AddPPAssemblage
  10023. fprintf(d.addppassemblage_f, "1\n");
  10024. #endif
  10025. }
  10026. else
  10027. {
  10028. CopyToTempEOSList(pure_phase_ptr->phase->eos_list, 1.0);
  10029. #ifdef DEBUG_AddPPAssemblage
  10030. fprintf(d.addppassemblage_f, "2\n");
  10031. #endif
  10032. }
  10033. if (pure_phase_ptr->moles > 0.0)
  10034. {
  10035. for (i = 0; i < eos_list->Count(); i++)
  10036. {
  10037. eos_p = (*eos_list)[i];
  10038. master_ptr = eos_p->e->primary;
  10039. if (master_ptr->s == gd->s_hplus)
  10040. continue;
  10041. else if (master_ptr->s == gd->s_h2o)
  10042. continue;
  10043. else if (master_ptr->total > MIN_TOTAL)
  10044. continue;
  10045. else
  10046. {
  10047. total = (-master_ptr->total + (LDBLE)1e-10) / eos_p->coef;
  10048. if (amount_to_add < total)
  10049. amount_to_add = total;
  10050. #ifdef DEBUG_AddPPAssemblage
  10051. fprintf(d.addppassemblage_f, "%d 2 i: %d eos_p->coef: %.20e master_ptr->total: %.20e amount_to_add: %.20e\n", d.addppassemblage_count, i, eos_p->coef, master_ptr->total, amount_to_add);
  10052. #endif
  10053. }
  10054. }
  10055. if (pure_phase_ptr->moles < amount_to_add)
  10056. {
  10057. amount_to_add = pure_phase_ptr->moles;
  10058. #ifdef DEBUG_AddPPAssemblage
  10059. fprintf(d.addppassemblage_f, "%d 3 amount_to_add: %.20e\n", d.addppassemblage_count, amount_to_add);
  10060. #endif
  10061. }
  10062. }
  10063. if (amount_to_add > 0.0)
  10064. {
  10065. pure_phase_ptr->moles -= amount_to_add;
  10066. pure_phase_ptr->delta = amount_to_add;
  10067. #ifdef DEBUG_AddPPAssemblage
  10068. fprintf(d.addppassemblage_f, "%d 4 pure_phase_ptr->moles: %.20e pure_phase_ptr->delta: %.20e\n", d.addppassemblage_count, pure_phase_ptr->moles, pure_phase_ptr->delta);
  10069. #endif
  10070. /*
  10071. * Add reaction to totals
  10072. */
  10073. for (i = 0; i < eos_list->Count(); i++)
  10074. {
  10075. eos_p = (*eos_list)[i];
  10076. master_ptr = eos_p->e->primary;
  10077. if (master_ptr->s == gd->s_hplus)
  10078. {
  10079. md->total_h_x += eos_p->coef * amount_to_add;
  10080. #ifdef DEBUG_AddPPAssemblage
  10081. fprintf(d.addppassemblage_f, "%d 5 i: %d eos_p->coef: %.20e amount_to_add: %.20e md->total_h_x: %.20e\n", d.addppassemblage_count, i, eos_p->coef, amount_to_add, md->total_h_x);
  10082. #endif
  10083. }
  10084. else if (master_ptr->s == gd->s_h2o)
  10085. {
  10086. md->total_o_x += eos_p->coef * amount_to_add;
  10087. #ifdef DEBUG_AddPPAssemblage
  10088. fprintf(d.addppassemblage_f, "%d 6 i: %d eos_p->coef: %.20e amount_to_add: %.20e md->total_o_x: %.20e\n", d.addppassemblage_count, i, eos_p->coef, amount_to_add, md->total_o_x);
  10089. #endif
  10090. }
  10091. else
  10092. {
  10093. master_ptr->total += eos_p->coef * amount_to_add;
  10094. #ifdef DEBUG_AddPPAssemblage
  10095. fprintf(d.addppassemblage_f, "%d 7 i: %d eos_p->coef: %.20e amount_to_add: %.20e master_ptr->total: %.20e\n", d.addppassemblage_count, i, eos_p->coef, amount_to_add, master_ptr->total);
  10096. #endif
  10097. }
  10098. }
  10099. }
  10100. }
  10101. return true;
  10102. }
  10103. //-----------------------------------------------------------------------------------------------------------
  10104. bool ModelEngine::AddSSAssemblage(SSAssemblage *s_s_assemblage_ptr)
  10105. {
  10106. /*
  10107. * Accumulate solid_solution data in master->totals and _x variables.
  10108. */
  10109. int i, j, k;
  10110. LDBLE amount_to_add, total;
  10111. SS *s_s_ptr;
  10112. SSComp *ssc_p;
  10113. ElementOfSpecies *eos_p;
  10114. Master *master_ptr;
  10115. char token[DEFAULT_STR_LENGTH];
  10116. char *ptr;
  10117. if (s_s_assemblage_ptr == NULL)
  10118. return true;
  10119. eos_list->Count();
  10120. parent_count = 0;
  10121. /*
  10122. * Check that all elements are in solution for phases with greater than zero mass
  10123. */
  10124. for (i = 0; i < s_s_assemblage_ptr->ss_list->Count(); i++)
  10125. {
  10126. s_s_ptr = (*s_s_assemblage_ptr->ss_list)[i];
  10127. eos_list->Count();
  10128. parent_count = 0;
  10129. for (j = 0; j < s_s_ptr->comps_list->Count(); j++)
  10130. {
  10131. ssc_p = (*s_s_ptr->comps_list)[j];
  10132. amount_to_add = 0.0;
  10133. ssc_p->delta = 0.0;
  10134. if (ssc_p->moles > 0.0)
  10135. {
  10136. ssc_p->phase->formula.Copy(token);
  10137. ptr = &(token[0]);
  10138. GetElementsInSpecies(&ptr, 1.0);
  10139. for (k = 0; k < eos_list->Count(); k++)
  10140. {
  10141. eos_p = (*eos_list)[k];
  10142. master_ptr = eos_p->e->primary;
  10143. if (master_ptr->s == gd->s_hplus)
  10144. continue;
  10145. else if (master_ptr->s == gd->s_h2o)
  10146. continue;
  10147. else if (master_ptr->total > MIN_TOTAL_SS)
  10148. continue;
  10149. else
  10150. {
  10151. total = (-master_ptr->total + (LDBLE)1e-10) / eos_p->coef;
  10152. if (amount_to_add < total)
  10153. amount_to_add = total;
  10154. }
  10155. }
  10156. }
  10157. if (ssc_p->moles < amount_to_add)
  10158. amount_to_add = ssc_p->moles;
  10159. if (amount_to_add > 0.0)
  10160. {
  10161. ssc_p->moles -= amount_to_add;
  10162. ssc_p->delta = amount_to_add;
  10163. /*
  10164. * Add reaction to totals
  10165. */
  10166. for (k = 0; k < eos_list->Count(); k++)
  10167. {
  10168. eos_p = (*eos_list)[k];
  10169. master_ptr = eos_p->e->primary;
  10170. if (master_ptr->s == gd->s_hplus)
  10171. md->total_h_x += eos_p->coef * amount_to_add;
  10172. else if (master_ptr->s == gd->s_h2o)
  10173. md->total_o_x += eos_p->coef * amount_to_add;
  10174. else
  10175. master_ptr->total += eos_p->coef * amount_to_add;
  10176. }
  10177. }
  10178. }
  10179. }
  10180. return true;
  10181. }
  10182. //-----------------------------------------------------------------------------------------------------------
  10183. void ModelEngine::ResetData()
  10184. {
  10185. count_unknowns = 0;
  10186. //ToDo: Check to see if this is really necessary...
  10187. ArrNewMax(0);
  10188. ResidualNewMax(0);
  10189. DeltaNewMax(0);
  10190. /*
  10191. arr_max = 0;
  10192. residual_max = 0;
  10193. delta_max = 0;
  10194. */
  10195. /*
  10196. delete [] arr;
  10197. delete [] residual;
  10198. delete [] delta;
  10199. arr = NULL;
  10200. residual = NULL;
  10201. delta = NULL;
  10202. arr_capacity = 0;
  10203. residual_capacity = 0;
  10204. delta_capacity = 0;
  10205. */
  10206. }
  10207. //-----------------------------------------------------------------------------------------------------------
  10208. void ModelEngine::SaveExchange(Exchange *dest)
  10209. {
  10210. /*
  10211. * Save exchanger assemblage into structure exchange with user
  10212. * number n_user.
  10213. */
  10214. int i, j;
  10215. //int count_comps;
  10216. LDBLE charge;
  10217. if (md->use.exc_p == NULL)
  10218. return;
  10219. md->use.exc_p->CopyTo(dest);
  10220. dest->new_def = false;
  10221. dest->solution_equilibria = false;
  10222. dest->comps->Clear();
  10223. Unknown *u;
  10224. ExchComp *ec_p;
  10225. Master *m;
  10226. SpeciesInfo *s_i;
  10227. for (i = 0; i < count_unknowns; i++)
  10228. {
  10229. u = (*unknown_list)[i];
  10230. if (u->type == EXCH)
  10231. {
  10232. ec_p = dest->comps->AddNew();
  10233. m = (*u->master)[0];
  10234. u->exch_comp->CopyTo(ec_p);
  10235. ec_p->master = m;
  10236. ec_p->la = m->s->la;
  10237. u->exch_comp->formula_totals->CopyTo(ec_p->formula_totals);
  10238. ec_p->moles = 0.;
  10239. /*
  10240. * Save element concentrations on exchanger
  10241. */
  10242. eos_list->Clear();
  10243. parent_count = 0;
  10244. charge = 0.0;
  10245. for (j = 0; j < md->species_info_list->Count(); j++)
  10246. {
  10247. s_i = (*md->species_info_list)[j];
  10248. if (s_i->master_s == m->s)
  10249. {
  10250. CopyToTempEOSList(s_i->s->eos_list, s_i->s->moles);
  10251. charge += s_i->s->moles * s_i->s->z;
  10252. }
  10253. }
  10254. /*
  10255. * Keep exchanger related to phase even if none currently in solution
  10256. */
  10257. if (!u->exch_comp->phase_name.IsEmpty() && eos_list->Count() == 0)
  10258. CopyToTempEOSList(m->s->eos_list, (LDBLE)1e-20);
  10259. /*
  10260. * Store list
  10261. */
  10262. ec_p->charge_balance = charge;
  10263. SaveEOSList(ec_p->totals);
  10264. /* update unknown pointer */
  10265. u->exch_comp = ec_p;
  10266. }
  10267. }
  10268. md->use.exc_p = NULL;
  10269. }
  10270. //-----------------------------------------------------------------------------------------------------------
  10271. void ModelEngine::SaveGasPhase(GasPhase *dest)
  10272. {
  10273. /*
  10274. * Save gas composition into structure gas_phase with user
  10275. * number n_user.
  10276. */
  10277. int i;
  10278. //char token[MAX_LENGTH];
  10279. if (md->use.gas_p == NULL)
  10280. return;
  10281. /*
  10282. * Store in gas_phase
  10283. */
  10284. md->use.gas_p->CopyTo(dest);
  10285. dest->new_def = false;
  10286. dest->solution_equilibria = false;
  10287. //temp_gas_phase.n_solution = -99;
  10288. /*
  10289. * Update amounts
  10290. */
  10291. dest->comps->Clear();
  10292. GasComp *gc_p, *gc_o_p;
  10293. for (i = 0; i < md->use.gas_p->comps->Count(); i++)
  10294. {
  10295. gc_o_p = (*md->use.gas_p->comps)[i];
  10296. gc_p = dest->comps->AddNew();
  10297. gc_p->moles = gc_o_p->phase->moles_x;
  10298. }
  10299. /* update unknown pointer */
  10300. if (md->gas_unknown != NULL)
  10301. md->gas_unknown->gas_phase = dest;
  10302. md->use.gas_p = NULL;
  10303. }
  10304. //-----------------------------------------------------------------------------------------------------------
  10305. void ModelEngine::SaveSurface(Surface *dest)
  10306. {
  10307. /*
  10308. * Save surface data into structure surface with user
  10309. * number n_user.
  10310. */
  10311. int i, j, last_charge;
  10312. int count_charge;
  10313. LDBLE charge;
  10314. if (md->use.sur_p == NULL)
  10315. return;
  10316. /*
  10317. * Store data for structure surface
  10318. */
  10319. md->use.sur_p->CopyTo(dest);
  10320. dest->new_def = false;
  10321. dest->dl_type = md->dl_type_x;
  10322. dest->solution_equilibria = false;
  10323. /*
  10324. * Allocate space to pointer comps
  10325. */
  10326. dest->comps->Clear();
  10327. dest->charge->Clear();
  10328. /*
  10329. * Initial entry of surface sites is random
  10330. * Charge balance numbering follows the initial entry
  10331. * Surface sites are then sorted alphabetically
  10332. * Now when we save, the site order differs from the charge order
  10333. * last_charge sets up logic to renumber charge balance equations.
  10334. */
  10335. last_charge = -1;
  10336. Unknown *x;
  10337. SurfaceComp *sc_p;
  10338. SurfaceCharge *sch_p;
  10339. Master *m;
  10340. SpeciesInfo *s;
  10341. count_charge = 0;
  10342. for (i = 0; i < count_unknowns; i++)
  10343. {
  10344. x = (*unknown_list)[i];
  10345. m = (*x->master)[0];
  10346. if (x->type == SURFACE)
  10347. {
  10348. sc_p = dest->comps->AddNew();
  10349. x->surface_comp->CopyTo(sc_p);
  10350. sc_p->master = m;
  10351. sc_p->la = m->s->la;
  10352. sc_p->moles = 0.;
  10353. if (x->surface_comp->charge == last_charge)
  10354. sc_p->charge = count_charge - 1;
  10355. else
  10356. sc_p->charge = count_charge;
  10357. last_charge = x->surface_comp->charge;
  10358. /*
  10359. * Save element concentrations on surface
  10360. */
  10361. eos_list->Clear();
  10362. parent_count = 0;
  10363. charge = 0.0;
  10364. for (j = 0; j < md->species_info_list->Count(); j++)
  10365. {
  10366. s = (*md->species_info_list)[j];
  10367. if (s->master_s == m->s)
  10368. {
  10369. CopyToTempEOSList(s->s->eos_list, s->s->moles);
  10370. charge += s->s->moles * s->s->z;
  10371. }
  10372. }
  10373. SaveEOSList(sc_p->totals);
  10374. sc_p->totals->CopyTo(sc_p->formula_totals);
  10375. sc_p->cb = charge;
  10376. /* update unknown pointer */
  10377. x->surface_comp = sc_p;
  10378. }
  10379. else if (x->type == SURFACE_CB && md->use.sur_p->type == DDL)
  10380. {
  10381. sch_p = dest->charge->AddNew();
  10382. x->surface_charge->CopyTo(sch_p);
  10383. sch_p->charge_balance = x->f;
  10384. sch_p->mass_water = x->surface_charge->mass_water;
  10385. sch_p->diffuse_layer_totals->Clear();
  10386. sch_p->g->Clear();
  10387. /*
  10388. * Added code to save g
  10389. */
  10390. if (x->surface_charge->g->Count() > 0)
  10391. x->surface_charge->g->CopyTo(sch_p->g);
  10392. sch_p->la_psi = m->s->la;
  10393. /*
  10394. * Store moles from diffuse_layer
  10395. */
  10396. if (md->dl_type_x != NO_DL)
  10397. {
  10398. SumDiffuseLayer(x->surface_charge);
  10399. SaveEOSList(sch_p->diffuse_layer_totals);
  10400. }
  10401. /* update unknown pointer */
  10402. x->surface_charge = sch_p;
  10403. x->surface_comp = (*unknown_list)[i - 1]->surface_comp;
  10404. }
  10405. else if (x->type == SURFACE_CB && md->use.sur_p->type == CD_MUSIC)
  10406. {
  10407. sch_p = dest->charge->AddNew();
  10408. x->surface_charge->CopyTo(sch_p);
  10409. if (md->dl_type_x != NO_DL)
  10410. sch_p->charge_balance = (x->surface_charge->sigma0 + x->surface_charge->sigma1 + x->surface_charge->sigma2 + x->surface_charge->sigmaddl) * (x->surface_charge->specific_area * x->surface_charge->grams) / F_C_MOL;
  10411. else
  10412. sch_p->charge_balance = (x->surface_charge->sigma0 + x->surface_charge->sigma1 + x->surface_charge->sigma2) * (x->surface_charge->specific_area * x->surface_charge->grams) / F_C_MOL;
  10413. sch_p->mass_water = x->surface_charge->mass_water;
  10414. sch_p->diffuse_layer_totals->Clear();
  10415. sch_p->g->Clear();
  10416. /*
  10417. * Added code to save g
  10418. */
  10419. if (x->surface_charge->g->Count() > 0)
  10420. x->surface_charge->g->CopyTo(sch_p->g);
  10421. sch_p->la_psi = m->s->la;
  10422. /*
  10423. * Store moles from diffuse_layer
  10424. */
  10425. if (md->dl_type_x != NO_DL)
  10426. {
  10427. SumDiffuseLayer(x->surface_charge);
  10428. SaveEOSList(sch_p->diffuse_layer_totals);
  10429. }
  10430. /* update unknown pointer */
  10431. x->surface_charge = sch_p;
  10432. x->surface_comp = (*unknown_list)[i - 1]->surface_comp;
  10433. }
  10434. }
  10435. md->use.sur_p = NULL;
  10436. }
  10437. //-----------------------------------------------------------------------------------------------------------
  10438. void ModelEngine::FreeModelAllocs()
  10439. {
  10440. //int i;
  10441. //unknown_list->SetNewMax(0);
  10442. count_unknowns = 0;
  10443. //ToDo: Check to see if this is really necessary...
  10444. ArrNewMax(0);
  10445. ResidualNewMax(0);
  10446. DeltaNewMax(0);
  10447. /*
  10448. arr_max = 0;
  10449. residual_max = 0;
  10450. delta_max = 0;
  10451. */
  10452. /*
  10453. delete [] arr;
  10454. arr = NULL;
  10455. arr_capacity = 0;
  10456. delete [] delta;
  10457. delta = NULL;
  10458. delta_capacity = 0;
  10459. delete [] residual;
  10460. residual = NULL;
  10461. residual_capacity = 0;
  10462. */
  10463. md->s_x->Clear();
  10464. md->sum_mb1->Clear();
  10465. md->sum_mb2->Clear();
  10466. md->sum_jacob0->Clear();
  10467. md->sum_jacob1->Clear();
  10468. md->sum_jacob2->Clear();
  10469. md->sum_delta->Clear();
  10470. gd->charge_group.Clear();
  10471. return;
  10472. }
  10473. //-----------------------------------------------------------------------------------------------------------
  10474. void ModelEngine::SaveSolutionResults()
  10475. {
  10476. for (int i = 0; i < count_unknowns; i++)
  10477. {
  10478. if ((*unknown_list)[i] == md->alkalinity_unknown)
  10479. {
  10480. (*unknown_list)[i]->molality_result = (*unknown_list)[i]->f / md->mass_water_aq_x;
  10481. (*unknown_list)[i]->moles_result = (*unknown_list)[i]->f;
  10482. continue;
  10483. }
  10484. if ((*unknown_list)[i] == md->ph_unknown)
  10485. continue;
  10486. if ((*unknown_list)[i] == md->pe_unknown)
  10487. continue;
  10488. if ((*unknown_list)[i] == md->charge_balance_unknown)
  10489. {
  10490. (*unknown_list)[i]->molality_result = (*unknown_list)[i]->sum / md->mass_water_aq_x;
  10491. (*unknown_list)[i]->moles_result = (*unknown_list)[i]->sum;
  10492. continue;
  10493. }
  10494. if ((*unknown_list)[i]->type == SOLUTION_PHASE_BOUNDARY)
  10495. {
  10496. (*unknown_list)[i]->molality_result = (*unknown_list)[i]->sum / md->mass_water_aq_x;
  10497. (*unknown_list)[i]->moles_result = (*unknown_list)[i]->sum;
  10498. continue;
  10499. }
  10500. else if ((*unknown_list)[i]->type == MB)
  10501. {
  10502. (*unknown_list)[i]->molality_result = (*unknown_list)[i]->sum / md->mass_water_aq_x;
  10503. (*unknown_list)[i]->moles_result = (*unknown_list)[i]->sum;
  10504. continue;
  10505. }
  10506. (*unknown_list)[i]->molality_result = 0.0;
  10507. (*unknown_list)[i]->moles_result = 0.0;
  10508. }
  10509. }
  10510. //-----------------------------------------------------------------------------------------------------------
  10511. void ModelEngine::SavePPResults(PPAssemblage *ppa)
  10512. {
  10513. Unknown *x;
  10514. PurePhase *pp;
  10515. int i, j;
  10516. for(i = 0; i < unknown_list->Count(); i++)
  10517. {
  10518. x = (*unknown_list)[i];
  10519. if (x->type == PP)
  10520. {
  10521. pp = NULL;
  10522. for(j = 0; j < ppa->pure_phases->Count(); j++)
  10523. if (ppa->pure_phases->Element(j)->name == x->pure_phase->name)
  10524. {
  10525. pp = ppa->pure_phases->Element(j);
  10526. break;
  10527. }
  10528. if (pp != NULL)
  10529. pp->moles = x->moles;
  10530. }
  10531. }
  10532. }
  10533. //-----------------------------------------------------------------------------------------------------------
  10534. //-----------------------------------------------------------------------------------------------------------
  10535. //-----------------------------------------------------------------------------------------------------------
  10536. //-----------------------------------------------------------------------------------------------------------