PageRenderTime 36ms CodeModel.GetById 18ms 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

Large files files are truncated, but you can click here to view the full 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;

Large files files are truncated, but you can click here to view the full file